Thread (4 messages) 4 messages, 2 authors, 2021-03-29

Re: [PATCH 1/1] nvme: introduce generic per-namespace chardev

From: Kanchan Joshi <hidden>
Date: 2021-03-29 17:54:45

On Thu, Mar 25, 2021 at 6:08 PM Minwoo Im [off-list ref] wrote:
quoted hunk ↗ jump to hunk
Userspace has not been allowed to I/O to device that's failed to
be initialized.  This patch introduces generic per-namespace character
device to allow userspace to I/O regardless the block device is there or
not.

The chardev naming convention will exactly be the same with the existing
blkdev's one.  nvme_set_generic_ns_name() introduced in this patch may
look like a duplication of nvme_set_disk_name(), but it's not allowing
controller path specific.  It just for per-namespace path specific.  So,
the naming will be:

        - /dev/nvme-generic-XnY

It also supports multipath which means it will not expose chardev for
the hidden namespace blkdevs (e.g., nvmeXcYnZ).  If
/dev/nvme-generic-XnY is created for the ns_head, then I/O request will
be routed to the controller-specified path by the iopolicy in the
subsystem.

This can be controlled by the module parameter `generic_ns` which is
turned on by default.

Signed-off-by: Minwoo Im <redacted>
Signed-off-by: Javier González <redacted>
---
 drivers/nvme/host/core.c      | 170 +++++++++++++++++++++++++++++++++-
 drivers/nvme/host/multipath.c |  17 ++++
 drivers/nvme/host/nvme.h      |  25 +++++
 3 files changed, 209 insertions(+), 3 deletions(-)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index c371db47de3c..7dc0e216524b 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -61,6 +61,10 @@ static bool streams;
 module_param(streams, bool, 0644);
 MODULE_PARM_DESC(streams, "turn on support for Streams write directives");

