[Bug 232671] [gmirror] gmirror fails to recover from degraded mirror sets in some circumstances

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Thu Oct 25 01:02:53 UTC 2018


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=232671

            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 at FreeBSD.org
          Reporter: cem at 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=6).
  da2p5[READ(offset=1836285952, length=16384)]
  GEOM_MIRROR[x]: Disk da2p5 state changed from ACTIVE to DISCONNECTED (device
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 (device
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=22).

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=1633884690).
  GEOM_MIRROR[1]: Device root0 created (2 components, id=1633884690).
  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 (device
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=false) is called as a side effect of da16p3
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 = g_mirror_ndisks(sc, -1);
                  if (sc->sc_ndisks == ndisks || (force && ndisks > 0)) {
                          ;

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

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

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

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

  v. And jump to RUNNING.

                  state = 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 = state;

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

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

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

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


More information about the freebsd-bugs mailing list