NVMe: Add a character device for each nvme device
authorKeith Busch <keith.busch@intel.com>
Tue, 19 Feb 2013 17:17:58 +0000 (10:17 -0700)
committerMatthew Wilcox <matthew.r.wilcox@intel.com>
Tue, 16 Apr 2013 19:43:55 +0000 (15:43 -0400)
Registers a miscellaneous device for each nvme controller probed. This
creates character device files as /dev/nvmeN, where N is the device
instance, and supports nvme admin ioctl commands so devices without
namespaces can be managed.

Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
drivers/block/nvme-core.c
include/linux/nvme.h

index dbd2103..40908a0 100644 (file)
@@ -1647,6 +1647,56 @@ static void nvme_release_instance(struct nvme_dev *dev)
        spin_unlock(&dev_list_lock);
 }
 
+static void nvme_free_dev(struct kref *kref)
+{
+       struct nvme_dev *dev = container_of(kref, struct nvme_dev, kref);
+       nvme_dev_remove(dev);
+       pci_disable_msix(dev->pci_dev);
+       iounmap(dev->bar);
+       nvme_release_instance(dev);
+       nvme_release_prp_pools(dev);
+       pci_disable_device(dev->pci_dev);
+       pci_release_regions(dev->pci_dev);
+       kfree(dev->queues);
+       kfree(dev->entry);
+       kfree(dev);
+}
+
+static int nvme_dev_open(struct inode *inode, struct file *f)
+{
+       struct nvme_dev *dev = container_of(f->private_data, struct nvme_dev,
+                                                               miscdev);
+       kref_get(&dev->kref);
+       f->private_data = dev;
+       return 0;
+}
+
+static int nvme_dev_release(struct inode *inode, struct file *f)
+{
+       struct nvme_dev *dev = f->private_data;
+       kref_put(&dev->kref, nvme_free_dev);
+       return 0;
+}
+
+static long nvme_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+       struct nvme_dev *dev = f->private_data;
+       switch (cmd) {
+       case NVME_IOCTL_ADMIN_CMD:
+               return nvme_user_admin_cmd(dev, (void __user *)arg);
+       default:
+               return -ENOTTY;
+       }
+}
+
+static const struct file_operations nvme_dev_fops = {
+       .owner          = THIS_MODULE,
+       .open           = nvme_dev_open,
+       .release        = nvme_dev_release,
+       .unlocked_ioctl = nvme_dev_ioctl,
+       .compat_ioctl   = nvme_dev_ioctl,
+};
+
 static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        int bars, result = -ENOMEM;
@@ -1705,8 +1755,20 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (result)
                goto delete;
 
+       scnprintf(dev->name, sizeof(dev->name), "nvme%d", dev->instance);
+       dev->miscdev.minor = MISC_DYNAMIC_MINOR;
+       dev->miscdev.parent = &pdev->dev;
+       dev->miscdev.name = dev->name;
+       dev->miscdev.fops = &nvme_dev_fops;
+       result = misc_register(&dev->miscdev);
+       if (result)
+               goto remove;
+
+       kref_init(&dev->kref);
        return 0;
 
+ remove:
+       nvme_dev_remove(dev);
  delete:
        spin_lock(&dev_list_lock);
        list_del(&dev->node);
@@ -1732,16 +1794,8 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 static void nvme_remove(struct pci_dev *pdev)
 {
        struct nvme_dev *dev = pci_get_drvdata(pdev);
-       nvme_dev_remove(dev);
-       pci_disable_msix(pdev);
-       iounmap(dev->bar);
-       nvme_release_instance(dev);
-       nvme_release_prp_pools(dev);
-       pci_disable_device(pdev);
-       pci_release_regions(pdev);
-       kfree(dev->queues);
-       kfree(dev->entry);
-       kfree(dev);
+       misc_deregister(&dev->miscdev);
+       kref_put(&dev->kref, nvme_free_dev);
 }
 
 /* These functions are yet to be implemented */
index 7ae7ecf..9b6fba8 100644 (file)
@@ -507,6 +507,8 @@ struct nvme_admin_cmd {
 
 #ifdef __KERNEL__
 #include <linux/pci.h>
+#include <linux/miscdevice.h>
+#include <linux/kref.h>
 
 #define NVME_IO_TIMEOUT        (5 * HZ)
 
@@ -527,6 +529,9 @@ struct nvme_dev {
        struct msix_entry *entry;
        struct nvme_bar __iomem *bar;
        struct list_head namespaces;
+       struct kref kref;
+       struct miscdevice miscdev;
+       char name[12];
        char serial[20];
        char model[40];
        char firmware_rev[8];