--- a/sys/dev/disk/nata/atapi-fd.c 2026-09-24 17:15:01.460690390 +0000 +++ b/sys/dev/disk/nata/atapi-fd.c 2026-09-24 17:15:14.072526409 +0000 @@ -134,23 +134,45 @@ static int afd_detach(device_t dev) { + struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct afd_softc *fdp = device_get_ivars(dev); /* check that we have a valid device to detach */ if (!device_get_ivars(dev)) return ENXIO; - + + /* + * Refuse to detach while any disk/slice device is open: the cdevs + * outlive disk_destroy() and keep walking the embedded struct disk + * until their last close. (DF-4874, twin of DF-4797) + */ + if (fdp->disk.d_opencount != 0) + return EBUSY; + + /* + * Stop the data path: no new bio may pass afd_strategy(), and the + * softc must survive until the last in-flight request's afd_done(). + */ + lockmgr(&ch->queue_mtx, LK_EXCLUSIVE); + fdp->flags |= AFD_F_DETACHING; + device_set_ivars(dev, NULL); + lockmgr(&ch->queue_mtx, LK_RELEASE); + while (fdp->refs) + tsleep(fdp, 0, "afdetach", hz); + + /* + * Fail anything still queued or running on the channel so their + * biodone()s happen while the disk layer structures are intact. + */ + ata_fail_requests(dev); + /* detroy disk from the system so we dont get any further requests */ disk_invalidate(&fdp->disk); disk_destroy(&fdp->disk); - /* fail requests on the queue and any thats "in flight" for this device */ - ata_fail_requests(dev); - /* dont leave anything behind */ /* disk_destroy() already took care of the dev_ops */ devstat_remove_entry(&fdp->stats); - device_set_ivars(dev, NULL); kfree(fdp, M_AFD); return 0; } @@ -255,6 +277,25 @@ return 0; } + /* + * Serialize against afd_detach(): either we obtain a reference and + * the softc stays alive until our afd_done() drops it, or the driver + * is detaching and the bio is failed immediately. (DF-4874) + */ + { + struct ata_channel *ch = device_get_softc(device_get_parent(dev)); + + lockmgr(&ch->queue_mtx, LK_EXCLUSIVE); + fdp = device_get_ivars(dev); + if (fdp == NULL || (fdp->flags & AFD_F_DETACHING)) { + lockmgr(&ch->queue_mtx, LK_RELEASE); + biofinish(bp, NULL, ENXIO); + return 0; + } + atomic_add_int(&fdp->refs, 1); + lockmgr(&ch->queue_mtx, LK_RELEASE); + } + lba = bp->bio_offset / fdp->sectorsize; count = bbp->b_bcount / fdp->sectorsize; bbp->b_resid = bbp->b_bcount; @@ -326,7 +367,11 @@ if ((bbp->b_error = request->result)) bbp->b_flags |= B_ERROR; bbp->b_resid = bbp->b_bcount - request->donecount; - devstat_end_transaction_buf(&fdp->stats, bbp); + if (fdp) { + devstat_end_transaction_buf(&fdp->stats, bbp); + atomic_subtract_int(&fdp->refs, 1); + wakeup(fdp); + } biodone(bp); ata_free_request(request); } --- a/sys/dev/disk/nata/atapi-fd.h 2026-09-24 17:15:01.464690338 +0000 +++ b/sys/dev/disk/nata/atapi-fd.h 2026-09-24 17:15:14.060526565 +0000 @@ -80,7 +80,11 @@ u_int32_t blocksize; }; +#define AFD_F_DETACHING 0x0001 + struct afd_softc { + int flags; + int refs; /* in-flight afd_strategy references */ u_int64_t mediasize; u_int32_t heads; u_int32_t sectors;