+static bool generic_ns = true;
+module_param(generic_ns, bool, 0644);
+MODULE_PARM_DESC(generic_ns, "support generic namespace character device");
+
 /*
  * nvme_wq - hosts nvme related works that are not reset or delete
  * nvme_reset_wq - hosts nvme reset works
@@ -85,8 +89,11 @@ static LIST_HEAD(nvme_subsystems);
 static DEFINE_MUTEX(nvme_subsystems_lock);

 static DEFINE_IDA(nvme_instance_ida);
+static DEFINE_IDA(nvme_generic_ns_minor_ida);
 static dev_t nvme_ctrl_base_chr_devt;
+static dev_t nvme_generic_ns_devt;
 static struct class *nvme_class;
+static struct class *nvme_generic_ns_class;
 static struct class *nvme_subsys_class;

 static void nvme_put_subsystem(struct nvme_subsystem *subsys);
@@ -542,6 +549,7 @@ static void nvme_free_ns_head(struct kref *ref)
        struct nvme_ns_head *head =
                container_of(ref, struct nvme_ns_head, ref);

+       cdev_device_del(&head->generic_ns->cdev, &head->generic_ns->device);
        nvme_mpath_remove_disk(head);
        ida_simple_remove(&head->subsys->ns_ida, head->instance);
        cleanup_srcu_struct(&head->srcu);
@@ -3784,6 +3792,126 @@ static int __nvme_check_ids(struct nvme_subsystem *subsys,
        return 0;
 }

+#ifdef CONFIG_NVME_MULTIPATH
+static int nvme_generic_ns_open(struct inode *inode, struct file *file)
+{
+       struct nvme_generic_ns *generic_ns = container_of(inode->i_cdev,
+                       struct nvme_generic_ns, cdev);
+
+       if (!generic_ns->head->disk)
+               return -ENODEV;
+
+       file->private_data = generic_ns;
+       return nvme_ns_head_open(generic_ns->head->disk->part0, file->f_mode);
+}
+
+static int nvme_generic_ns_release(struct inode *inode, struct file *file)
+{
+       struct nvme_generic_ns *generic_ns = container_of(inode->i_cdev,
+                       struct nvme_generic_ns, cdev);
+
+       nvme_ns_head_release(generic_ns->head->disk, file->f_mode);
+
+       return 0;
+}
+
+static long nvme_generic_ns_ioctl(struct file *file, unsigned int cmd,
+               unsigned long arg)
+{
+       struct nvme_generic_ns *generic_ns = file->private_data;
+
+       return nvme_ioctl(generic_ns->head->disk->part0, file->f_mode, cmd, arg);
+}
+#else
+static int nvme_generic_ns_open(struct inode *inode, struct file *file)
+{
+       struct nvme_generic_ns *generic_ns = container_of(inode->i_cdev,
+                       struct nvme_generic_ns, cdev);
+
+       if (!generic_ns->ns)
+               return -ENODEV;
+
+       file->private_data = generic_ns;
+       return nvme_open(generic_ns->ns->disk->part0, file->f_mode);
+}
+
+static int nvme_generic_ns_release(struct inode *inode, struct file *file)
+{
+       struct nvme_generic_ns *generic_ns = container_of(inode->i_cdev,
+                       struct nvme_generic_ns, cdev);
+
+       nvme_release(generic_ns->ns->disk, file->f_mode);
+
+       return 0;
+}
+
+static long nvme_generic_ns_ioctl(struct file *file, unsigned int cmd,
+               unsigned long arg)
+{
+       struct nvme_generic_ns *generic_ns = file->private_data;
+
+       return nvme_ioctl(generic_ns->ns->disk->part0, file->f_mode, cmd, arg);
+}
+#endif
+
+static const struct file_operations nvme_generic_ns_fops = {
+       .owner          = THIS_MODULE,
+       .open           = nvme_generic_ns_open,
+       .release        = nvme_generic_ns_release,
+       .unlocked_ioctl = nvme_generic_ns_ioctl,
+       .compat_ioctl   = compat_ptr_ioctl,
+};
+
+static int nvme_alloc_generic_ns(struct nvme_ctrl *ctrl,
+               struct nvme_ns_head *head, struct nvme_ns *ns)
+{
+       struct nvme_generic_ns *generic_ns;
+       char name[DISK_NAME_LEN];
+       int minor;
+       int ret;
+
+       generic_ns = kzalloc_node(sizeof(*generic_ns), GFP_KERNEL,
+                       ctrl->numa_node);
+       if (!generic_ns)
+               return -ENOMEM;
+
+       ret = ida_simple_get(&nvme_generic_ns_minor_ida, 0, 0, GFP_KERNEL);
+       if (ret < 0)
+               goto free_ns;
+       minor = ret;
+
+       device_initialize(&generic_ns->device);
+       generic_ns->device.devt = MKDEV(MAJOR(nvme_generic_ns_devt), minor);
+       generic_ns->device.class = nvme_generic_ns_class;
+       generic_ns->device.parent = ctrl->device;
+       dev_set_drvdata(&generic_ns->device, generic_ns);
+
+       nvme_set_generic_ns_name(name, head, ctrl);
+       ret = dev_set_name(&generic_ns->device, "%s", name);
+       if (ret)
+               goto put_ida;
+
+       cdev_init(&generic_ns->cdev, &nvme_generic_ns_fops);
+       generic_ns->cdev.owner = ctrl->ops->module;
+
+       ret = cdev_device_add(&generic_ns->cdev, &generic_ns->device);
+       if (ret)
+               goto free_kobj;
+
+       head->generic_ns = generic_ns;
+       generic_ns->head = head;
+       generic_ns->ns = ns;
+       return 0;
+
+free_kobj:
+       kfree_const(generic_ns->device.kobj.name);
+put_ida:
+       ida_simple_remove(&nvme_generic_ns_minor_ida, minor);
+free_ns:
+       kfree(generic_ns);
+       return ret;
+}
+
 static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
                unsigned nsid, struct nvme_ns_ids *ids)
 {
@@ -3862,6 +3990,13 @@ static int nvme_init_ns_head(struct nvme_ns *ns, unsigned nsid,
                        goto out_unlock;
                }
                head->shared = is_shared;
+
+               if (generic_ns && nvme_alloc_generic_ns(ctrl, head, ns)) {
+                       dev_err(ctrl->device,
+                               "Failed to initialize generic namespace %d\n",
+                               nsid);
+                       goto out_put_ns_head;
+               }
        } else {
                ret = -EINVAL;
                if (!is_shared || !head->shared) {
@@ -3963,8 +4098,17 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid,
        memcpy(disk->disk_name, disk_name, DISK_NAME_LEN);
        ns->disk = disk;

-       if (nvme_update_ns_info(ns, id))
-               goto out_put_disk;
+       /*
+        * If the namespace update fails in a graceful manner, hide the block
+        * device, but still allow for the generic namespae device to be
+        * craeted.
+        */
+       if (nvme_update_ns_info(ns, id)) {
+               if (generic_ns)
+                       ns->disk->flags |= GENHD_FL_HIDDEN;
+               else
+                       goto out_put_disk;
+       }

        if ((ctrl->quirks & NVME_QUIRK_LIGHTNVM) && id->vs[0] == 0x1) {
                if (nvme_nvm_register(ns, disk_name, node)) {
@@ -3983,6 +4127,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid,

        nvme_mpath_add_disk(ns, id);
        nvme_fault_inject_init(&ns->fault_inject, ns->disk->disk_name);
+
        kfree(id);

        return;
@@ -4802,13 +4947,31 @@ static int __init nvme_core_init(void)
        }
        nvme_class->dev_uevent = nvme_class_uevent;

+       if (!generic_ns) return 0;
This will suppress creation of nvme_subsys_class when generic_ns
parameter is set as 0.
quoted hunk ↗ jump to hunk
+       result = alloc_chrdev_region(&nvme_generic_ns_devt, 0,
+                       NVME_MINORS, "nvme-generic-ns");
+       if (result < 0)
+               goto destroy_class;
+
        nvme_subsys_class = class_create(THIS_MODULE, "nvme-subsystem");
        if (IS_ERR(nvme_subsys_class)) {
                result = PTR_ERR(nvme_subsys_class);
-               goto destroy_class;
+               goto unregister_generic_ns;
        }
+
+       nvme_generic_ns_class = class_create(THIS_MODULE, "nvme-generic-ns");
+       if (IS_ERR(nvme_generic_ns_class)) {
+               result = PTR_ERR(nvme_subsys_class);
+               goto destroy_subsys_class;
+       }
+
        return 0;

+destroy_subsys_class:
+       class_destroy(nvme_subsys_class);
+unregister_generic_ns:
+       unregister_chrdev_region(nvme_generic_ns_devt, NVME_MINORS);
 destroy_class:
        class_destroy(nvme_class);
 unregister_chrdev:
@@ -4827,6 +4990,7 @@ static void __exit nvme_core_exit(void)
 {
        class_destroy(nvme_subsys_class);
        class_destroy(nvme_class);
+       unregister_chrdev_region(nvme_generic_ns_devt, NVME_MINORS);
        unregister_chrdev_region(nvme_ctrl_base_chr_devt, NVME_MINORS);
        destroy_workqueue(nvme_delete_wq);
        destroy_workqueue(nvme_reset_wq);
The "nvme-generic-ns" class has not been destroyed here.

-- 
Kanchan

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help