GEOM class unload deadlock
Jaakko Heinonen
jh at FreeBSD.org
Mon Apr 5 09:26:15 UTC 2010
Hi,
Below is a patch to fix a possible deadlock between g_unload_class()
and withering. Probably the easiest way to reproduce the deadlock is to
load geom_mbr.ko module and then try to unload it.
Some details are explained this message:
http://docs.freebsd.org/cgi/mid.cgi?20081216210311.GA5229
---
Fix deadlock between GEOM class unloading and withering. Withering can't
proceed while g_unload_class() blocks the event thread. Fix this by
queuing the unload event repeatedly until withering has finished.
PR: kern/139847
%%%
Index: sys/geom/geom_subr.c
===================================================================
--- sys/geom/geom_subr.c (revision 206155)
+++ sys/geom/geom_subr.c (working copy)
@@ -146,17 +146,8 @@ g_unload_class(void *arg, int flag)
G_VALID_CLASS(mp);
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 */
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;
@@ -167,6 +158,23 @@ g_unload_class(void *arg, int flag)
hh->error = EBUSY;
return;
}
+ /*
+ * Check for unfinished withering. We are in the event
+ * thread, so withering can't proceed.
+ */
+ if (gp->flags & G_GEOM_WITHER) {
+ hh->error = EDEADLK;
+ return;
+ }
+ }
+
+ /*
+ * 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;
}
/* Bar new entries */
@@ -181,6 +189,12 @@ g_unload_class(void *arg, int flag)
error = mp->destroy_geom(NULL, mp, gp);
if (error != 0)
break;
+ /* Return, if withering needs to proceed. */
+ if (gp == LIST_FIRST(&mp->geom) && gp->flags & G_GEOM_WITHER) {
+ hh->error = EDEADLK;
+ return;
+ }
+
}
if (error == 0) {
if (mp->fini != NULL)
@@ -233,9 +247,12 @@ g_modevent(module_t mod, int type, void
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;
+ do {
+ error = g_waitfor_event(g_unload_class, hh, M_WAITOK,
+ NULL);
+ if (error == 0)
+ error = hh->error;
+ } while (error == EDEADLK);
if (error == 0) {
KASSERT(LIST_EMPTY(&hh->mp->geom),
("Unloaded class (%s) still has geom", hh->mp->name));
%%%
--
Jaakko
More information about the freebsd-geom
mailing list