summaryrefslogtreecommitdiffstats
path: root/sys/geom/gate
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2006-09-05 21:56:00 +0000
committerpjd <pjd@FreeBSD.org>2006-09-05 21:56:00 +0000
commit5c567602d82c61d90ee3ea5f3262c9e13e064769 (patch)
tree1bc73e60d8daf49147a0b02027819d920fa6f93c /sys/geom/gate
parentb48a7df7a28b9bcf280e082e2160b3312dee8f49 (diff)
downloadFreeBSD-src-5c567602d82c61d90ee3ea5f3262c9e13e064769.zip
FreeBSD-src-5c567602d82c61d90ee3ea5f3262c9e13e064769.tar.gz
Fix problems with destroy and forcible destroy functionality:
- hold/release device in start/done routines, this will probably slow down things a bit, but previous code was racy; - only release device if g_gate_destroy() failed - if it succeeded device is dead and there is nothing to release; - various other changes which makes forcible destruction reliable. MFC after: 3 days
Diffstat (limited to 'sys/geom/gate')
-rw-r--r--sys/geom/gate/g_gate.c118
-rw-r--r--sys/geom/gate/g_gate.h4
2 files changed, 47 insertions, 75 deletions
diff --git a/sys/geom/gate/g_gate.c b/sys/geom/gate/g_gate.c
index 81b322d..6c7558c 100644
--- a/sys/geom/gate/g_gate.c
+++ b/sys/geom/gate/g_gate.c
@@ -56,12 +56,9 @@ static u_int g_gate_debug = 0;
SYSCTL_UINT(_kern_geom_gate, OID_AUTO, debug, CTLFLAG_RW, &g_gate_debug, 0,
"Debug level");
-static int g_gate_destroy_geom(struct gctl_req *, struct g_class *,
- struct g_geom *);
struct g_class g_gate_class = {
.name = G_GATE_CLASS_NAME,
.version = G_VERSION,
- .destroy_geom = g_gate_destroy_geom
};
static struct cdev *status_dev;
@@ -78,17 +75,11 @@ static LIST_HEAD(, g_gate_softc) g_gate_list =
static struct mtx g_gate_list_mtx;
-static void
-g_gate_wither(struct g_gate_softc *sc)
-{
-
- atomic_set_32(&sc->sc_flags, G_GATE_FLAG_DESTROY);
-}
-
static int
g_gate_destroy(struct g_gate_softc *sc, boolean_t force)
{
struct g_provider *pp;
+ struct g_geom *gp;
struct bio *bp;
g_topology_assert();
@@ -98,18 +89,15 @@ g_gate_destroy(struct g_gate_softc *sc, boolean_t force)
mtx_unlock(&g_gate_list_mtx);
return (EBUSY);
}
- if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0) {
- g_gate_wither(sc);
- LIST_REMOVE(sc, sc_next);
- }
mtx_unlock(&g_gate_list_mtx);
mtx_lock(&sc->sc_queue_mtx);
+ if ((sc->sc_flags & G_GATE_FLAG_DESTROY) == 0)
+ sc->sc_flags |= G_GATE_FLAG_DESTROY;
wakeup(sc);
mtx_unlock(&sc->sc_queue_mtx);
- if (sc->sc_ref > 0) {
- G_GATE_DEBUG(1, "Cannot destroy %s yet.", sc->sc_name);
- return (0);
- }
+ gp = pp->geom;
+ pp->flags |= G_PF_WITHER;
+ g_orphan_provider(pp, ENXIO);
callout_drain(&sc->sc_callout);
mtx_lock(&sc->sc_queue_mtx);
for (;;) {
@@ -134,35 +122,26 @@ g_gate_destroy(struct g_gate_softc *sc, boolean_t force)
break;
}
}
+ mtx_unlock(&sc->sc_queue_mtx);
+ g_topology_unlock();
+ mtx_lock(&g_gate_list_mtx);
+ /* One reference is ours. */
+ sc->sc_ref--;
+ while (sc->sc_ref > 0) {
+ msleep(&sc->sc_ref, &g_gate_list_mtx, 0, "gg:destroy", 0);
+ }
+ LIST_REMOVE(sc, sc_next);
+ mtx_unlock(&g_gate_list_mtx);
mtx_destroy(&sc->sc_queue_mtx);
- G_GATE_DEBUG(0, "Device %s destroyed.", sc->sc_name);
- pp->geom->softc = NULL;
- g_wither_geom(pp->geom, ENXIO);
+ g_topology_lock();
+ G_GATE_DEBUG(0, "Device %s destroyed.", gp->name);
+ gp->softc = NULL;
+ g_wither_geom(gp, ENXIO);
sc->sc_provider = NULL;
free(sc, M_GATE);
return (0);
}
-static void
-g_gate_destroy_it(void *arg, int flag __unused)
-{
- struct g_gate_softc *sc;
-
- g_topology_assert();
- sc = arg;
- mtx_lock(&g_gate_list_mtx);
- g_gate_destroy(sc, 1);
-}
-
-static int
-g_gate_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp)
-{
-
- g_topology_assert();
- mtx_lock(&g_gate_list_mtx);
- return (g_gate_destroy(gp->softc, 0));
-}
-
static int
g_gate_access(struct g_provider *pp, int dr, int dw, int de)
{
@@ -231,30 +210,17 @@ g_gate_start(struct bio *bp)
}
static struct g_gate_softc *
-g_gate_find(u_int unit)
+g_gate_hold(u_int unit)
{
struct g_gate_softc *sc;
+ mtx_lock(&g_gate_list_mtx);
LIST_FOREACH(sc, &g_gate_list, sc_next) {
if (sc->sc_unit == unit)
break;
}
- return (sc);
-}
-
-static struct g_gate_softc *
-g_gate_hold(u_int unit)
-{
- struct g_gate_softc *sc;
-
- mtx_lock(&g_gate_list_mtx);
- sc = g_gate_find(unit);
- if (sc != NULL) {
- if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0)
- sc = NULL;
- else
- sc->sc_ref++;
- }
+ if (sc != NULL)
+ sc->sc_ref++;
mtx_unlock(&g_gate_list_mtx);
return (sc);
}
@@ -268,8 +234,8 @@ g_gate_release(struct g_gate_softc *sc)
sc->sc_ref--;
KASSERT(sc->sc_ref >= 0, ("Negative sc_ref for %s.", sc->sc_name));
if (sc->sc_ref == 0 && (sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
+ wakeup(&sc->sc_ref);
mtx_unlock(&g_gate_list_mtx);
- g_waitfor_event(g_gate_destroy_it, sc, M_WAITOK, NULL);
} else {
mtx_unlock(&g_gate_list_mtx);
}
@@ -485,10 +451,9 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa
g_topology_lock();
mtx_lock(&g_gate_list_mtx);
error = g_gate_destroy(sc, ggio->gctl_force);
- if (error == 0)
- g_gate_wither(sc);
g_topology_unlock();
- g_gate_release(sc);
+ if (error != 0)
+ g_gate_release(sc);
return (error);
}
case G_GATE_CMD_CANCEL:
@@ -534,22 +499,24 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa
struct g_gate_ctl_io *ggio = (void *)addr;
G_GATE_CHECK_VERSION(ggio);
- sc = g_gate_find(ggio->gctl_unit);
+ sc = g_gate_hold(ggio->gctl_unit);
if (sc == NULL)
return (ENXIO);
+ error = 0;
for (;;) {
mtx_lock(&sc->sc_queue_mtx);
bp = bioq_first(&sc->sc_inqueue);
if (bp != NULL)
break;
- if (msleep(sc, &sc->sc_queue_mtx,
- PPAUSE | PDROP | PCATCH, "ggwait", 0) != 0) {
+ if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
ggio->gctl_error = ECANCELED;
- return (0);
+ mtx_unlock(&sc->sc_queue_mtx);
+ goto start_end;
}
- if ((sc->sc_flags & G_GATE_FLAG_DESTROY) != 0) {
+ if (msleep(sc, &sc->sc_queue_mtx,
+ PPAUSE | PDROP | PCATCH, "ggwait", 0) != 0) {
ggio->gctl_error = ECANCELED;
- return (0);
+ goto start_end;
}
}
ggio->gctl_cmd = bp->bio_cmd;
@@ -558,7 +525,7 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa
mtx_unlock(&sc->sc_queue_mtx);
ggio->gctl_length = bp->bio_length;
ggio->gctl_error = ENOMEM;
- return (0);
+ goto start_end;
}
bioq_remove(&sc->sc_inqueue, bp);
bioq_insert_tail(&sc->sc_outqueue, bp);
@@ -580,20 +547,23 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa
bioq_remove(&sc->sc_outqueue, bp);
bioq_insert_head(&sc->sc_inqueue, bp);
mtx_unlock(&sc->sc_queue_mtx);
- return (error);
+ goto start_end;
}
break;
}
- return (0);
+start_end:
+ g_gate_release(sc);
+ return (error);
}
case G_GATE_CMD_DONE:
{
struct g_gate_ctl_io *ggio = (void *)addr;
G_GATE_CHECK_VERSION(ggio);
- sc = g_gate_find(ggio->gctl_unit);
+ sc = g_gate_hold(ggio->gctl_unit);
if (sc == NULL)
return (ENOENT);
+ error = 0;
mtx_lock(&sc->sc_queue_mtx);
TAILQ_FOREACH(bp, &sc->sc_outqueue.queue, bio_queue) {
if (ggio->gctl_seq == (uintptr_t)bp->bio_driver1)
@@ -608,7 +578,7 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa
/*
* Request was probably canceled.
*/
- return (0);
+ goto done_end;
}
if (ggio->gctl_error == EAGAIN) {
bp->bio_error = 0;
@@ -637,6 +607,8 @@ g_gate_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct threa
G_GATE_LOGREQ(2, bp, "Request done.");
g_io_deliver(bp, bp->bio_error);
}
+done_end:
+ g_gate_release(sc);
return (error);
}
}
diff --git a/sys/geom/gate/g_gate.h b/sys/geom/gate/g_gate.h
index c6e8689..63ab356 100644
--- a/sys/geom/gate/g_gate.h
+++ b/sys/geom/gate/g_gate.h
@@ -68,9 +68,9 @@
*/
struct g_gate_softc {
int sc_unit; /* P: (read-only) */
- int16_t sc_ref; /* P: g_gate_list_mtx */
+ int sc_ref; /* P: g_gate_list_mtx */
struct g_provider *sc_provider; /* P: (read-only) */
- uint32_t sc_flags; /* P: (read-only) */
+ uint32_t sc_flags; /* P: sc_queue_mtx */
struct bio_queue_head sc_inqueue; /* P: sc_queue_mtx */
struct bio_queue_head sc_outqueue; /* P: sc_queue_mtx */
OpenPOWER on IntegriCloud