diff options
| author | Keith Busch <keith.busch@intel.com> | 2015-02-12 17:33:00 -0500 |
|---|---|---|
| committer | Keith Busch <keith.busch@intel.com> | 2015-02-19 18:15:36 -0500 |
| commit | 2e1d8448196ba85cd78a18723413a3c92aabe0f3 (patch) | |
| tree | ac1e4f9df21d4eb478c4cf95c5ee3488824a1021 | |
| parent | b3fffdefabab266ae5176a136d93b6670b07bb30 (diff) | |
NVMe: Asynchronous controller probe
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>
| -rw-r--r-- | drivers/block/nvme-core.c | 48 | ||||
| -rw-r--r-- | include/linux/nvme.h | 1 |
2 files changed, 32 insertions, 17 deletions
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index bb2b861cfed9..a57685f74e5e 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c | |||
| @@ -2800,6 +2800,10 @@ static int nvme_dev_open(struct inode *inode, struct file *f) | |||
| 2800 | spin_lock(&dev_list_lock); | 2800 | spin_lock(&dev_list_lock); |
| 2801 | list_for_each_entry(dev, &dev_list, node) { | 2801 | list_for_each_entry(dev, &dev_list, node) { |
| 2802 | if (dev->instance == instance) { | 2802 | if (dev->instance == instance) { |
| 2803 | if (!dev->admin_q) { | ||
| 2804 | ret = -EWOULDBLOCK; | ||
| 2805 | break; | ||
| 2806 | } | ||
| 2803 | if (!kref_get_unless_zero(&dev->kref)) | 2807 | if (!kref_get_unless_zero(&dev->kref)) |
| 2804 | break; | 2808 | break; |
| 2805 | f->private_data = dev; | 2809 | f->private_data = dev; |
| @@ -2982,6 +2986,7 @@ static void nvme_reset_workfn(struct work_struct *work) | |||
| 2982 | dev->reset_workfn(work); | 2986 | dev->reset_workfn(work); |
| 2983 | } | 2987 | } |
| 2984 | 2988 | ||
| 2989 | static void nvme_async_probe(struct work_struct *work); | ||
| 2985 | static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) | 2990 | static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) |
| 2986 | { | 2991 | { |
| 2987 | int node, result = -ENOMEM; | 2992 | int node, result = -ENOMEM; |
| @@ -3017,34 +3022,20 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
| 3017 | goto release; | 3022 | goto release; |
| 3018 | 3023 | ||
| 3019 | kref_init(&dev->kref); | 3024 | kref_init(&dev->kref); |
| 3020 | result = nvme_dev_start(dev); | ||
| 3021 | if (result) | ||
| 3022 | goto release_pools; | ||
| 3023 | |||
| 3024 | dev->device = device_create(nvme_class, &pdev->dev, | 3025 | dev->device = device_create(nvme_class, &pdev->dev, |
| 3025 | MKDEV(nvme_char_major, dev->instance), | 3026 | MKDEV(nvme_char_major, dev->instance), |
| 3026 | dev, "nvme%d", dev->instance); | 3027 | dev, "nvme%d", dev->instance); |
| 3027 | if (IS_ERR(dev->device)) { | 3028 | if (IS_ERR(dev->device)) { |
| 3028 | result = PTR_ERR(dev->device); | 3029 | result = PTR_ERR(dev->device); |
| 3029 | goto shutdown; | 3030 | goto release_pools; |
| 3030 | } | 3031 | } |
| 3031 | get_device(dev->device); | 3032 | get_device(dev->device); |
| 3032 | 3033 | ||
| 3033 | if (dev->online_queues > 1) | 3034 | INIT_WORK(&dev->probe_work, nvme_async_probe); |
| 3034 | result = nvme_dev_add(dev); | 3035 | schedule_work(&dev->probe_work); |
| 3035 | if (result) | ||
| 3036 | goto device_del; | ||
| 3037 | |||
| 3038 | nvme_set_irq_hints(dev); | ||
| 3039 | dev->initialized = 1; | ||
| 3040 | return 0; | 3036 | return 0; |
| 3041 | 3037 | ||
| 3042 | device_del: | ||
| 3043 | device_destroy(nvme_class, MKDEV(nvme_char_major, dev->instance)); | ||
| 3044 | shutdown: | ||
| 3045 | nvme_dev_shutdown(dev); | ||
| 3046 | release_pools: | 3038 | release_pools: |
| 3047 | nvme_free_queues(dev, 0); | ||
| 3048 | nvme_release_prp_pools(dev); | 3039 | nvme_release_prp_pools(dev); |
| 3049 | release: | 3040 | release: |
| 3050 | nvme_release_instance(dev); | 3041 | nvme_release_instance(dev); |
| @@ -3057,6 +3048,28 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
| 3057 | return result; | 3048 | return result; |
| 3058 | } | 3049 | } |
| 3059 | 3050 | ||
| 3051 | static void nvme_async_probe(struct work_struct *work) | ||
| 3052 | { | ||
| 3053 | struct nvme_dev *dev = container_of(work, struct nvme_dev, probe_work); | ||
| 3054 | int result; | ||
| 3055 | |||
| 3056 | result = nvme_dev_start(dev); | ||
| 3057 | if (result) | ||
| 3058 | goto reset; | ||
| 3059 | |||
| 3060 | if (dev->online_queues > 1) | ||
| 3061 | result = nvme_dev_add(dev); | ||
| 3062 | if (result) | ||
| 3063 | goto reset; | ||
| 3064 | |||
| 3065 | nvme_set_irq_hints(dev); | ||
| 3066 | dev->initialized = 1; | ||
| 3067 | return; | ||
| 3068 | reset: | ||
| 3069 | dev->reset_workfn = nvme_reset_failed_dev; | ||
| 3070 | queue_work(nvme_workq, &dev->reset_work); | ||
| 3071 | } | ||
| 3072 | |||
| 3060 | static void nvme_reset_notify(struct pci_dev *pdev, bool prepare) | 3073 | static void nvme_reset_notify(struct pci_dev *pdev, bool prepare) |
| 3061 | { | 3074 | { |
| 3062 | struct nvme_dev *dev = pci_get_drvdata(pdev); | 3075 | struct nvme_dev *dev = pci_get_drvdata(pdev); |
| @@ -3082,6 +3095,7 @@ static void nvme_remove(struct pci_dev *pdev) | |||
| 3082 | spin_unlock(&dev_list_lock); | 3095 | spin_unlock(&dev_list_lock); |
| 3083 | 3096 | ||
| 3084 | pci_set_drvdata(pdev, NULL); | 3097 | pci_set_drvdata(pdev, NULL); |
| 3098 | flush_work(&dev->probe_work); | ||
| 3085 | flush_work(&dev->reset_work); | 3099 | flush_work(&dev->reset_work); |
| 3086 | nvme_dev_shutdown(dev); | 3100 | nvme_dev_shutdown(dev); |
| 3087 | nvme_dev_remove(dev); | 3101 | nvme_dev_remove(dev); |
diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 383d495c5e4c..e2429e8cdab4 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h | |||
| @@ -91,6 +91,7 @@ struct nvme_dev { | |||
| 91 | struct device *device; | 91 | struct device *device; |
| 92 | work_func_t reset_workfn; | 92 | work_func_t reset_workfn; |
| 93 | struct work_struct reset_work; | 93 | struct work_struct reset_work; |
| 94 | struct work_struct probe_work; | ||
| 94 | char name[12]; | 95 | char name[12]; |
| 95 | char serial[20]; | 96 | char serial[20]; |
| 96 | char model[40]; | 97 | char model[40]; |
