nvmet: make ctrl-id configurable
authorChaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
Thu, 30 Jan 2020 18:29:31 +0000 (10:29 -0800)
committerKeith Busch <kbusch@kernel.org>
Wed, 4 Mar 2020 17:09:08 +0000 (09:09 -0800)
This patch adds a new target subsys attribute which allows user to
optionally specify target controller IDs which then used in the
nvmet_execute_identify_ctrl() to fill up the nvme_id_ctrl structure.

For example, when using a cluster setup with two nodes, with a dual
ported NVMe drive and exporting the drive from both the nodes,
The connection to the host fails due to the same controller ID and
results in the following error message:-

"nvme nvmeX: Duplicate cntlid XXX with nvmeX, rejecting"

With this patch now user can partition the controller IDs for each
subsystem by setting up the cntlid_min and cntlid_max. These values
will be used at the time of the controller ID creation. By partitioning
the ctrl-ids for each subsystem results in the unique ctrl-id space
which avoids the collision.

When new attribute is not specified target will fall back to original
cntlid calculation method.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
Signed-off-by: Keith Busch <kbusch@kernel.org>
drivers/nvme/target/configfs.c
drivers/nvme/target/core.c
drivers/nvme/target/nvmet.h

index 403508a52e17f1637e8f037ab69400fe2cabc947..71c50751b5a66a56bdfb09e5eb248ec823135968 100644 (file)
@@ -859,10 +859,72 @@ static ssize_t nvmet_subsys_attr_serial_store(struct config_item *item,
 }
 CONFIGFS_ATTR(nvmet_subsys_, attr_serial);
 
+static ssize_t nvmet_subsys_attr_cntlid_min_show(struct config_item *item,
+                                                char *page)
+{
+       return snprintf(page, PAGE_SIZE, "%u\n", to_subsys(item)->cntlid_min);
+}
+
+static ssize_t nvmet_subsys_attr_cntlid_min_store(struct config_item *item,
+                                                 const char *page, size_t cnt)
+{
+       u16 cntlid_min;
+
+       if (sscanf(page, "%hu\n", &cntlid_min) != 1)
+               return -EINVAL;
+
+       if (cntlid_min == 0)
+               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:
+       up_write(&nvmet_config_sem);
+       return -EINVAL;
+}
+CONFIGFS_ATTR(nvmet_subsys_, attr_cntlid_min);
+
+static ssize_t nvmet_subsys_attr_cntlid_max_show(struct config_item *item,
+                                                char *page)
+{
+       return snprintf(page, PAGE_SIZE, "%u\n", to_subsys(item)->cntlid_max);
+}
+
+static ssize_t nvmet_subsys_attr_cntlid_max_store(struct config_item *item,
+                                                 const char *page, size_t cnt)
+{
+       u16 cntlid_max;
+
+       if (sscanf(page, "%hu\n", &cntlid_max) != 1)
+               return -EINVAL;
+
+       if (cntlid_max == 0)
+               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:
+       up_write(&nvmet_config_sem);
+       return -EINVAL;
+}
+CONFIGFS_ATTR(nvmet_subsys_, attr_cntlid_max);
+
 static struct configfs_attribute *nvmet_subsys_attrs[] = {
        &nvmet_subsys_attr_attr_allow_any_host,
        &nvmet_subsys_attr_attr_version,
        &nvmet_subsys_attr_attr_serial,
+       &nvmet_subsys_attr_attr_cntlid_min,
+       &nvmet_subsys_attr_attr_cntlid_max,
        NULL,
 };
 
index 576de773b4dbf62be6ee4431f9c5b40993275a68..48080c9486929dadf5413d7e6034866fda4fbf28 100644 (file)
@@ -1289,8 +1289,11 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
        if (!ctrl->sqs)
                goto out_free_cqs;
 
+       if (subsys->cntlid_min > subsys->cntlid_max)
+               goto out_free_cqs;
+
        ret = ida_simple_get(&cntlid_ida,
-                            NVME_CNTLID_MIN, NVME_CNTLID_MAX,
+                            subsys->cntlid_min, subsys->cntlid_max,
                             GFP_KERNEL);
        if (ret < 0) {
                status = NVME_SC_CONNECT_CTRL_BUSY | NVME_SC_DNR;
@@ -1438,7 +1441,8 @@ struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn,
                kfree(subsys);
                return ERR_PTR(-ENOMEM);
        }
-
+       subsys->cntlid_min = NVME_CNTLID_MIN;
+       subsys->cntlid_max = NVME_CNTLID_MAX;
        kref_init(&subsys->ref);
 
        mutex_init(&subsys->lock);
index eda28b22a2c87e3d8254cb8546cf512cf334e8e1..c2d518fb1789603a6209516eb7f56b468019d0b5 100644 (file)
@@ -211,6 +211,8 @@ struct nvmet_subsys {
        struct list_head        namespaces;
        unsigned int            nr_namespaces;
        unsigned int            max_nsid;
+       u16                     cntlid_min;
+       u16                     cntlid_max;
 
        struct list_head        ctrls;