From: javier@javigon.com
To: linux-nvme@lists.infradead.org
Cc: sagi@grimberg.me, linux-block@vger.kernel.org,
minwoo.im.dev@gmail.com, kbusch@kernel.org,
"Javier González" <javier.gonz@samsung.com>,
hch@lst.de
Subject: [PATCH V5 1/2] nvme: enable char device per namespace
Date: Mon, 22 Feb 2021 20:01:06 +0100 [thread overview]
Message-ID: <20210222190107.8479-2-javier.gonz@samsung.com> (raw)
In-Reply-To: <20210222190107.8479-1-javier.gonz@samsung.com>
From: Javier González <javier.gonz@samsung.com>
Create a char device per NVMe namespace. This char device is always
initialized, independently of whether the features implemented by the
device are supported by the kernel. User-space can therefore always
issue IOCTLs to the NVMe driver using the char device.
The char device is presented as /dev/nvme-generic-XcYnZ. This naming
scheme follows the convention of the hidden device (nvmeXcYnZ). Support
for multipath will follow.
Signed-off-by: Javier González <javier.gonz@samsung.com>
Signed-off-by: Minwoo Im <minwoo.im.dev@gmail.com>
---
drivers/nvme/host/core.c | 171 +++++++++++++++++++++++++++++++++++----
drivers/nvme/host/nvme.h | 9 +++
2 files changed, 164 insertions(+), 16 deletions(-)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index d77f3f26d8d3..d4884105ad95 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -86,13 +86,27 @@ static DEFINE_MUTEX(nvme_subsystems_lock);
static DEFINE_IDA(nvme_instance_ida);
static dev_t nvme_ctrl_base_chr_devt;
+
+static DEFINE_IDA(nvme_gen_minor_ida);
+static dev_t nvme_ns_base_chr_devt;
static struct class *nvme_class;
+static struct class *nvme_ns_class;
static struct class *nvme_subsys_class;
static void nvme_put_subsystem(struct nvme_subsystem *subsys);
static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
unsigned nsid);
+static inline bool nvme_dev_is_generic(struct device *dev)
+{
+ return dev->class == nvme_ns_class;
+}
+
+static inline bool nvme_ns_is_generic(struct nvme_ns *ns)
+{
+ return !!ns->minor;
+}
+
/*
* Prepare a queue for teardown.
*
@@ -559,7 +573,10 @@ static void nvme_free_ns(struct kref *kref)
if (ns->ndev)
nvme_nvm_unregister(ns);
+ if (nvme_ns_is_generic(ns))
+ ida_simple_remove(&nvme_gen_minor_ida, ns->minor - 1);
+ cdev_device_del(&ns->cdev, &ns->cdev_device);
put_disk(ns->disk);
nvme_put_ns_head(ns->head);
nvme_put_ctrl(ns->ctrl);
@@ -1772,15 +1789,15 @@ static int nvme_handle_ctrl_ioctl(struct nvme_ns *ns, unsigned int cmd,
return ret;
}
-static int nvme_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg)
+static int nvme_disk_ioctl(struct gendisk *disk, unsigned int cmd,
+ unsigned long arg)
{
struct nvme_ns_head *head = NULL;
void __user *argp = (void __user *)arg;
struct nvme_ns *ns;
int srcu_idx, ret;
- ns = nvme_get_ns_from_disk(bdev->bd_disk, &head, &srcu_idx);
+ ns = nvme_get_ns_from_disk(disk, &head, &srcu_idx);
if (unlikely(!ns))
return -EWOULDBLOCK;
@@ -1817,6 +1834,12 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode,
return ret;
}
+static int nvme_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ return nvme_disk_ioctl(bdev->bd_disk, cmd, arg);
+}
+
#ifdef CONFIG_COMPAT
struct nvme_user_io32 {
__u8 opcode;
@@ -1858,10 +1881,8 @@ static int nvme_compat_ioctl(struct block_device *bdev, fmode_t mode,
#define nvme_compat_ioctl NULL
#endif /* CONFIG_COMPAT */
-static int nvme_open(struct block_device *bdev, fmode_t mode)
+static int nvme_ns_open(struct nvme_ns *ns)
{
- struct nvme_ns *ns = bdev->bd_disk->private_data;
-
#ifdef CONFIG_NVME_MULTIPATH
/* should never be called due to GENHD_FL_HIDDEN */
if (WARN_ON_ONCE(ns->head->disk))
@@ -1880,14 +1901,22 @@ static int nvme_open(struct block_device *bdev, fmode_t mode)
return -ENXIO;
}
-static void nvme_release(struct gendisk *disk, fmode_t mode)
+static void nvme_ns_release(struct nvme_ns *ns)
{
- struct nvme_ns *ns = disk->private_data;
-
module_put(ns->ctrl->ops->module);
nvme_put_ns(ns);
}
+static int nvme_open(struct block_device *bdev, fmode_t mode)
+{
+ return nvme_ns_open(bdev->bd_disk->private_data);
+}
+
+static void nvme_release(struct gendisk *disk, fmode_t mode)
+{
+ nvme_ns_release(disk->private_data);
+}
+
static int nvme_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
/* some standard values */
@@ -2241,6 +2270,13 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_id_ns *id)
return 0;
out_unfreeze:
+ /*
+ * When the device does not support any of the features required by the
+ * kernel (or viceversa), hide the block device. We can still rely on
+ * the namespace char device for submitting IOCTLs
+ */
+ ns->disk->flags |= GENHD_FL_HIDDEN;
+
blk_mq_unfreeze_queue(ns->disk->queue);
return ret;
}
@@ -2378,6 +2414,38 @@ static const struct block_device_operations nvme_bdev_ops = {
.pr_ops = &nvme_pr_ops,
};
+static int nvme_cdev_open(struct inode *inode, struct file *file)
+{
+ struct nvme_ns *ns = container_of(inode->i_cdev, struct nvme_ns, cdev);
+
+ return nvme_ns_open(ns);
+}
+
+static int nvme_cdev_release(struct inode *inode, struct file *file)
+{
+ struct nvme_ns *ns = container_of(inode->i_cdev, struct nvme_ns, cdev);
+
+ nvme_ns_release(ns);
+ return 0;
+}
+
+static long nvme_cdev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct nvme_ns *ns = container_of(file->f_inode->i_cdev,
+ struct nvme_ns, cdev);
+
+ return nvme_disk_ioctl(ns->disk, cmd, arg);
+}
+
+static const struct file_operations nvme_cdev_fops = {
+ .owner = THIS_MODULE,
+ .open = nvme_cdev_open,
+ .release = nvme_cdev_release,
+ .unlocked_ioctl = nvme_cdev_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+};
+
#ifdef CONFIG_NVME_MULTIPATH
static int nvme_ns_head_open(struct block_device *bdev, fmode_t mode)
{
@@ -3379,6 +3447,9 @@ static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev)
{
struct gendisk *disk = dev_to_disk(dev);
+ if (nvme_dev_is_generic(dev))
+ return nvme_get_ns_from_cdev(dev)->head;
+
if (disk->fops == &nvme_bdev_ops)
return nvme_get_ns_from_dev(dev)->head;
else
@@ -3488,6 +3559,8 @@ static umode_t nvme_ns_id_attrs_are_visible(struct kobject *kobj,
}
#ifdef CONFIG_NVME_MULTIPATH
if (a == &dev_attr_ana_grpid.attr || a == &dev_attr_ana_state.attr) {
+ if (nvme_dev_is_generic(dev))
+ return 0;
if (dev_to_disk(dev)->fops != &nvme_bdev_ops) /* per-path attr */
return 0;
if (!nvme_ctrl_use_ana(nvme_get_ns_from_dev(dev)->ctrl))
@@ -3510,6 +3583,11 @@ const struct attribute_group *nvme_ns_id_attr_groups[] = {
NULL,
};
+const struct attribute_group *nvme_ns_char_id_attr_groups[] = {
+ &nvme_ns_id_attr_group,
+ NULL,
+};
+
#define nvme_show_str_function(field) \
static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
@@ -3902,6 +3980,47 @@ struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid)
}
EXPORT_SYMBOL_NS_GPL(nvme_find_get_ns, NVME_TARGET_PASSTHRU);
+static int nvme_alloc_chardev_ns(struct nvme_ctrl *ctrl, struct nvme_ns *ns)
+{
+ char cdisk_name[DISK_NAME_LEN];
+ int ret;
+
+ ret = ida_simple_get(&nvme_gen_minor_ida, 0, 0, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ ns->minor = ret + 1;
+ device_initialize(&ns->cdev_device);
+ ns->cdev_device.devt = MKDEV(MAJOR(nvme_ns_base_chr_devt), ret);
+ ns->cdev_device.class = nvme_ns_class;
+ ns->cdev_device.parent = ctrl->device;
+ ns->cdev_device.groups = nvme_ns_char_id_attr_groups;
+ dev_set_drvdata(&ns->cdev_device, ns);
+
+ sprintf(cdisk_name, "nvme-generic-%dc%dn%d", ctrl->subsys->instance,
+ ctrl->instance, ns->head->instance);
+
+ ret = dev_set_name(&ns->cdev_device, "%s", cdisk_name);
+ if (ret)
+ goto put_ida;
+
+ cdev_init(&ns->cdev, &nvme_cdev_fops);
+ ns->cdev.owner = ctrl->ops->module;
+
+ ret = cdev_device_add(&ns->cdev, &ns->cdev_device);
+ if (ret)
+ goto free_kobj;
+
+ return ret;
+
+free_kobj:
+ kfree_const(ns->cdev_device.kobj.name);
+put_ida:
+ ida_simple_remove(&nvme_gen_minor_ida, ns->minor - 1);
+ ns->minor = 0;
+ return ret;
+}
+
static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid,
struct nvme_ns_ids *ids)
{
@@ -3948,8 +4067,7 @@ 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;
+ nvme_update_ns_info(ns, id);
if ((ctrl->quirks & NVME_QUIRK_LIGHTNVM) && id->vs[0] == 0x1) {
if (nvme_nvm_register(ns, disk_name, node)) {
@@ -3965,9 +4083,14 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid,
nvme_get_ctrl(ctrl);
device_add_disk(ctrl->device, ns->disk, nvme_ns_id_attr_groups);
-
nvme_mpath_add_disk(ns, id);
nvme_fault_inject_init(&ns->fault_inject, ns->disk->disk_name);
+
+ if (nvme_alloc_chardev_ns(ctrl, ns))
+ dev_warn(ctrl->device,
+ "failed to create generic handle for nsid:%d\n",
+ nsid);
+
kfree(id);
return;
@@ -4780,23 +4903,38 @@ static int __init nvme_core_init(void)
if (result < 0)
goto destroy_delete_wq;
+ result = alloc_chrdev_region(&nvme_ns_base_chr_devt, 0,
+ NVME_MINORS, "nvmec");
+ if (result < 0)
+ goto unregister_dev_chrdev;
+
nvme_class = class_create(THIS_MODULE, "nvme");
if (IS_ERR(nvme_class)) {
result = PTR_ERR(nvme_class);
- goto unregister_chrdev;
+ goto unregister_ns_chrdev;
}
nvme_class->dev_uevent = nvme_class_uevent;
+ nvme_ns_class = class_create(THIS_MODULE, "nvme-ns");
+ if (IS_ERR(nvme_ns_class)) {
+ result = PTR_ERR(nvme_ns_class);
+ goto destroy_dev_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 destroy_ns_class;
}
return 0;
-destroy_class:
+destroy_ns_class:
+ class_destroy(nvme_ns_class);
+destroy_dev_class:
class_destroy(nvme_class);
-unregister_chrdev:
+unregister_ns_chrdev:
+ unregister_chrdev_region(nvme_ns_base_chr_devt, NVME_MINORS);
+unregister_dev_chrdev:
unregister_chrdev_region(nvme_ctrl_base_chr_devt, NVME_MINORS);
destroy_delete_wq:
destroy_workqueue(nvme_delete_wq);
@@ -4812,6 +4950,7 @@ static void __exit nvme_core_exit(void)
{
class_destroy(nvme_subsys_class);
class_destroy(nvme_class);
+ unregister_chrdev_region(nvme_ns_base_chr_devt, NVME_MINORS);
unregister_chrdev_region(nvme_ctrl_base_chr_devt, NVME_MINORS);
destroy_workqueue(nvme_delete_wq);
destroy_workqueue(nvme_reset_wq);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 07b34175c6ce..8528caab61c5 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -442,6 +442,10 @@ struct nvme_ns {
struct kref kref;
struct nvme_ns_head *head;
+ struct device cdev_device; /* char device */
+ struct cdev cdev;
+ int minor;
+
int lba_shift;
u16 ms;
u16 sgs;
@@ -819,6 +823,11 @@ static inline struct nvme_ns *nvme_get_ns_from_dev(struct device *dev)
return dev_to_disk(dev)->private_data;
}
+static inline struct nvme_ns *nvme_get_ns_from_cdev(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
#ifdef CONFIG_NVME_HWMON
int nvme_hwmon_init(struct nvme_ctrl *ctrl);
void nvme_hwmon_exit(struct nvme_ctrl *ctrl);
--
2.17.1
_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme
next prev parent reply other threads:[~2021-02-22 19:01 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-02-22 19:01 [PATCH V5 0/2] nvme: enable char device per namespace javier
2021-02-22 19:01 ` javier [this message]
2021-02-24 16:44 ` [PATCH V5 1/2] " Christoph Hellwig
2021-02-24 20:28 ` Javier González
2021-02-22 19:01 ` [PATCH V5 2/2] nvme: allow open for nvme-generic char device javier
2021-02-24 16:45 ` Christoph Hellwig
2021-02-24 18:44 ` Minwoo Im
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210222190107.8479-2-javier.gonz@samsung.com \
--to=javier@javigon.com \
--cc=hch@lst.de \
--cc=javier.gonz@samsung.com \
--cc=kbusch@kernel.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-nvme@lists.infradead.org \
--cc=minwoo.im.dev@gmail.com \
--cc=sagi@grimberg.me \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).