[PATCH 09/19] nvme-multipath: add nvme_mpath_{bdev, cdev}_ioctl()

John Garry john.g.garry at oracle.com
Wed Feb 25 07:39:57 PST 2026


Add ioctl callsbacks as follows:
- nvme_mpath_bdev_ioctl(), which does the same as nvme_ns_head_ioctl()
- nvme_mpath_cdev_ioctl(), which does the same as nvme_ns_head_chr_ioctl()

Note that ioctl callbacks are called with the mpath_head srcu read lock
taken. Since nvme_ns_head_ctrl_ioctl() releases the lock itself, it
is expected that the ioctls always release the srcu read lock themselves.

Signed-off-by: John Garry <john.g.garry at oracle.com>
---
 drivers/nvme/host/ioctl.c     | 76 +++++++++++++++++++++++++++++++++++
 drivers/nvme/host/multipath.c | 42 ++++++++++---------
 drivers/nvme/host/nvme.h      |  6 +++
 3 files changed, 104 insertions(+), 20 deletions(-)

diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index a9c097dacad6f..7f0bd38f8c24e 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -682,6 +682,25 @@ int nvme_ns_chr_uring_cmd_iopoll(struct io_uring_cmd *ioucmd,
 	return 0;
 }
 #ifdef CONFIG_NVME_MULTIPATH
+static int nvme_mpath_device_ctrl_ioctl(struct mpath_device *mpath_device,
+			unsigned int cmd, void __user *argp,
+			struct nvme_ns_head *head, int srcu_idx,
+			bool open_for_write)
+{
+	struct nvme_ns *ns = nvme_mpath_to_ns(mpath_device);
+	struct mpath_disk *mpath_disk = head->mpath_disk;
+	struct mpath_head *mpath_head = mpath_disk->mpath_head;
+	struct nvme_ctrl *ctrl = ns->ctrl;
+	int ret;
+
+	nvme_get_ctrl(ns->ctrl);
+	mpath_head_read_unlock(mpath_head, srcu_idx);
+	ret = nvme_ctrl_ioctl(ns->ctrl, cmd, argp, open_for_write);
+
+	nvme_put_ctrl(ctrl);
+	return ret;
+}
+
 static int nvme_ns_head_ctrl_ioctl(struct nvme_ns *ns, unsigned int cmd,
 		void __user *argp, struct nvme_ns_head *head, int srcu_idx,
 		bool open_for_write)
@@ -698,6 +717,63 @@ static int nvme_ns_head_ctrl_ioctl(struct nvme_ns *ns, unsigned int cmd,
 	return ret;
 }
 
