diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 82f5f1d030d4..818788275406 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1310,14 +1310,31 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, if (unlikely(!ns)) return -EWOULDBLOCK; + /* + * Handle ioctls that apply to the controller instead of the namespace + * seperately and drop the ns SRCU reference early. This avoids a + * deadlock when deleting namespaces using the passthrough interface. + */ + if (cmd == NVME_IOCTL_ADMIN_CMD || is_sed_ioctl(cmd)) { + struct nvme_ctrl *ctrl = ns->ctrl; + + nvme_get_ctrl(ns->ctrl); + nvme_put_ns_from_disk(head, srcu_idx); + + if (cmd == NVME_IOCTL_ADMIN_CMD) + ret = nvme_user_cmd(ctrl, NULL, argp); + else + ret = sed_ioctl(ctrl->opal_dev, cmd, argp); + + nvme_put_ctrl(ctrl); + return ret; + } + switch (cmd) { case NVME_IOCTL_ID: force_successful_syscall_return(); ret = ns->head->ns_id; break; - case NVME_IOCTL_ADMIN_CMD: - ret = nvme_user_cmd(ns->ctrl, NULL, argp); - break; case NVME_IOCTL_IO_CMD: ret = nvme_user_cmd(ns->ctrl, ns, argp); break; @@ -1327,8 +1344,6 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, default: if (ns->ndev) ret = nvme_nvm_ioctl(ns, cmd, arg); - else if (is_sed_ioctl(cmd)) - ret = sed_ioctl(ns->ctrl->opal_dev, cmd, argp); else ret = -ENOTTY; }