geom_vfs: Pre-allocate event for g_vfs_destroy.

When an active g_vfs is orphaned due to an underlying disk going away
the destroy is deferred until the filesystem is unmounted in
g_vfs_done().  However, g_vfs_done() is invoked from a non-sleepable
context and cannot use M_WAITOK to allocate the event.  Instead,
allocate the event in g_vfs_orphan() and save it in the softc to be
retrieved by the last call to g_vfs_done().

Reported by:	Jithesh Arakkan @ Chelsio
Reviewed by:	imp
Sponsored by:	Chelsio Communications
Differential Revision:	https://reviews.freebsd.org/D31354
This commit is contained in:
John Baldwin 2021-07-29 17:09:23 -07:00
parent 5b5d78897c
commit 419d406e4e

View File

@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
struct g_vfs_softc {
struct mtx sc_mtx;
struct bufobj *sc_bo;
struct g_event *sc_event;
int sc_active;
int sc_orphaned;
int sc_enxio_active;
@ -96,6 +97,7 @@ static void
g_vfs_done(struct bio *bip)
{
struct g_consumer *cp;
struct g_event *event;
struct g_vfs_softc *sc;
struct buf *bp;
int destroy;
@ -157,9 +159,14 @@ g_vfs_done(struct bio *bip)
mtx_lock(&sc->sc_mtx);
destroy = ((--sc->sc_active) == 0 && sc->sc_orphaned);
if (destroy) {
event = sc->sc_event;
sc->sc_event = NULL;
} else
event = NULL;
mtx_unlock(&sc->sc_mtx);
if (destroy)
g_post_event(g_vfs_destroy, cp, M_WAITOK, NULL);
g_post_event_ep(g_vfs_destroy, cp, event, NULL);
bufdone(bp);
}
@ -212,6 +219,7 @@ static void
g_vfs_orphan(struct g_consumer *cp)
{
struct g_geom *gp;
struct g_event *event;
struct g_vfs_softc *sc;
int destroy;
@ -222,12 +230,20 @@ g_vfs_orphan(struct g_consumer *cp)
sc = gp->softc;
if (sc == NULL)
return;
event = g_alloc_event(M_WAITOK);
mtx_lock(&sc->sc_mtx);
KASSERT(sc->sc_event == NULL, ("g_vfs %p already has an event", sc));
sc->sc_orphaned = 1;
destroy = (sc->sc_active == 0);
if (!destroy) {
sc->sc_event = event;
event = NULL;
}
mtx_unlock(&sc->sc_mtx);
if (destroy)
if (destroy) {
g_free(event);
g_vfs_destroy(cp, 0);
}
/*
* Do not destroy the geom. Filesystem will do that during unmount.
@ -297,5 +313,6 @@ g_vfs_close(struct g_consumer *cp)
mtx_destroy(&sc->sc_mtx);
if (!sc->sc_orphaned || cp->provider == NULL)
g_wither_geom_close(gp, ENXIO);
KASSERT(sc->sc_event == NULL, ("g_vfs %p event is non-NULL", sc));
g_free(sc);
}