Category: geom Index: sys/geom/zero/g_zero.c =================================================================== --- sys/geom/zero/g_zero.c (revision 186191) +++ sys/geom/zero/g_zero.c (working copy) @@ -104,7 +104,7 @@ g_zero_destroy_geom(struct gctl_req *req if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) return (EBUSY); g_wither_geom(gp, ENXIO); - return (EBUSY); + return (0); } static struct g_class g_zero_class = { Index: sys/geom/geom_subr.c =================================================================== --- sys/geom/geom_subr.c (revision 186191) +++ sys/geom/geom_subr.c (working copy) @@ -135,7 +135,7 @@ g_unload_class(void *arg, int flag) { struct g_hh00 *hh; struct g_class *mp; - struct g_geom *gp; + struct g_geom *gp, *gp2; struct g_provider *pp; struct g_consumer *cp; int error; @@ -146,15 +146,6 @@ 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) { LIST_FOREACH(pp, &gp->provider, provider) @@ -167,6 +158,24 @@ g_unload_class(void *arg, int flag) hh->error = EBUSY; return; } + /* + * Check if there is unfinished withering pending. We are + * likely in the event thread and can't get the work done + * right now. + */ + 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 */ @@ -174,13 +183,25 @@ g_unload_class(void *arg, int flag) mp->config = NULL; error = 0; + gp2 = NULL; for (;;) { gp = LIST_FIRST(&mp->geom); if (gp == NULL) break; + /* + * Break the loop if the geom was not destroyed. + * Many classes use withering in their destroy method + * but withering is unable to proceed now because we are + * blocking the event thread. + */ + if (gp == gp2) { + error = EDEADLK; + break; + } error = mp->destroy_geom(NULL, mp, gp); if (error != 0) break; + gp2 = gp; } if (error == 0) { if (mp->fini != NULL) @@ -233,9 +254,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));