diff options
author | smh <smh@FreeBSD.org> | 2015-12-15 21:11:41 +0000 |
---|---|---|
committer | smh <smh@FreeBSD.org> | 2015-12-15 21:11:41 +0000 |
commit | 4bf76e39ad4e861072749c6657b36de165fc8e5d (patch) | |
tree | 2e7154979986ab8f76d6b34b456c5bef28f6efb6 /sys/geom | |
parent | e78f8167878561c8737d8abbce6bba5dfaaa21d9 (diff) | |
download | FreeBSD-src-4bf76e39ad4e861072749c6657b36de165fc8e5d.zip FreeBSD-src-4bf76e39ad4e861072749c6657b36de165fc8e5d.tar.gz |
Prevent g_access calls to bad multipath members
When a multipath member is orphaned its access members are zeroed before its
removed if marked for wither, so prevent any future calls to g_access on
such members.
This prevents a panic on debug kernels which validates the resultant values
aren't negative.
Reviewed by: mav
MFC after: 2 weeks
Sponsored by: Multiplay
Differential Revision: https://reviews.freebsd.org/D4416
Diffstat (limited to 'sys/geom')
-rw-r--r-- | sys/geom/multipath/g_multipath.c | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/sys/geom/multipath/g_multipath.c b/sys/geom/multipath/g_multipath.c index 0953d18..6644532 100644 --- a/sys/geom/multipath/g_multipath.c +++ b/sys/geom/multipath/g_multipath.c @@ -107,8 +107,9 @@ struct g_class g_multipath_class = { #define MP_NEW 0x00000004 #define MP_POSTED 0x00000008 #define MP_BAD (MP_FAIL | MP_LOST | MP_NEW) -#define MP_IDLE 0x00000010 -#define MP_IDLE_MASK 0xfffffff0 +#define MP_WITHER 0x00000010 +#define MP_IDLE 0x00000020 +#define MP_IDLE_MASK 0xffffffe0 static int g_multipath_good(struct g_geom *gp) @@ -204,6 +205,7 @@ g_mpd(void *arg, int flags __unused) g_access(cp, -cp->acr, -cp->acw, -cp->ace); if (w > 0 && cp->provider != NULL && (cp->provider->geom->flags & G_GEOM_WITHER) == 0) { + cp->index |= MP_WITHER; g_post_event(g_mpd, cp, M_WAITOK, NULL); return; } @@ -467,23 +469,37 @@ g_multipath_access(struct g_provider *pp, int dr, int dw, int de) gp = pp->geom; + /* Error used if we have no valid consumers. */ + error = ENXIO; + LIST_FOREACH(cp, &gp->consumer, consumer) { + if (cp->index & MP_WITHER) + continue; + error = g_access(cp, dr, dw, de); if (error) { badcp = cp; goto fail; } } + + if (error != 0) + return (error); + sc = gp->softc; sc->sc_opened += dr + dw + de; if (sc->sc_stopping && sc->sc_opened == 0) g_multipath_destroy(gp); + return (0); fail: LIST_FOREACH(cp, &gp->consumer, consumer) { if (cp == badcp) break; + if (cp->index & MP_WITHER) + continue; + (void) g_access(cp, -dr, -dw, -de); } return (error); |