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 /drivers/block | |
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>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/nvme-core.c | 48 |
1 files changed, 31 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); |