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 <[email protected]> --- 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

