⬢ DragonFlyBSD Kernel Audit
DF-5013 / fix.diff
← back to finding ↓ download raw
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();