Fix GEOM_MOUNTVER orphanization.

Previous code closed and detached consumer even with I/O still in progress.
This patch adds locking and request counting to postpone the close till
the last of running requests completes.

MFC after:	2 weeks
Sponsored by:	iXsystems, Inc.
This commit is contained in:
Alexander Motin 2019-12-29 17:10:21 +00:00
parent ee0fe82ee2
commit d2d5fee931
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=356178

View File

@ -84,15 +84,30 @@ struct g_class g_mountver_class = {
.fini = g_mountver_fini
};
static void
g_mountver_detach(void *arg, int flags __unused)
{
struct g_consumer *cp = arg;
g_topology_assert();
if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
g_access(cp, -cp->acr, -cp->acw, -cp->ace);
g_detach(cp);
}
static void
g_mountver_done(struct bio *bp)
{
struct g_mountver_softc *sc;
struct g_geom *gp;
struct g_consumer *cp;
struct bio *pbp;
cp = bp->bio_from;
gp = cp->geom;
if (bp->bio_error != ENXIO) {
g_std_done(bp);
return;
goto done;
}
/*
@ -101,32 +116,45 @@ g_mountver_done(struct bio *bp)
* gets called. To work around that, we have to queue requests
* that failed with ENXIO, in order to send them later.
*/
gp = bp->bio_from->geom;
pbp = bp->bio_parent;
KASSERT(pbp->bio_to == LIST_FIRST(&gp->provider),
("parent request was for someone else"));
g_destroy_bio(bp);
pbp->bio_inbed++;
g_mountver_queue(pbp);
done:
sc = gp->softc;
mtx_lock(&sc->sc_mtx);
if (--cp->index == 0 && sc->sc_orphaned)
g_post_event(g_mountver_detach, cp, M_NOWAIT, NULL);
mtx_unlock(&sc->sc_mtx);
}
/*
* Send the BIO down. The function is called with sc_mtx held to cover
* the race with orphan, but drops it before external calls.
*/
static void
g_mountver_send(struct bio *bp)
g_mountver_send(struct g_geom *gp, struct bio *bp)
{
struct g_geom *gp;
struct g_mountver_softc *sc = gp->softc;
struct g_consumer *cp;
struct bio *cbp;
gp = bp->bio_to->geom;
mtx_assert(&sc->sc_mtx, MA_OWNED);
cbp = g_clone_bio(bp);
if (cbp == NULL) {
mtx_unlock(&sc->sc_mtx);
g_io_deliver(bp, ENOMEM);
return;
}
cp = LIST_FIRST(&gp->consumer);
cp->index++;
mtx_unlock(&sc->sc_mtx);
cbp->bio_done = g_mountver_done;
g_io_request(cbp, LIST_FIRST(&gp->consumer));
g_io_request(cbp, cp);
}
static void
@ -152,10 +180,12 @@ g_mountver_send_queued(struct g_geom *gp)
sc = gp->softc;
mtx_lock(&sc->sc_mtx);
while ((bp = TAILQ_FIRST(&sc->sc_queue)) != NULL) {
while ((bp = TAILQ_FIRST(&sc->sc_queue)) != NULL && !sc->sc_orphaned) {
TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue);
G_MOUNTVER_LOGREQ(bp, "Sending queued request.");
g_mountver_send(bp);
/* sc_mtx is dropped inside */
g_mountver_send(gp, bp);
mtx_lock(&sc->sc_mtx);
}
mtx_unlock(&sc->sc_mtx);
}
@ -171,8 +201,10 @@ g_mountver_discard_queued(struct g_geom *gp)
mtx_lock(&sc->sc_mtx);
while ((bp = TAILQ_FIRST(&sc->sc_queue)) != NULL) {
TAILQ_REMOVE(&sc->sc_queue, bp, bio_queue);
mtx_unlock(&sc->sc_mtx);
G_MOUNTVER_LOGREQ(bp, "Discarding queued request.");
g_io_deliver(bp, ENXIO);
mtx_lock(&sc->sc_mtx);
}
mtx_unlock(&sc->sc_mtx);
}
@ -192,7 +224,9 @@ g_mountver_start(struct bio *bp)
* orphaning didn't happen yet. In that case, queue all subsequent
* requests in order to maintain ordering.
*/
mtx_lock(&sc->sc_mtx);
if (sc->sc_orphaned || !TAILQ_EMPTY(&sc->sc_queue)) {
mtx_unlock(&sc->sc_mtx);
if (sc->sc_shutting_down) {
G_MOUNTVER_LOGREQ(bp, "Discarding request due to shutdown.");
g_io_deliver(bp, ENXIO);
@ -204,7 +238,8 @@ g_mountver_start(struct bio *bp)
g_mountver_send_queued(gp);
} else {
G_MOUNTVER_LOGREQ(bp, "Sending request.");
g_mountver_send(bp);
/* sc_mtx is dropped inside */
g_mountver_send(gp, bp);
}
}
@ -466,14 +501,17 @@ static void
g_mountver_orphan(struct g_consumer *cp)
{
struct g_mountver_softc *sc;
int done;
g_topology_assert();
sc = cp->geom->softc;
mtx_lock(&sc->sc_mtx);
sc->sc_orphaned = 1;
if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
g_access(cp, -cp->acr, -cp->acw, -cp->ace);
g_detach(cp);
done = (cp->index == 0);
mtx_unlock(&sc->sc_mtx);
if (done)
g_mountver_detach(cp, 0);
G_MOUNTVER_DEBUG(0, "%s is offline. Mount verification in progress.", sc->sc_provider_name);
}
@ -571,8 +609,8 @@ g_mountver_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
return (NULL);
}
}
g_mountver_send_queued(gp);
sc->sc_orphaned = 0;
g_mountver_send_queued(gp);
G_MOUNTVER_DEBUG(0, "%s has completed mount verification.", sc->sc_provider_name);
return (gp);