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