NVMe: Asynchronous controller probe
authorKeith Busch <keith.busch@intel.com>
Thu, 12 Feb 2015 22:33:00 +0000 (15:33 -0700)
committerKeith Busch <keith.busch@intel.com>
Thu, 19 Feb 2015 23:15:36 +0000 (16:15 -0700)
This performs the longest parts of nvme device probe in scheduled work.
This speeds up probe significantly when multiple devices are in use.

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

index bb2b861cfed9f4afb569567e3a524d333d43e09f..a57685f74e5eebf4f50288ac20a0bcaf7b173a3b 100644 (file)
@@ -2800,6 +2800,10 @@ static int nvme_dev_open(struct inode *inode, struct file *f)
        spin_lock(&dev_list_lock);
        list_for_each_entry(dev, &dev_list, node) {
                if (dev->instance == instance) {
+                       if (!dev->admin_q) {
+                               ret = -EWOULDBLOCK;
+                               break;
+                       }
                        if (!kref_get_unless_zero(&dev->kref))
                                break;
                        f->private_data = dev;
@@ -2982,6 +2986,7 @@ static void nvme_reset_workfn(struct work_struct *work)
        dev->reset_workfn(work);
 }
 
+static void nvme_async_probe(struct work_struct *work);
 static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        int node, result = -ENOMEM;
@@ -3017,34 +3022,20 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto release;
 
        kref_init(&dev->kref);
-       result = nvme_dev_start(dev);
-       if (result)
-               goto release_pools;
-
        dev->device = device_create(nvme_class, &pdev->dev,
                                MKDEV(nvme_char_major, dev->instance),
                                dev, "nvme%d", dev->instance);
        if (IS_ERR(dev->device)) {
                result = PTR_ERR(dev->device);
-               goto shutdown;
+               goto release_pools;
        }
        get_device(dev->device);
 
-       if (dev->online_queues > 1)
-               result = nvme_dev_add(dev);
-       if (result)
-               goto device_del;
-
-       nvme_set_irq_hints(dev);
-       dev->initialized = 1;
+       INIT_WORK(&dev->probe_work, nvme_async_probe);
+       schedule_work(&dev->probe_work);
        return 0;
 
- device_del:
-       device_destroy(nvme_class, MKDEV(nvme_char_major, dev->instance));
- shutdown:
-       nvme_dev_shutdown(dev);
  release_pools:
-       nvme_free_queues(dev, 0);
        nvme_release_prp_pools(dev);
  release:
        nvme_release_instance(dev);
@@ -3057,6 +3048,28 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        return result;
 }
 
+static void nvme_async_probe(struct work_struct *work)
+{
+       struct nvme_dev *dev = container_of(work, struct nvme_dev, probe_work);
+       int result;
+
+       result = nvme_dev_start(dev);
+       if (result)
+               goto reset;
+
+       if (dev->online_queues > 1)
+               result = nvme_dev_add(dev);
+       if (result)
+               goto reset;
+
+       nvme_set_irq_hints(dev);
+       dev->initialized = 1;
+       return;
+ reset:
+       dev->reset_workfn = nvme_reset_failed_dev;
+       queue_work(nvme_workq, &dev->reset_work);
+}
+
 static void nvme_reset_notify(struct pci_dev *pdev, bool prepare)
 {
        struct nvme_dev *dev = pci_get_drvdata(pdev);
@@ -3082,6 +3095,7 @@ static void nvme_remove(struct pci_dev *pdev)
        spin_unlock(&dev_list_lock);
 
        pci_set_drvdata(pdev, NULL);
+       flush_work(&dev->probe_work);
        flush_work(&dev->reset_work);
        nvme_dev_shutdown(dev);
        nvme_dev_remove(dev);
index 383d495c5e4cb58c9451f53fc8b180e5385fae62..e2429e8cdab4f738cfa733dc330adc18dbcbabda 100644 (file)
@@ -91,6 +91,7 @@ struct nvme_dev {
        struct device *device;
        work_func_t reset_workfn;
        struct work_struct reset_work;
+       struct work_struct probe_work;
        char name[12];
        char serial[20];
        char model[40];