From: Javier González <javier.gonz@xxxxxxxxxxx>
Create a char device per NVMe namespace. This char device is always
initialized, independently of whether thedeatures implemented by the
device are supported by the kernel. User-space can therefore always
issue IOCTLs to the NVMe driver using this char device.
The char device is presented as /dev/nvmeXcYnZ to follow the hidden
block device. This naming also aligns with nvme-cli filters, so the char
device should be usable without tool changes.
Signed-off-by: Javier González <javier.gonz@xxxxxxxxxxx>
---
drivers/nvme/host/core.c | 144 +++++++++++++++++++++++++++++++++++----
drivers/nvme/host/nvme.h | 3 +
2 files changed, 132 insertions(+), 15 deletions(-)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 2c23ea6dc296..9c4acf2725f3 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -86,7 +86,9 @@ static DEFINE_MUTEX(nvme_subsystems_lock);
static DEFINE_IDA(nvme_instance_ida);
static dev_t nvme_ctrl_base_chr_devt;
+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);
@@ -497,6 +499,7 @@ static void nvme_free_ns(struct kref *kref)
if (ns->ndev)
nvme_nvm_unregister(ns);
+ cdev_device_del(&ns->cdev, &ns->cdev_device);
put_disk(ns->disk);
nvme_put_ns_head(ns->head);
nvme_put_ctrl(ns->ctrl);
@@ -1696,15 +1699,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_ns_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;
@@ -1741,6 +1744,18 @@ 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_ns_ioctl(bdev->bd_disk, cmd, arg);
+}
+
+static long nvme_cdev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return __nvme_ns_ioctl((struct gendisk *)file->private_data, cmd, arg);
+}
+
#ifdef CONFIG_COMPAT
struct nvme_user_io32 {
__u8 opcode;
@@ -1782,10 +1797,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_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))
@@ -1804,12 +1817,24 @@ static int nvme_open(struct block_device *bdev, fmode_t mode)
return -ENXIO;
}
+static void __nvme_release(struct nvme_ns *ns)
+{
+ module_put(ns->ctrl->ops->module);
+ nvme_put_ns(ns);
+}
+
+static int nvme_open(struct block_device *bdev, fmode_t mode)
+{
+ struct nvme_ns *ns = bdev->bd_disk->private_data;
+
+ return __nvme_open(ns);
+}
+
static void nvme_release(struct gendisk *disk, fmode_t mode)
{
struct nvme_ns *ns = disk->private_data;
- module_put(ns->ctrl->ops->module);
- nvme_put_ns(ns);
+ __nvme_release(ns);
}
static int nvme_getgeo(struct block_device *bdev, struct hd_geometry *geo)
@@ -1821,6 +1846,26 @@ static int nvme_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return 0;
}
+static int nvme_cdev_open(struct inode *inode, struct file *file)
+{
+ struct nvme_ns *ns = container_of(inode->i_cdev, struct nvme_ns, cdev);
+ int ret;
+
+ ret = __nvme_open(ns);
+ if (!ret)
+ file->private_data = ns->disk;
+
+ return ret;
+}
+
+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_release(ns);
+ return 0;
+}
+
#ifdef CONFIG_BLK_DEV_INTEGRITY
static void nvme_init_integrity(struct gendisk *disk, u16 ms, u8 pi_type,
u32 max_integrity_segments)
@@ -2303,6 +2348,14 @@ static const struct block_device_operations nvme_bdev_ops = {
.pr_ops = &nvme_pr_ops,
};
+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)
{
@@ -3301,6 +3354,9 @@ static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev)
{
struct gendisk *disk = dev_to_disk(dev);
+ if (dev->class == nvme_ns_class)
+ return ((struct nvme_ns *)dev_get_drvdata(dev))->head;