fix.diff — DF-5049: mfi system-PD lifetime: AEN PD removal (MR_EVT_PD_REMOVED) deletes the syspd child locklessly with no disable gate, and mfi_syspd_detach frees the softc under open descriptors and bios parked on the controller bioq. Part 1 (trigger side, sys/dev/raid/mfi/mfi.c): give the MR_EVT_PD_REMOVED handler the same lock discipline every other syspd-list walker has (mfi_config_lock + mfi_io_lock) and gate the delete through mfi_syspd_disable() so an open syspd is refused (EBUSY) instead of being freed under the open descriptor. Part 2 (sink side, sys/dev/raid/mfi/mfi_syspd.c): mfi_syspd_detach refuses to detach while the device is open (except during controller detach, which must make forward progress), marks the syspd DISABLED, and drains bios parked on the controller bioq that reference this softc, completing them with ENXIO. A DISABLED re-check in mfi_syspd_strategy makes racing bios error out instead of enqueueing against the dying softc. Residual (documented in VERDICT.md): a bio already built into an in-flight firmware command at the instant of delete can still complete through mfi_disk_complete after the softc free; fully closing that window needs in-flight command accounting (follow-up shared with the DF-5013 LD fix). --- a/sys/dev/raid/mfi/mfi.c 2026-09-26 11:29:12.879998603 +0000 +++ b/sys/dev/raid/mfi/mfi.c 2026-09-26 11:29:19.447913071 +0000 @@ -1515,20 +1515,32 @@ if (mfi_detect_jbod_change) { /* * If the removed device is a SYSPD then - * delete it + * delete it. DF-5049: serialize with the + * rest of the driver and gate the delete + * through mfi_syspd_disable() so an open + * syspd is refused (EBUSY) instead of being + * freed under the open descriptor. */ + lockmgr(&sc->mfi_config_lock, LK_EXCLUSIVE); + lockmgr(&sc->mfi_io_lock, LK_EXCLUSIVE); TAILQ_FOREACH(syspd, &sc->mfi_syspd_tqh, pd_link) { if (syspd->pd_id == - detail->args.pd.device_id) { - get_mplock(); - device_delete_child( - sc->mfi_dev, - syspd->pd_dev); - rel_mplock(); + detail->args.pd.device_id) break; - } } + if (syspd != NULL && + mfi_syspd_disable(syspd) == 0) { + lockmgr(&sc->mfi_io_lock, LK_RELEASE); + get_mplock(); + device_delete_child( + sc->mfi_dev, syspd->pd_dev); + rel_mplock(); + lockmgr(&sc->mfi_io_lock, + LK_EXCLUSIVE); + } + lockmgr(&sc->mfi_io_lock, LK_RELEASE); + lockmgr(&sc->mfi_config_lock, LK_RELEASE); } } if (detail->code == MR_EVT_PD_INSERTED) { --- a/sys/dev/raid/mfi/mfi_syspd.c 2026-09-26 11:29:12.879998603 +0000 +++ b/sys/dev/raid/mfi/mfi_syspd.c 2026-09-26 11:29:29.919776703 +0000 @@ -156,6 +156,9 @@ mfi_syspd_detach(device_t dev) { struct mfi_system_pd *sc; + struct mfi_softc *controller; + struct bio *bio; + int nbio, i; sc = device_get_softc(dev); lockmgr(&sc->pd_controller->mfi_io_lock, LK_EXCLUSIVE); @@ -165,6 +168,39 @@ lockmgr(&sc->pd_controller->mfi_io_lock, LK_RELEASE); return (EBUSY); } + /* + * DF-5049: never free the softc under an open descriptor unless + * the controller itself is detaching (shutdown must progress). + */ + if ((sc->pd_flags & MFI_DISK_FLAGS_OPEN) && + !sc->pd_controller->mfi_detaching) { + lockmgr(&sc->pd_controller->mfi_io_lock, LK_RELEASE); + return (EBUSY); + } + sc->pd_flags |= MFI_DISK_FLAGS_DISABLED; + + /* + * DF-5049: drain bios parked on the controller queue that still + * reference this softc via bio_driver_info; letting them be + * dequeued after the kfree below is a use-after-free + * (mfi_bio_command -> mfid->ld_flags, mfi_build_syspdio -> + * disk->pd_id). + */ + controller = sc->pd_controller; + nbio = controller->mfi_qstat[MFIQ_BIO].q_length; + for (i = 0; i < nbio; i++) { + bio = mfi_dequeue_bio(controller); + if (bio == NULL) + break; + if (bio->bio_driver_info == sc) { + bio->bio_buf->b_error = ENXIO; + bio->bio_buf->b_flags |= B_ERROR; + bio->bio_buf->b_resid = bio->bio_buf->b_bcount; + biodone(bio); + } else { + mfi_enqueue_bio(controller, bio); + } + } lockmgr(&sc->pd_controller->mfi_io_lock, LK_RELEASE); disk_destroy(&sc->pd_disk); @@ -277,6 +313,15 @@ bio->bio_driver_info = sc; lockmgr(&controller->mfi_io_lock, LK_EXCLUSIVE); + if (sc->pd_flags & MFI_DISK_FLAGS_DISABLED) { + /* DF-5049: syspd is going away */ + 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); + } mfi_enqueue_bio(controller, bio); devstat_start_transaction(&sc->pd_devstat); mfi_startio(controller);