diff --git a/sys/geom/geom_slice.c b/sys/geom/geom_slice.c index 6cf17fcdfde3a..4d8b5088ce5d0 100644 --- a/sys/geom/geom_slice.c +++ b/sys/geom/geom_slice.c @@ -103,6 +103,7 @@ g_slice_access(struct g_provider *pp, int dr, int dw, int de) struct g_provider *pp2; struct g_slicer *gsp; struct g_slice *gsl, *gsl2; + bool first_open; gp = pp->geom; cp = LIST_FIRST(&gp->consumer); @@ -128,14 +129,36 @@ g_slice_access(struct g_provider *pp, int dr, int dw, int de) return (EPERM); } } + /* On first open, grab an extra "exclusive" bit */ - if (cp->acr == 0 && cp->acw == 0 && cp->ace == 0) + first_open = false; + if (cp->acr == 0 && cp->acw == 0 && cp->ace == 0) { de++; + first_open = true; + } + /* ... and let go of it on last close */ if ((cp->acr + dr) == 0 && (cp->acw + dw) == 0 && (cp->ace + de) == 1) de--; + error = g_access(cp, dr, dw, de); + if (error == 0 && first_open && + (cp->acr != dr || cp->acw != dw || cp->ace != de)) { + /* + * GEOM topology lock has been dropped and reacquired by one + * of underlying geoms because it needed to perform an I/O + * operation. While the lock was not held by this thread + * another thread performed a concurrent "first open" and this + * thread lost the race. So, drop the extra "exclusive" bit. + */ + g_topology_assert(); + KASSERT(cp->ace >= 2, ("%s: lost exclusive bit", __func__)); + g_trace(G_T_ACCESS, "%s(%s): lost first-open race", __func__, + gp->name); + cp->ace--; + } + /* * Free the softc if all providers have been closed and this geom * is being removed.