DF-4714 / fix.diff
--- a/sys/dev/raid/vinum/vinumraid5.c +++ b/sys/dev/raid/vinum/vinumraid5.c @@ -401,21 +401,37 @@ enum requeststatus s; s = checksdstate(sd, rq, *diskaddr, diskend); /* do we need to change state? */ - if (s && (m.badsdno >= 0)) { /* second bad disk, */ + if (m.badsdno >= 0) { /* second bad subdisk (incl. the parity sd) */ int sdno; + int sdord; /* ordinal of the involved subdisk */ /* * If the parity disk is down, there's * no recovery. We make all involved * subdisks stale. Otherwise, we * should be able to recover, but it's * like pulling teeth. Fix it later. + * + * The guard must not depend on checksdstate() + * failing: a reviving or reborn data subdisk + * returns REQUEST_OK and would silently + * overwrite m.badsdno below, defeating the + * parityless-write conversion and accepting + * writes with two unusable data columns. */ + sdord = m.firstsdno; /* walk the involved ordinals */ for (sdno = 0; sdno < m.sdcount; sdno++) { - struct sd *sd = &SD[plex->sdnos[sdno]]; + if (sdord == m.psdno) /* parity, */ + sdord++; + if (sdord == plex->subdisks) /* got to the end, */ + sdord = 0; + if (sdord == m.psdno) /* parity, */ + sdord++; + sd = &SD[plex->sdnos[sdord]]; + sdord++; if (sd->state >= sd_reborn) /* sort of up, */ set_sd_state(sd->sdno, sd_stale, setstate_force); /* make it stale */ } - return s; /* and crap out */ + return (s != REQUEST_OK) ? s : REQUEST_DOWN; /* and crap out */ } m.badsdno = mysdno; /* note which one is bad */ m.flags |= XFR_DEGRADED_WRITE; /* we need recovery */ |