diff -ruN a/sys/dev/disk/nvme/nvme_admin.c b/sys/dev/disk/nvme/nvme_admin.c --- a/sys/dev/disk/nvme/nvme_admin.c 2026-09-22 13:06:19.531148522 +0000 +++ b/sys/dev/disk/nvme/nvme_admin.c 2026-09-22 13:06:41.770859207 +0000 @@ -120,6 +120,13 @@ if ((nsc = sc->nscary[i]) != NULL) { nvme_disk_detach(nsc); + /* + * The cdev may still be referenced by open + * descriptors after disk_destroy(); make any late + * dispatch (which reads nsc->sc through si_drv1) + * fail cleanly instead of touching freed memory. + */ + nsc->sc = NULL; kfree(nsc, M_NVME); sc->nscary[i] = NULL; } @@ -186,10 +193,23 @@ /* * Cleanup state. * + * Poll every completion queue once more before the queues are + * deleted and freed, so in-flight callbacks observe valid softcs + * and their bios complete before the namespace memory goes away. + * * Note that we actually issue delete queue commands here. The NVME * spec says that for a normal shutdown the I/O queues should be * deleted prior to issuing the shutdown in the CONFIG register. */ + for (i = 1; i <= sc->niocomqs; ++i) { + nvme_comqueue_t *comq = &sc->comqueues[i]; + + if (comq->nqe == 0) /* not configured */ + continue; + lockmgr(&comq->lk, LK_EXCLUSIVE); + nvme_poll_completions(comq, &comq->lk); + lockmgr(&comq->lk, LK_RELEASE); + } for (i = 1; i <= sc->niosubqs; ++i) { nvme_delete_subqueue(sc, i); nvme_free_subqueue(sc, i); diff -ruN a/sys/dev/disk/nvme/nvme_attach.c b/sys/dev/disk/nvme/nvme_attach.c --- a/sys/dev/disk/nvme/nvme_attach.c 2026-09-22 13:06:19.527148574 +0000 +++ b/sys/dev/disk/nvme/nvme_attach.c 2026-09-22 13:21:22.835431041 +0000 @@ -112,6 +112,7 @@ #endif sc->dev = dev; + sc->flags &= ~NVME_SC_UNLOADING; /* * Map the register window @@ -473,6 +474,13 @@ int i; /* + * Block new data-path and admin-ioctl work before tearing down + * the queues. (nvme_open() has always checked this flag; it was + * never actually set anywhere.) + */ + sc->flags |= NVME_SC_UNLOADING; + + /* * Stop the admin thread */ nvme_stop_admin_thread(sc); diff -ruN a/sys/dev/disk/nvme/nvme_disk.c b/sys/dev/disk/nvme/nvme_disk.c --- a/sys/dev/disk/nvme/nvme_disk.c 2026-09-22 13:06:19.527148574 +0000 +++ b/sys/dev/disk/nvme/nvme_disk.c 2026-09-22 13:21:22.835431041 +0000 @@ -116,6 +116,28 @@ nvme_disk_detach(nvme_softns_t *nsc) { if (nsc->cdev) { + /* + * Fail parked bios before the softc goes away. The admin + * thread would otherwise re-dispatch them after kfree(). + */ + lockmgr(&nsc->lk, LK_EXCLUSIVE); + while (bioq_first(&nsc->bioq) != NULL) { + struct bio *bio = bioq_first(&nsc->bioq); + struct buf *bp = bio->bio_buf; + + bioq_remove(&nsc->bioq, bio); + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + biodone(bio); + } + lockmgr(&nsc->lk, LK_RELEASE); + + /* + * devfs keeps dispatching through open descriptors until + * the cdev is fully released; fail them cleanly instead + * of dereferencing the about-to-be-freed softns. + */ + nsc->cdev->si_drv1 = NULL; disk_destroy(&nsc->disk); devstat_remove_entry(&nsc->stats); } @@ -127,7 +149,11 @@ { cdev_t dev = ap->a_head.a_dev; nvme_softns_t *nsc = dev->si_drv1; - nvme_softc_t *sc = nsc->sc; + nvme_softc_t *sc; + + if (nsc == NULL || nsc->sc == NULL) + return ENXIO; + sc = nsc->sc; if (sc->flags & NVME_SC_UNLOADING) return ENXIO; @@ -143,7 +169,11 @@ { cdev_t dev = ap->a_head.a_dev; nvme_softns_t *nsc = dev->si_drv1; - nvme_softc_t *sc = nsc->sc; + nvme_softc_t *sc; + + if (nsc == NULL || nsc->sc == NULL) + return ENXIO; + sc = nsc->sc; atomic_add_long(&sc->opencnt, -1); @@ -155,9 +185,13 @@ { cdev_t dev = ap->a_head.a_dev; nvme_softns_t *nsc = dev->si_drv1; - nvme_softc_t *sc = nsc->sc; + nvme_softc_t *sc; int error; + if (nsc == NULL || nsc->sc == NULL) + return ENXIO; + sc = nsc->sc; + switch(ap->a_cmd) { case NVMEIOCGETLOG: error = nvme_getlog_ioctl(sc, (void *)ap->a_data); @@ -175,6 +209,16 @@ cdev_t dev = ap->a_head.a_dev; nvme_softns_t *nsc = dev->si_drv1; + if (nsc == NULL || nsc->sc == NULL) { + struct bio *bio = ap->a_bio; + struct buf *bp = bio->bio_buf; + + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + biodone(bio); + return 0; + } + nvme_strategy_core(nsc, ap->a_bio, nvme_sync_delay); return 0; @@ -192,6 +236,9 @@ struct bio *bio; int i; + if (sc->flags & NVME_SC_UNLOADING) + return; + for (i = 0; i < sc->nscmax; ++i) { nsc = sc->nscary[i]; if (nsc == NULL || nsc->sc == NULL) @@ -238,6 +285,19 @@ devstat_start_transaction(&nsc->stats); + if (sc->flags & NVME_SC_UNLOADING) { + /* + * Device teardown in progress (queues may already be + * freed/bzero'd). Fail the bio; do NOT enter the + * request machinery. + */ + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + devstat_end_transaction_buf(&nsc->stats, bp); + biodone(bio); + return 0; + } + subq = NULL; req = NULL; nobytes = 0;