+int nvme_mpath_bdev_ioctl(struct block_device *bdev,
+			struct mpath_device *mpath_device, blk_mode_t mode,
+			unsigned int cmd, unsigned long arg, int srcu_idx)
+{
+	struct gendisk *disk = bdev->bd_disk;
+	struct mpath_disk *mpath_disk = mpath_gendisk_to_disk(disk);
+	struct nvme_ns *ns = nvme_mpath_to_ns(mpath_device);
+	struct nvme_ns_head *head = ns->head;
+	bool open_for_write = mode & BLK_OPEN_WRITE;
+	void __user *argp = (void __user *)arg;
+	struct mpath_head *mpath_head = mpath_disk->mpath_head;
+	int ret = -EWOULDBLOCK;
+	unsigned int flags = 0;
+
+	if (bdev_is_partition(bdev))
+		flags |= NVME_IOCTL_PARTITION;
+
+	/*
+	 * Handle ioctls that apply to the controller instead of the namespace
+	 * separately and drop the ns SRCU reference early.  This avoids a
+	 * deadlock when deleting namespaces using the passthrough interface.
+	 */
+	if (is_ctrl_ioctl(cmd))
+		return nvme_mpath_device_ctrl_ioctl(mpath_device, cmd, argp,
+				head, srcu_idx, open_for_write);
+
+	ret = nvme_ns_ioctl(ns, cmd, argp, flags, open_for_write);
+	mpath_head_read_unlock(mpath_head, srcu_idx);
+
+	return ret;
+}
+
+int nvme_mpath_cdev_ioctl(struct mpath_head *mpath_head,
+			struct mpath_device *mpath_device, blk_mode_t mode,
+			unsigned int cmd, unsigned long arg, int srcu_idx)
+{
+	struct nvme_ns *ns = nvme_mpath_to_ns(mpath_device);
+	struct nvme_ns_head *head = ns->head;
+	bool open_for_write = mode & BLK_OPEN_WRITE;
+	void __user *argp = (void __user *)arg;
+	int ret = -EWOULDBLOCK;
+
+	/*
+	 * Handle ioctls that apply to the controller instead of the namespace
+	 * separately and drop the ns SRCU reference early.  This avoids a
+	 * deadlock when deleting namespaces using the passthrough interface.
+	 */
+	if (is_ctrl_ioctl(cmd))
+		return nvme_mpath_device_ctrl_ioctl(mpath_device, cmd, argp,
+				head, srcu_idx, open_for_write);
+
+	ret = nvme_ns_ioctl(ns, cmd, argp, 0, open_for_write);
+	mpath_head_read_unlock(mpath_head, srcu_idx);
+
+	return ret;
+}
+
 int nvme_ns_head_ioctl(struct block_device *bdev, blk_mode_t mode,
 		unsigned int cmd, unsigned long arg)
 {
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index a67db36f3c5a5..513d73e589a58 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -642,26 +642,6 @@ const struct block_device_operations nvme_ns_head_ops = {
 	.pr_ops		= &nvme_pr_ops,
 };
 
-static int nvme_mpath_add_cdev(struct mpath_head *mpath_head)
-{
-	struct nvme_ns_head *head = mpath_head->drvdata;
-	int ret;
-
-	mpath_head->cdev_device.parent = &head->subsys->dev;
-	ret = dev_set_name(&mpath_head->cdev_device, "ng%dn%d",
-			   head->subsys->instance, head->instance);
-	if (ret)
-		return ret;
-	ret = nvme_cdev_add(&mpath_head->cdev, &mpath_head->cdev_device,
-			    &mpath_generic_chr_fops, THIS_MODULE);
-	return ret;
-}
-
-static void nvme_mpath_del_cdev(struct mpath_head *mpath_head)
-{
-	nvme_cdev_del(&mpath_head->cdev, &mpath_head->cdev_device);
-}
-
 static inline struct nvme_ns_head *cdev_to_ns_head(struct cdev *cdev)
 {
 	return container_of(cdev, struct nvme_ns_head, cdev);
@@ -690,6 +670,26 @@ static const struct file_operations nvme_ns_head_chr_fops = {
 	.uring_cmd_iopoll = nvme_ns_chr_uring_cmd_iopoll,
 };
 
+static int nvme_mpath_add_cdev(struct mpath_head *mpath_head)
+{
+	struct nvme_ns_head *head = mpath_head->drvdata;
+	int ret;
+
+	mpath_head->cdev_device.parent = &head->subsys->dev;
+	ret = dev_set_name(&mpath_head->cdev_device, "ng%dn%d",
+			   head->subsys->instance, head->instance);
+	if (ret)
+		return ret;
+	ret = nvme_cdev_add(&mpath_head->cdev, &mpath_head->cdev_device,
+			    &mpath_generic_chr_fops, THIS_MODULE);
+	return ret;
+}
+
+static void nvme_mpath_del_cdev(struct mpath_head *mpath_head)
+{
+	nvme_cdev_del(&mpath_head->cdev, &mpath_head->cdev_device);
+}
+
 static int nvme_add_ns_head_cdev(struct nvme_ns_head *head)
 {
 	int ret;
@@ -1490,4 +1490,6 @@ static const struct mpath_head_template mpdt = {
 	.is_disabled = nvme_mpath_is_disabled,
 	.is_optimized = nvme_mpath_is_optimized,
 	.get_access_state = nvme_mpath_get_access_state,
+	.bdev_ioctl = nvme_mpath_bdev_ioctl,
+	.cdev_ioctl = nvme_mpath_cdev_ioctl,
 };
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index c48efbfb46efc..11b63e92502ad 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -1047,6 +1047,12 @@ void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl);
 void nvme_mpath_remove_disk(struct nvme_ns_head *head);
 void nvme_mpath_start_request(struct request *rq);
 void nvme_mpath_end_request(struct request *rq);
+int nvme_mpath_bdev_ioctl(struct block_device *bdev,
+		struct mpath_device *mpath_device, blk_mode_t mode,
+		unsigned int cmd, unsigned long arg, int srcu_idx);
+int nvme_mpath_cdev_ioctl(struct mpath_head *mpath_device,
+		struct mpath_device *mpath_head, blk_mode_t mode,
+		unsigned int cmd, unsigned long arg, int srcu_idx);
 
 static inline bool nvme_is_mpath_request(struct request *req)
 {
-- 
2.43.5




More information about the Linux-nvme mailing list