From 42b0f5196dc2f2113368993317eccbfd1127f298 Mon Sep 17 00:00:00 2001 From: Nilay Shroff Date: Fri, 18 Sep 2026 21:57:30 +0530 Subject: [PATCH] nvmet: defer setting ns->enabled to false in nvmet_ns_disable() nvmet_ns_disable() currently clears ns->enabled before draining in-flight I/O references. This allows namespace configuration to be changed while existing I/O requests can still hold a reference to the namespace. This can race with configuration of namespace attributes such as the device path, UUID, NGUID etc. These attributes can be accessed by I/O requests without holding subsys->lock and must not be modified while such requests are still using the namespace. In nvmet_ns_disable(), keep ns->enabled set while existing namespace references are being drained, so namespace configuration remains blocked until all in-flight I/O has completed. Set ns->enabled to false only after the namespace references have been drained and the namespace device has been disabled. Introduce the NVMET_NS_IO_LIVE flag, which is set after the namespace is successfully enabled in nvmet_ns_enable(). When nvmet_ns_disable() starts, clear NVMET_NS_IO_LIVE so that the I/O path stops admitting new I/O once the flag is cleared. Using test_and_clear_bit() in nvmet_ns_disable() also prevents concurrent callers from starting a second disable operation. Reviewed-by: Christoph Hellwig Signed-off-by: Nilay Shroff Signed-off-by: Keith Busch --- drivers/nvme/target/core.c | 14 ++++++++------ drivers/nvme/target/nvmet.h | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index 8c8c8627871f..059b14da845a 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -448,7 +448,8 @@ u16 nvmet_req_find_ns(struct nvmet_req *req) struct nvmet_subsys *subsys = nvmet_req_subsys(req); req->ns = xa_load(&subsys->namespaces, nsid); - if (unlikely(!req->ns || !req->ns->enabled)) { + if (unlikely(!req->ns) || + !test_bit(NVMET_NS_IO_LIVE, &req->ns->flags)) { req->error_loc = offsetof(struct nvme_common_command, nsid); if (!req->ns) /* ns doesn't exist! */ return NVME_SC_INVALID_NS | NVME_STATUS_DNR; @@ -623,6 +624,7 @@ int nvmet_ns_enable(struct nvmet_ns *ns) ns->enabled = true; xa_set_mark(&subsys->namespaces, ns->nsid, NVMET_NS_ENABLED); nvmet_debugfs_ns_setup(ns); + set_bit(NVMET_NS_IO_LIVE, &ns->flags); ret = 0; out_unlock: mutex_unlock(&subsys->lock); @@ -643,11 +645,11 @@ void nvmet_ns_disable(struct nvmet_ns *ns) struct nvmet_subsys *subsys = ns->subsys; struct nvmet_ctrl *ctrl; - mutex_lock(&subsys->lock); - if (!ns->enabled) - goto out_unlock; + if (!test_and_clear_bit(NVMET_NS_IO_LIVE, &ns->flags)) + return; + + mutex_lock(&subsys->lock); - ns->enabled = false; xa_clear_mark(&subsys->namespaces, ns->nsid, NVMET_NS_ENABLED); nvmet_debugfs_ns_free(ns); @@ -675,7 +677,7 @@ void nvmet_ns_disable(struct nvmet_ns *ns) mutex_lock(&subsys->lock); nvmet_ns_changed(subsys, ns->nsid); nvmet_ns_dev_disable(ns); -out_unlock: + ns->enabled = false; mutex_unlock(&subsys->lock); } diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index dbda55895f4f..162e2fdd848e 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -112,6 +112,8 @@ struct nvmet_ns { bool buffered_io; bool enabled; +#define NVMET_NS_IO_LIVE 0 + unsigned long flags; struct nvmet_subsys *subsys; const char *device_path;