git: 20e94950c54e - main - nvme: Notify namespace changes better

From: Warner Losh <imp_at_FreeBSD.org>
Date: Tue, 18 Nov 2025 21:04:50 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=20e94950c54e398049396647da36b9e2c3b639c1

commit 20e94950c54e398049396647da36b9e2c3b639c1
Author:     Wanpeng Qian <wanpengqian@gmail.com>
AuthorDate: 2025-11-18 15:24:23 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2025-11-18 20:32:22 +0000

    nvme: Notify namespace changes better
    
    When we get a namespace notification, we have to reconstrut the
    namespace to get the new identification data from the namespace. For
    each namespace in the AEN, we will reconstrict it before we call the
    notification. We also flag it as changed for the duration of the change
    callback (prior versions of the patch needed to keep track, but we no
    longer do, so this bit may be removed). Note when we've seen the
    namespace so we can notify when it goes away.
    
    Co-authored-by: imp
    Differential Revision: https://reviews.freebsd.org/D33032
---
 sys/dev/nvme/nvme.h       |  7 +++++--
 sys/dev/nvme/nvme_ctrlr.c | 12 +++++++++++-
 sys/dev/nvme/nvme_ns.c    |  9 ++++++---
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h
index 22421f5600ec..8f7a7fbda14c 100644
--- a/sys/dev/nvme/nvme.h
+++ b/sys/dev/nvme/nvme.h
@@ -1929,8 +1929,11 @@ typedef void (*nvme_cons_async_fn_t)(void *, const struct nvme_completion *,
 typedef void (*nvme_cons_fail_fn_t)(void *);
 
 enum nvme_namespace_flags {
-	NVME_NS_DEALLOCATE_SUPPORTED	= 0x1,
-	NVME_NS_FLUSH_SUPPORTED		= 0x2,
+	NVME_NS_DEALLOCATE_SUPPORTED	= 0x01,
+	NVME_NS_FLUSH_SUPPORTED		= 0x02,
+	NVME_NS_ADDED			= 0x04,
+	NVME_NS_CHANGED			= 0x08,
+	NVME_NS_GONE			= 0x10,
 };
 
 int	nvme_ctrlr_passthrough_cmd(struct nvme_controller *ctrlr,
diff --git a/sys/dev/nvme/nvme_ctrlr.c b/sys/dev/nvme/nvme_ctrlr.c
index 34d4abd826b7..41542d24c107 100644
--- a/sys/dev/nvme/nvme_ctrlr.c
+++ b/sys/dev/nvme/nvme_ctrlr.c
@@ -1216,10 +1216,20 @@ nvme_ctrlr_aer_task(void *arg, int pending)
 	} else if (aer->log_page_id == NVME_LOG_CHANGED_NAMESPACE) {
 		struct nvme_ns_list *nsl =
 		    (struct nvme_ns_list *)aer->log_page_buffer;
+		struct nvme_controller *ctrlr = aer->ctrlr;
+
 		for (int i = 0; i < nitems(nsl->ns) && nsl->ns[i] != 0; i++) {
+			struct nvme_namespace *ns;
+			uint32_t id = nsl->ns[i];
+
 			if (nsl->ns[i] > NVME_MAX_NAMESPACES)
 				break;
-			nvme_notify_ns(aer->ctrlr, nsl->ns[i]);
+
+			ns = &ctrlr->ns[id - 1];
+			ns->flags |= NVME_NS_CHANGED;
+			nvme_ns_construct(ns, id, ctrlr);
+			nvme_notify_ns(ctrlr, id);
+			ns->flags &= ~NVME_NS_CHANGED;
 		}
 	}
 
diff --git a/sys/dev/nvme/nvme_ns.c b/sys/dev/nvme/nvme_ns.c
index f238fa552350..4ebcc03c4f04 100644
--- a/sys/dev/nvme/nvme_ns.c
+++ b/sys/dev/nvme/nvme_ns.c
@@ -78,7 +78,7 @@ nvme_ns_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int flag,
 		break;
 	case NVME_PASSTHROUGH_CMD:
 		pt = (struct nvme_pt_command *)arg;
-		return (nvme_ctrlr_passthrough_cmd(ctrlr, pt, ns->id, 
+		return (nvme_ctrlr_passthrough_cmd(ctrlr, pt, ns->id,
 		    1 /* is_user_buffer */, 0 /* is_admin_cmd */));
 	case NVME_GET_NSID:
 	{
@@ -558,8 +558,10 @@ nvme_ns_construct(struct nvme_namespace *ns, uint32_t id,
 	 * standard says the entire id will be zeros, so this is a
 	 * cheap way to test for that.
 	 */
-	if (ns->data.nsze == 0)
-		return (ENXIO);
+	if (ns->data.nsze == 0) {
+		ns->flags |= NVME_NS_GONE;
+		return ((ns->flags & NVME_NS_ADDED) ? 0 : ENXIO);
+	}
 
 	flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, ns->data.flbas);
 
@@ -623,6 +625,7 @@ nvme_ns_construct(struct nvme_namespace *ns, uint32_t id,
 	ns->cdev->si_drv2 = make_dev_alias(ns->cdev, "%sns%d",
 	    device_get_nameunit(ctrlr->dev), ns->id);
 	ns->cdev->si_flags |= SI_UNMAPPED;
+	ns->flags |= NVME_NS_ADDED;
 
 	return (0);
 }