diff --git a/sys/dev/raid/mfi/mfivar.h b/sys/dev/raid/mfi/mfivar.h --- a/sys/dev/raid/mfi/mfivar.h +++ b/sys/dev/raid/mfi/mfivar.h @@ -132,6 +132,7 @@ struct mfi_disk { struct disk ld_disk; struct devstat ld_devstat; int ld_flags; + int ld_active; /* bios in flight; guarded by mfi_io_lock */ #define MFI_DISK_FLAGS_OPEN 0x01 #define MFI_DISK_FLAGS_DISABLED 0x02 #define MFI_DISK_FLAGS_SYSPD 0x04 diff --git a/sys/dev/raid/mfi/mfi_disk.c b/sys/dev/raid/mfi/mfi_disk.c --- a/sys/dev/raid/mfi/mfi_disk.c +++ b/sys/dev/raid/mfi/mfi_disk.c @@ -184,11 +184,16 @@ mfi_disk_detach(device_t dev) } lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); - disk_destroy(&sc->ld_disk); - devstat_remove_entry(&sc->ld_devstat); lockmgr(&sc->ld_controller->mfi_io_lock, LK_EXCLUSIVE); + sc->ld_flags |= MFI_DISK_FLAGS_DISABLED; + while (sc->ld_active > 0) + lksleep(sc, &sc->ld_controller->mfi_io_lock, 0, + "mfiddetach", hz); TAILQ_REMOVE(&sc->ld_controller->mfi_ld_tqh, sc, ld_link); lockmgr(&sc->ld_controller->mfi_io_lock, LK_RELEASE); + + disk_destroy(&sc->ld_disk); + devstat_remove_entry(&sc->ld_devstat); kfree(sc->ld_info, M_MFIBUF); return (0); } @@ -294,6 +299,15 @@ mfi_disk_strategy(struct dev_strategy_args *ap) bio->bio_driver_info = sc; lockmgr(&controller->mfi_io_lock, LK_EXCLUSIVE); + if (sc->ld_flags & MFI_DISK_FLAGS_DISABLED) { + lockmgr(&controller->mfi_io_lock, LK_RELEASE); + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + bp->b_resid = bp->b_bcount; + biodone(bio); + return (0); + } + sc->ld_active++; mfi_enqueue_bio(controller, bio); devstat_start_transaction(&sc->ld_devstat); mfi_startio(controller); @@ -307,6 +321,9 @@ mfi_disk_complete(struct bio *bio) struct mfi_disk *sc = bio->bio_driver_info; struct buf *bp = bio->bio_buf; + /* caller (mfi_complete) holds mfi_io_lock */ + sc->ld_active--; + wakeup(sc); devstat_end_transaction_buf(&sc->ld_devstat, bp); if (bp->b_flags & B_ERROR) { if (bp->b_error == 0) diff --git a/sys/dev/raid/mfi/mfi.c b/sys/dev/raid/mfi/mfi.c --- a/sys/dev/raid/mfi/mfi.c +++ b/sys/dev/raid/mfi/mfi.c @@ -1504,6 +1504,18 @@ mfi_decode_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail) KASSERT(ld != NULL, ("volume dissappeared")); */ if (ld != NULL) { + /* + * device_delete_child() frees the mfi_disk + * softc on return: refuse to tear the volume + * down while it is open. mfi_disk_detach() + * drains any in-flight bios. + */ + lockmgr(&sc->mfi_io_lock, LK_EXCLUSIVE); + if (mfi_disk_disable(ld) != 0) { + lockmgr(&sc->mfi_io_lock, LK_RELEASE); + break; + } + lockmgr(&sc->mfi_io_lock, LK_RELEASE); get_mplock(); device_delete_child(sc->mfi_dev, ld->ld_dev); rel_mplock();