[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