nvmet: implement unique discovery NQN
authorHannes Reinecke <hare@kernel.org>
Wed, 3 Apr 2024 11:31:14 +0000 (13:31 +0200)
committerKeith Busch <kbusch@kernel.org>
Thu, 4 Apr 2024 15:35:49 +0000 (08:35 -0700)
Unique discovery NQNs allow to differentiate between discovery
services from (typically physically separate) NVMe-oF subsystems.
This is required for establishing secured connections as otherwise
the credentials won't be unique and the integrity of the connection
cannot be guaranteed.
This patch adds a configfs attribute 'discovery_nqn' in the 'nvmet'
configfs directory to specify the unique discovery NQN.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
Signed-off-by: Keith Busch <kbusch@kernel.org>
drivers/nvme/target/configfs.c
drivers/nvme/target/core.c

index 77a6e817b31596998e4424aa8205f8cfd9219f1d..a2325330bf22145202837aa5cf89d9ec6543ab59 100644 (file)
@@ -1613,6 +1613,11 @@ static struct config_group *nvmet_subsys_make(struct config_group *group,
                return ERR_PTR(-EINVAL);
        }
 
+       if (sysfs_streq(name, nvmet_disc_subsys->subsysnqn)) {
+               pr_err("can't create subsystem using unique discovery NQN\n");
+               return ERR_PTR(-EINVAL);
+       }
+
        subsys = nvmet_subsys_alloc(name, NVME_NQN_NVME);
        if (IS_ERR(subsys))
                return ERR_CAST(subsys);
@@ -2159,7 +2164,49 @@ static const struct config_item_type nvmet_hosts_type = {
 
 static struct config_group nvmet_hosts_group;
 
+static ssize_t nvmet_root_discovery_nqn_show(struct config_item *item,
+                                            char *page)
+{
+       return snprintf(page, PAGE_SIZE, "%s\n", nvmet_disc_subsys->subsysnqn);
+}
+
+static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
+               const char *page, size_t count)
+{
+       struct list_head *entry;
+       size_t len;
+
+       len = strcspn(page, "\n");
+       if (!len || len > NVMF_NQN_FIELD_LEN - 1)
+               return -EINVAL;
+
+       down_write(&nvmet_config_sem);
+       list_for_each(entry, &nvmet_subsystems_group.cg_children) {
+               struct config_item *item =
+                       container_of(entry, struct config_item, ci_entry);
+
+               if (!strncmp(config_item_name(item), page, len)) {
+                       pr_err("duplicate NQN %s\n", config_item_name(item));
+                       up_write(&nvmet_config_sem);
+                       return -EINVAL;
+               }
+       }
+       memset(nvmet_disc_subsys->subsysnqn, 0, NVMF_NQN_FIELD_LEN);
+       memcpy(nvmet_disc_subsys->subsysnqn, page, len);
+       up_write(&nvmet_config_sem);
+
+       return len;
+}
+
+CONFIGFS_ATTR(nvmet_root_, discovery_nqn);
+
+static struct configfs_attribute *nvmet_root_attrs[] = {
+       &nvmet_root_attr_discovery_nqn,
+       NULL,
+};
+
 static const struct config_item_type nvmet_root_type = {
+       .ct_attrs               = nvmet_root_attrs,
        .ct_owner               = THIS_MODULE,
 };
 
index 6bbe4df0166ca56949a5f5b14ad90f68305d6f36..8860a3eb71ec891e948a34060f34b4b148553418 100644 (file)
@@ -1541,6 +1541,13 @@ static struct nvmet_subsys *nvmet_find_get_subsys(struct nvmet_port *port,
        }
 
        down_read(&nvmet_config_sem);
+       if (!strncmp(nvmet_disc_subsys->subsysnqn, subsysnqn,
+                               NVMF_NQN_SIZE)) {
+               if (kref_get_unless_zero(&nvmet_disc_subsys->ref)) {
+                       up_read(&nvmet_config_sem);
+                       return nvmet_disc_subsys;
+               }
+       }
        list_for_each_entry(p, &port->subsystems, entry) {
                if (!strncmp(p->subsys->subsysnqn, subsysnqn,
                                NVMF_NQN_SIZE)) {