[PATCH 1/4] nvmet: forbid changing ctrl ID attributes for discovered subsystems

Max Gurtovoy mgurtovoy at nvidia.com
Wed Sep 24 13:26:01 PDT 2025


Controller identifiers are dynamically assigned when an NVMe host
connects to a target. The minimum and maximum allowed controller ID
values for a subsystem are configurable via configfs. Do not allow
changes to these attributes after a subsystem has already been
discovered to prevent invalid configuration.

Signed-off-by: Max Gurtovoy <mgurtovoy at nvidia.com>
---
 drivers/nvme/target/admin-cmd.c |  6 ----
 drivers/nvme/target/configfs.c  | 63 ++++++++++++++++++++++++---------
 drivers/nvme/target/core.c      |  6 ++++
 3 files changed, 53 insertions(+), 22 deletions(-)

diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 3e378153a781..72c741a95ac8 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -654,12 +654,6 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
 	u32 cmd_capsule_size, ctratt;
 	u16 status = 0;
 
-	if (!subsys->subsys_discovered) {
-		mutex_lock(&subsys->lock);
-		subsys->subsys_discovered = true;
-		mutex_unlock(&subsys->lock);
-	}
-
 	id = kzalloc(sizeof(*id), GFP_KERNEL);
 	if (!id) {
 		status = NVME_SC_INTERNAL;
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index e44ef69dffc2..979eb184756a 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -1348,10 +1348,30 @@ static ssize_t nvmet_subsys_attr_cntlid_min_show(struct config_item *item,
 	return snprintf(page, PAGE_SIZE, "%u\n", to_subsys(item)->cntlid_min);
 }
 
+static ssize_t
+nvmet_subsys_attr_cntlid_min_store_locked(struct nvmet_subsys *subsys,
+		u16 cntlid_min, size_t cnt)
+{
+
+	if (subsys->subsys_discovered) {
+		pr_err("Can't set minimal cntlid. %u is already assigned\n",
+		       subsys->cntlid_min);
+		return -EINVAL;
+	}
+
+	if (cntlid_min > subsys->cntlid_max)
+		return -EINVAL;
+
+	subsys->cntlid_min = cntlid_min;
+	return cnt;
+}
+
 static ssize_t nvmet_subsys_attr_cntlid_min_store(struct config_item *item,
 						  const char *page, size_t cnt)
 {
+	struct nvmet_subsys *subsys = to_subsys(item);
 	u16 cntlid_min;
+	ssize_t ret;
 
 	if (sscanf(page, "%hu\n", &cntlid_min) != 1)
 		return -EINVAL;
@@ -1360,15 +1380,11 @@ static ssize_t nvmet_subsys_attr_cntlid_min_store(struct config_item *item,
 		return -EINVAL;
 
 	down_write(&nvmet_config_sem);
-	if (cntlid_min > to_subsys(item)->cntlid_max)
-		goto out_unlock;
-	to_subsys(item)->cntlid_min = cntlid_min;
-	up_write(&nvmet_config_sem);
-	return cnt;
-
-out_unlock:
+	mutex_lock(&subsys->lock);
+	ret = nvmet_subsys_attr_cntlid_min_store_locked(subsys, cntlid_min, cnt);
+	mutex_unlock(&subsys->lock);
 	up_write(&nvmet_config_sem);
-	return -EINVAL;
+	return ret;
 }
 CONFIGFS_ATTR(nvmet_subsys_, attr_cntlid_min);
 
@@ -1378,10 +1394,29 @@ static ssize_t nvmet_subsys_attr_cntlid_max_show(struct config_item *item,
 	return snprintf(page, PAGE_SIZE, "%u\n", to_subsys(item)->cntlid_max);
 }
 
+static ssize_t
+nvmet_subsys_attr_cntlid_max_store_locked(struct nvmet_subsys *subsys,
+		u16 cntlid_max, size_t cnt)
+{
+
+	if (subsys->subsys_discovered) {
+		pr_err("Can't set maximal cntlid. %u is already assigned\n",
+		       subsys->cntlid_max);
+		return -EINVAL;
+	}
+
+	if (cntlid_max < subsys->cntlid_min)
+		return -EINVAL;
+	subsys->cntlid_max = cntlid_max;
+	return cnt;
+}
+
 static ssize_t nvmet_subsys_attr_cntlid_max_store(struct config_item *item,
 						  const char *page, size_t cnt)
 {
+	struct nvmet_subsys *subsys = to_subsys(item);
 	u16 cntlid_max;
+	ssize_t ret;
 
 	if (sscanf(page, "%hu\n", &cntlid_max) != 1)
 		return -EINVAL;
@@ -1390,15 +1425,11 @@ static ssize_t nvmet_subsys_attr_cntlid_max_store(struct config_item *item,
 		return -EINVAL;
 
 	down_write(&nvmet_config_sem);
-	if (cntlid_max < to_subsys(item)->cntlid_min)
-		goto out_unlock;
-	to_subsys(item)->cntlid_max = cntlid_max;
-	up_write(&nvmet_config_sem);
-	return cnt;
-
-out_unlock:
+	mutex_lock(&subsys->lock);
+	ret = nvmet_subsys_attr_cntlid_max_store_locked(subsys, cntlid_max, cnt);
+	mutex_unlock(&subsys->lock);
 	up_write(&nvmet_config_sem);
-	return -EINVAL;
+	return ret;
 }
 CONFIGFS_ATTR(nvmet_subsys_, attr_cntlid_max);
 
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 099a05409ac5..20e7b3d6a810 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1667,6 +1667,12 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
 	if (!ctrl->changed_ns_list)
 		goto out_free_ctrl;
 
+	if (!subsys->subsys_discovered) {
+		mutex_lock(&subsys->lock);
+		subsys->subsys_discovered = true;
+		mutex_unlock(&subsys->lock);
+	}
+
 	ctrl->sqs = kcalloc(subsys->max_qid + 1,
 			sizeof(struct nvmet_sq *),
 			GFP_KERNEL);
-- 
2.18.1




More information about the Linux-nvme mailing list