diff options
author | jh <jh@FreeBSD.org> | 2010-05-05 18:53:24 +0000 |
---|---|---|
committer | jh <jh@FreeBSD.org> | 2010-05-05 18:53:24 +0000 |
commit | 70139e5fa64ec30ed27ed723699c17200d26adcd (patch) | |
tree | 906c631de7ab5dc88ba8a8cbd804c21fdfef4d6f /sys/geom | |
parent | a0a9776a5cd835b11247f9190d1b1005b828c7ed (diff) | |
download | FreeBSD-src-70139e5fa64ec30ed27ed723699c17200d26adcd.zip FreeBSD-src-70139e5fa64ec30ed27ed723699c17200d26adcd.tar.gz |
Fix deadlock between GEOM class unloading and withering. Withering can't
proceed while g_unload_class() blocks the event thread. Fix this by not
running g_unload_class() as a GEOM event and dropping the topology lock
when withering needs to proceed.
PR: kern/139847
Silence on: freebsd-geom
Diffstat (limited to 'sys/geom')
-rw-r--r-- | sys/geom/geom.h | 3 | ||||
-rw-r--r-- | sys/geom/geom_subr.c | 98 |
2 files changed, 54 insertions, 47 deletions
diff --git a/sys/geom/geom.h b/sys/geom/geom.h index 82d0456..fe18a15 100644 --- a/sys/geom/geom.h +++ b/sys/geom/geom.h @@ -353,6 +353,9 @@ g_free(void *ptr) sx_assert(&topology_lock, SX_UNLOCKED); \ } while (0) +#define g_topology_sleep(chan, timo) \ + sx_sleep(chan, &topology_lock, 0, "gtopol", timo) + #define DECLARE_GEOM_CLASS(class, name) \ static moduledata_t name##_mod = { \ #name, g_modevent, &class \ diff --git a/sys/geom/geom_subr.c b/sys/geom/geom_subr.c index 772277e..b001c5f 100644 --- a/sys/geom/geom_subr.c +++ b/sys/geom/geom_subr.c @@ -134,65 +134,73 @@ g_load_class(void *arg, int flag) } } -static void -g_unload_class(void *arg, int flag) +static int +g_unload_class(struct g_class *mp) { - struct g_hh00 *hh; - struct g_class *mp; struct g_geom *gp; struct g_provider *pp; struct g_consumer *cp; int error; - g_topology_assert(); - hh = arg; - mp = hh->mp; - G_VALID_CLASS(mp); + g_topology_lock(); g_trace(G_T_TOPOLOGY, "g_unload_class(%s)", mp->name); - - /* - * We allow unloading if we have no geoms, or a class - * method we can use to get rid of them. - */ - if (!LIST_EMPTY(&mp->geom) && mp->destroy_geom == NULL) { - hh->error = EOPNOTSUPP; - return; - } - - /* We refuse to unload if anything is open */ +retry: + G_VALID_CLASS(mp); LIST_FOREACH(gp, &mp->geom, geom) { + /* We refuse to unload if anything is open */ LIST_FOREACH(pp, &gp->provider, provider) if (pp->acr || pp->acw || pp->ace) { - hh->error = EBUSY; - return; + g_topology_unlock(); + return (EBUSY); } LIST_FOREACH(cp, &gp->consumer, consumer) if (cp->acr || cp->acw || cp->ace) { - hh->error = EBUSY; - return; + g_topology_unlock(); + return (EBUSY); } + /* If the geom is withering, wait for it to finish. */ + if (gp->flags & G_GEOM_WITHER) { + g_topology_sleep(mp, 1); + goto retry; + } + } + + /* + * We allow unloading if we have no geoms, or a class + * method we can use to get rid of them. + */ + if (!LIST_EMPTY(&mp->geom) && mp->destroy_geom == NULL) { + g_topology_unlock(); + return (EOPNOTSUPP); } /* Bar new entries */ mp->taste = NULL; mp->config = NULL; - error = 0; + LIST_FOREACH(gp, &mp->geom, geom) { + error = mp->destroy_geom(NULL, mp, gp); + if (error != 0) { + g_topology_unlock(); + return (error); + } + } + /* Wait for withering to finish. */ for (;;) { gp = LIST_FIRST(&mp->geom); if (gp == NULL) break; - error = mp->destroy_geom(NULL, mp, gp); - if (error != 0) - break; + KASSERT(gp->flags & G_GEOM_WITHER, + ("Non-withering geom in class %s", mp->name)); + g_topology_sleep(mp, 1); } - if (error == 0) { - if (mp->fini != NULL) - mp->fini(mp); - LIST_REMOVE(mp, class); - } - hh->error = error; - return; + G_VALID_CLASS(mp); + if (mp->fini != NULL) + mp->fini(mp); + LIST_REMOVE(mp, class); + g_topology_unlock(); + + return (0); } int @@ -213,12 +221,12 @@ g_modevent(module_t mod, int type, void *data) g_ignition++; g_init(); } - hh = g_malloc(sizeof *hh, M_WAITOK | M_ZERO); - hh->mp = data; error = EOPNOTSUPP; switch (type) { case MOD_LOAD: - g_trace(G_T_TOPOLOGY, "g_modevent(%s, LOAD)", hh->mp->name); + g_trace(G_T_TOPOLOGY, "g_modevent(%s, LOAD)", mp->name); + hh = g_malloc(sizeof *hh, M_WAITOK | M_ZERO); + hh->mp = mp; /* * Once the system is not cold, MOD_LOAD calls will be * from the userland and the g_event thread will be able @@ -236,18 +244,14 @@ g_modevent(module_t mod, int type, void *data) } break; case MOD_UNLOAD: - g_trace(G_T_TOPOLOGY, "g_modevent(%s, UNLOAD)", hh->mp->name); - error = g_waitfor_event(g_unload_class, hh, M_WAITOK, NULL); - if (error == 0) - error = hh->error; + g_trace(G_T_TOPOLOGY, "g_modevent(%s, UNLOAD)", mp->name); + DROP_GIANT(); + error = g_unload_class(mp); + PICKUP_GIANT(); if (error == 0) { - KASSERT(LIST_EMPTY(&hh->mp->geom), - ("Unloaded class (%s) still has geom", hh->mp->name)); + KASSERT(LIST_EMPTY(&mp->geom), + ("Unloaded class (%s) still has geom", mp->name)); } - g_free(hh); - break; - default: - g_free(hh); break; } return (error); |