Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 25 Oct 2018 01:02:51 +0000
From:      bugzilla-noreply@freebsd.org
To:        bugs@FreeBSD.org
Subject:   [Bug 232671] [gmirror] gmirror fails to recover from degraded mirror sets in some circumstances
Message-ID:  <bug-232671-227@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D232671

            Bug ID: 232671
           Summary: [gmirror] gmirror fails to recover from degraded
                    mirror sets in some circumstances
           Product: Base System
           Version: CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: cem@freebsd.org

Here is the example scenario:

1. I start with a GMIRROR with two ACTIVE disks called "root0".
2. I essentially disconnect one of the disks:

  (pass1:pmspcbsd0:0:1:0): CAM_REMOVE_DEVICE
  da2 at pmspcbsd0 bus 0 scbus0 target 1 lun 0
  da2: <ATA HUS726040ALN610 T840> s/n NHGDJ73Y             detached
  g_access(977): provider da2 has error
  GEOM_MIRROR[0]: Request failed (error=3D6).
  da2p5[READ(offset=3D1836285952, length=3D16384)]
  GEOM_MIRROR[x]: Disk da2p5 state changed from ACTIVE to DISCONNECTED (dev=
ice
root0).
  ...
  GEOM_MIRROR[x]: Device root0: provider da2p5 disconnected.
  GEOM_MIRROR[x]: Consumer da2p5 destroyed.
  ...

3. I add a new hot-spare mirror device to the mirrorset.

  GEOM_MIRROR[1]: Adding disk da16p3 to root0.
  GEOM_MIRROR[1]: Disk da16p3 state changed from NONE to NEW (device root0).

4. GMIRROR begins synchronizing from the remaining live provider to the NEW
one.  As a result, the mirrorset's generation and/or sync id is bumped:

  GEOM_MIRROR[1]: Device root0: provider da16p3 detected.
  GEOM_MIRROR[1]: Disk da16p3 state changed from NEW to SYNCHRONIZING (devi=
ce
root0).
  GEOM_MIRROR[0]: Device root0: rebuilding provider da16p3.

5. The scsi bus is rescanned and da2 comes back:

  da2 at pmspcbsd0 bus 0 scbus0 target 1 lun 0

6. GEOM_MIRROR rejects it because it has a stale generation or sync id:

  GEOM_MIRROR[1]: Adding disk da2p5 to root0.
  GEOM_MIRROR[x]: Component da2p5 (device root0) broken, skipping.
  GEOM_MIRROR[0]: Cannot add disk da2p5 to root0 (error=3D22).

7. At this point, the mirrorset has two disks (da15p3, ACTIVE, and da16p3,
SYNCHRONIZING).  The machine is rebooted before synchronization completes.
8. At boot, before mounting root, GEOM happens to detect the mirror disks in
the following order:

  i. da2p5 (the stale mirror that was ejected in (2)
  ii. da16p3 (the mirror that is partially synchronized)
  iii. da15p3 (the only good / "ACTIVE" mirror in the set)

  GEOM_MIRROR[1]: Creating device root0 (id=3D1633884690).
  GEOM_MIRROR[1]: Device root0 created (2 components, id=3D1633884690).
  GEOM_MIRROR[1]: root_mount_hold 0xfffff8003f496160
  GEOM_MIRROR[1]: Adding disk da2p5 to root0.
  GEOM_MIRROR[1]: Disk da2p5 state changed from NONE to NEW (device root0).
  GEOM_MIRROR[1]: Device root0: provider da2p5 detected.
  GEOM_MIRROR[1]: Adding disk da16p3 to root0.
  GEOM_MIRROR[1]: Disk da16p3 state changed from NONE to NEW (device root0).
  GEOM_MIRROR[1]: Device root0: provider da16p3 detected.
  GEOM_MIRROR[0]: Component da2p5 (device root0) broken, skipping.

<< the bug is here, if not earlier >>

  GEOM_MIRROR[1]: Device root0 state changed from STARTING to RUNNING.
  GEOM_MIRROR[1]: Disk da16p3 state changed from NEW to SYNCHRONIZING (devi=
ce
root0).
  GEOM_MIRROR[1]: root_mount_rel[2352] 0xfffff8003f496160
  GEOM_MIRROR[1]: Adding disk da15p3 to root0.

9. Unfortunately, at the marked location above, GMIRROR sees the two broken
mirrors, and decides to transition the mirror set into RUNNING.

  i. g_mirror_update_device(force=3Dfalse) is called as a side effect of da=
16p3
transitioning to NEW.

  ii. We have the right number of mirrors, even though they are all broken:

  g_mirror_update_device(struct g_mirror_softc *sc, bool force)
  {
  ...
          switch (sc->sc_state) {
          case G_MIRROR_DEVICE_STATE_STARTING:
              {
  ...
                  /*
                   * Are we ready? We are, if all disks are connected or
                   * if we have any disks and 'force' is true.
                   */
                  ndisks =3D g_mirror_ndisks(sc, -1);
                  if (sc->sc_ndisks =3D=3D ndisks || (force && ndisks > 0))=
 {
                          ;

  iii. We don't see any "dirty" mirrors because the logic ignores stale
generations and disks mid-synchronization:

                  dirty =3D ndisks =3D 0;
                  pdisk =3D NULL;
                  LIST_FOREACH(disk, &sc->sc_disks, d_next) {
                          if (disk->d_sync.ds_syncid !=3D syncid)
                                  continue;
                          if ((disk->d_flags &
                              G_MIRROR_DISK_FLAG_SYNCHRONIZING) !=3D 0) {
                                  continue;

  iv. We interpret this as meaning we have a clean mirror set!

                  if (dirty =3D=3D 0) {
                          /* No dirty disks at all, great. */

  v. And jump to RUNNING.

                  state =3D G_MIRROR_DEVICE_STATE_RUNNING;
                  G_MIRROR_DEBUG(1, "Device %s state changed from %s to %s.=
",
                      sc->sc_name, g_mirror_device_state2str(sc->sc_state),
                      g_mirror_device_state2str(state));
                  sc->sc_state =3D state;

10. Something triggers an event, which causes g_mirror_update_deveice() to =
be
invoked again.=20
 The sc is in the RUNNING state:

          case G_MIRROR_DEVICE_STATE_RUNNING:
                  if (g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_ACTIVE) =3D=
=3D 0 &&
                      g_mirror_ndisks(sc, G_MIRROR_DISK_STATE_NEW) =3D=3D 0=
) {
                          /*
                           * No usable disks, so destroy the device.
                           */
                          sc->sc_flags |=3D G_MIRROR_DEVICE_FLAG_DESTROY;
                          break;

11. And the gmirror destroys itself, even though we had a valid mirror we c=
ould
have recovered from.

--=20
You are receiving this mail because:
You are the assignee for the bug.=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-232671-227>