diff --git a/sys/dev/raid/ips/ips_disk.h b/sys/dev/raid/ips/ips_disk.h --- a/sys/dev/raid/ips/ips_disk.h +++ b/sys/dev/raid/ips/ips_disk.h @@ -59,4 +59,6 @@ cdev_t ipsd_dev_t; ips_softc_t *sc; struct devstat stats; + int active; /* in-flight bios; sc->queue_lock */ + int disabled; /* detach started; sc->queue_lock */ } ipsdisk_softc_t; diff --git a/sys/dev/raid/ips/ips_disk.c b/sys/dev/raid/ips/ips_disk.c --- a/sys/dev/raid/ips/ips_disk.c +++ b/sys/dev/raid/ips/ips_disk.c @@ -89,7 +89,9 @@ if (dsc == NULL) return (ENXIO); + lockmgr(&dsc->sc->queue_lock, LK_EXCLUSIVE); dsc->state |= IPS_DEV_OPEN; + lockmgr(&dsc->sc->queue_lock, LK_RELEASE); DEVICE_PRINTF(2, dsc->dev, "I'm open\n"); return 0; } @@ -100,7 +102,11 @@ cdev_t dev = ap->a_head.a_dev; ipsdisk_softc_t *dsc = dev->si_drv1; + if (dsc == NULL) + return (ENXIO); + lockmgr(&dsc->sc->queue_lock, LK_EXCLUSIVE); dsc->state &= ~IPS_DEV_OPEN; + lockmgr(&dsc->sc->queue_lock, LK_RELEASE); DEVICE_PRINTF(2, dsc->dev, "I'm closed for the day\n"); return 0; } @@ -113,6 +119,12 @@ ipsdisk_softc_t *dsc; dsc = bio->bio_driver_info; + /* + * Every caller (adapter interrupt handlers, ips_timeout, and the + * strategy path) invokes us with sc->queue_lock held. + */ + dsc->active--; + wakeup(dsc); if (bp->b_flags & B_ERROR) { device_printf(dsc->dev, "iobuf error %d\n", bp->b_error); } else { @@ -129,13 +141,22 @@ { cdev_t dev = ap->a_head.a_dev; struct bio *bio = ap->a_bio; + struct buf *bp = bio->bio_buf; ipsdisk_softc_t *dsc; dsc = dev->si_drv1; DEVICE_PRINTF(8, dsc->dev, "in strategy\n"); + if (dsc->disabled) { + bp->b_flags |= B_ERROR; + bp->b_error = ENXIO; + bp->b_resid = bp->b_bcount; + biodone(bio); + return (0); + } bio->bio_driver_info = dsc; devstat_start_transaction(&dsc->stats); lockmgr(&dsc->sc->queue_lock, LK_EXCLUSIVE|LK_RETRY); + dsc->active++; bioqdisksort(&dsc->sc->bio_queue, bio); ips_start_io_request(dsc->sc); lockmgr(&dsc->sc->queue_lock, LK_RELEASE); @@ -208,8 +229,15 @@ DEVICE_PRINTF(2, dev, "in detach\n"); dsc = (ipsdisk_softc_t *)device_get_softc(dev); - if (dsc->state & IPS_DEV_OPEN) + lockmgr(&dsc->sc->queue_lock, LK_EXCLUSIVE); + if (dsc->state & IPS_DEV_OPEN) { + lockmgr(&dsc->sc->queue_lock, LK_RELEASE); return (EBUSY); + } + dsc->disabled = 1; + while (dsc->active > 0) + lksleep(dsc, &dsc->sc->queue_lock, 0, "ipsddetach", hz); + lockmgr(&dsc->sc->queue_lock, LK_RELEASE); devstat_remove_entry(&dsc->stats); disk_destroy(&dsc->ipsd_disk); return 0;