Index: sys/geom/geom_slice.c =================================================================== --- sys/geom/geom_slice.c +++ sys/geom/geom_slice.c @@ -105,6 +105,7 @@ struct g_provider *pp2; struct g_slicer *gsp; struct g_slice *gsl, *gsl2; + bool first_open; gp = pp->geom; cp = LIST_FIRST(&gp->consumer); @@ -130,13 +131,35 @@ 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