Index: sys/dev/nvme/nvme.c =================================================================== --- sys/dev/nvme/nvme.c +++ sys/dev/nvme/nvme.c @@ -294,12 +294,14 @@ return; ns = &ctrlr->ns[nsid - 1]; + nvme_ns_construct(ns, nsid, ctrlr, NVME_REASON_FLAGGED); for (i = 0; i < NVME_MAX_CONSUMERS; i++) { cons = &nvme_consumer[i]; if (cons->id != INVALID_CONSUMER_ID && cons->ns_fn != NULL && (ctrlr_cookie = ctrlr->cons_cookie[i]) != NULL) ns->cons_cookie[i] = (*cons->ns_fn)(ns, ctrlr_cookie); } + ns->flags &= ~NVME_NS_FLAG_CHANGED; } struct nvme_consumer * Index: sys/dev/nvme/nvme_ctrlr.c =================================================================== --- sys/dev/nvme/nvme_ctrlr.c +++ sys/dev/nvme/nvme_ctrlr.c @@ -586,7 +586,7 @@ for (i = 0; i < min(ctrlr->cdata.nn, NVME_MAX_NAMESPACES); i++) { ns = &ctrlr->ns[i]; - nvme_ns_construct(ns, i+1, ctrlr); + nvme_ns_construct(ns, i+1, ctrlr, NVME_REASON_RESET); } return (0); Index: sys/dev/nvme/nvme_ns.c =================================================================== --- sys/dev/nvme/nvme_ns.c +++ sys/dev/nvme/nvme_ns.c @@ -510,7 +510,7 @@ int nvme_ns_construct(struct nvme_namespace *ns, uint32_t id, - struct nvme_controller *ctrlr) + struct nvme_controller *ctrlr, enum nvme_ctor_reason why) { struct make_dev_args md_args; struct nvme_completion_poll_status status; @@ -549,10 +549,13 @@ * If the size of is zero, chances are this isn't a valid * namespace (eg one that's not been configured yet). The * standard says the entire id will be zeros, so this is a - * cheap way to test for that. + * cheap way to test for that. If we previously added this + * device, then it's now gone. */ - if (ns->data.nsze == 0) - return (ENXIO); + if (ns->data.nsze == 0) { + ns->flags |= NVME_NS_FLAG_GONE; + return ((ns->flags & NVME_NS_FLAG_ADDED) ? 0 : ENXIO); + } flbas_fmt = (ns->data.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & NVME_NS_DATA_FLBAS_FORMAT_MASK; @@ -597,10 +600,14 @@ /* * cdev may have already been created, if we are reconstructing the - * namespace after a controller-level reset. + * namespace after a controller-level reset. If not, then flag this + * ns as changed for notification. */ - if (ns->cdev != NULL) + if (ns->cdev != NULL) { + if (why != NVME_REASON_RESET) + ns->flags |= NVME_NS_FLAG_CHANGED; return (0); + } /* * Namespace IDs start at 1, so we need to subtract 1 to create a @@ -619,6 +626,7 @@ return (ENXIO); ns->cdev->si_flags |= SI_UNMAPPED; + ns->flags |= NVME_NS_FLAG_ADDED; return (0); } Index: sys/dev/nvme/nvme_private.h =================================================================== --- sys/dev/nvme/nvme_private.h +++ sys/dev/nvme/nvme_private.h @@ -222,6 +222,9 @@ struct nvme_namespace_data data; uint32_t id; uint32_t flags; +#define NVME_NS_FLAG_ADDED 0x1 +#define NVME_NS_FLAG_CHANGED 0x2 +#define NVME_NS_FLAG_GONE 0x4 struct cdev *cdev; void *cons_cookie[NVME_MAX_CONSUMERS]; uint32_t boundary; @@ -333,6 +336,11 @@ uint64_t hmb_desc_paddr; }; +enum nvme_ctor_reason { + NVME_REASON_RESET, /* Controller was reset, rebuilding */ + NVME_REASON_FLAGGED, /* NS was flagged as changed somehow */ +}; + #define nvme_mmio_offsetof(reg) \ offsetof(struct nvme_registers, reg) @@ -442,7 +450,7 @@ void nvme_io_qpair_destroy(struct nvme_qpair *qpair); int nvme_ns_construct(struct nvme_namespace *ns, uint32_t id, - struct nvme_controller *ctrlr); + struct nvme_controller *ctrlr, enum nvme_ctor_reason why); void nvme_ns_destruct(struct nvme_namespace *ns); void nvme_sysctl_initialize_ctrlr(struct nvme_controller *ctrlr); Index: sys/dev/nvme/nvme_sim.c =================================================================== --- sys/dev/nvme/nvme_sim.c +++ sys/dev/nvme/nvme_sim.c @@ -326,25 +326,37 @@ nvme_sim_ns_change(struct nvme_namespace *ns, void *sc_arg) { struct nvme_sim_softc *sc = sc_arg; + struct cam_path *tmppath; union ccb *ccb; + if (xpt_create_path(&tmppath, /*periph*/NULL, + cam_sim_path(sc->s_sim), 0, ns->id) != CAM_REQ_CMP) { + printf("unable to create path for rescan\n"); + return (NULL); + } + /* + * If it's gone, then signal that and leave. + */ + if (ns->flags & NVME_NS_FLAG_GONE) { + xpt_async(AC_LOST_DEVICE, tmppath, NULL); + xpt_free_path(tmppath); + return (sc_arg); + } + ccb = xpt_alloc_ccb_nowait(); if (ccb == NULL) { printf("unable to alloc CCB for rescan\n"); return (NULL); } + ccb->ccb_h.path = tmppath; /* * We map the NVMe namespace idea onto the CAM unit LUN. For * each new namespace, we create a new CAM path for it. We then * rescan the path to get it to enumerate. + * + * At the end of the scan, the path is freed, I think... */ - if (xpt_create_path(&ccb->ccb_h.path, /*periph*/NULL, - cam_sim_path(sc->s_sim), 0, ns->id) != CAM_REQ_CMP) { - printf("unable to create path for rescan\n"); - xpt_free_ccb(ccb); - return (NULL); - } xpt_rescan(ccb); return (sc_arg);