nvme: fixes for NVME_IOCTL_IO_CMD on the char device
authorChristoph Hellwig <hch@lst.de>
Thu, 24 Dec 2015 14:27:01 +0000 (15:27 +0100)
committerJens Axboe <axboe@fb.com>
Tue, 12 Jan 2016 20:30:15 +0000 (13:30 -0700)
Make sure we synchronize access to the namespaces list and grab a reference
to the namespace before doing I/O.  Make sure to reject the ioctl if multiple
namespaces are present as it's entirely unsafe, and warn when using it even
with a single namespace.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Sagi Grimberg <sagig@mellanox.com>
Acked-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
drivers/nvme/host/core.c

index a928ad5..51f6fc8 100644 (file)
@@ -922,21 +922,50 @@ static int nvme_dev_release(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int nvme_dev_user_cmd(struct nvme_ctrl *ctrl, void __user *argp)
+{
+       struct nvme_ns *ns;
+       int ret;
+
+       mutex_lock(&ctrl->namespaces_mutex);
+       if (list_empty(&ctrl->namespaces)) {
+               ret = -ENOTTY;
+               goto out_unlock;
+       }
+
+       ns = list_first_entry(&ctrl->namespaces, struct nvme_ns, list);
+       if (ns != list_last_entry(&ctrl->namespaces, struct nvme_ns, list)) {
+               dev_warn(ctrl->dev,
+                       "NVME_IOCTL_IO_CMD not supported when multiple namespaces present!\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       dev_warn(ctrl->dev,
+               "using deprecated NVME_IOCTL_IO_CMD ioctl on the char device!\n");
+       kref_get(&ns->kref);
+       mutex_unlock(&ctrl->namespaces_mutex);
+
+       ret = nvme_user_cmd(ctrl, ns, argp);
+       nvme_put_ns(ns);
+       return ret;
+
+out_unlock:
+       mutex_unlock(&ctrl->namespaces_mutex);
+       return ret;
+}
+
 static long nvme_dev_ioctl(struct file *file, unsigned int cmd,
                unsigned long arg)
 {
        struct nvme_ctrl *ctrl = file->private_data;
        void __user *argp = (void __user *)arg;
-       struct nvme_ns *ns;
 
        switch (cmd) {
        case NVME_IOCTL_ADMIN_CMD:
                return nvme_user_cmd(ctrl, NULL, argp);
        case NVME_IOCTL_IO_CMD:
-               if (list_empty(&ctrl->namespaces))
-                       return -ENOTTY;
-               ns = list_first_entry(&ctrl->namespaces, struct nvme_ns, list);
-               return nvme_user_cmd(ctrl, ns, argp);
+               return nvme_dev_user_cmd(ctrl, argp);
        case NVME_IOCTL_RESET:
                dev_warn(ctrl->dev, "resetting controller\n");
                return ctrl->ops->reset_ctrl(ctrl);