aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block/nvme-core.c
diff options
context:
space:
mode:
authorKeith Busch <keith.busch@intel.com>2014-06-11 13:51:35 -0400
committerMatthew Wilcox <matthew.r.wilcox@intel.com>2014-06-13 10:43:34 -0400
commitf3db22feb5de6b98b7bae924c2d4b6c8d65bedae (patch)
tree1a2fc6e5dd199f15280a40aae824ac41b72177b7 /drivers/block/nvme-core.c
parentbd67608a6127c994e897c49cc4f72d9095925301 (diff)
NVMe: Fix hot cpu notification dead lock
There is a potential dead lock if a cpu event occurs during nvme probe since it registered with hot cpu notification. This fixes the race by having the module register with notification outside of probe rather than have each device register. The actual work is done in a scheduled work queue instead of in the notifier since assigning IO queues has the potential to block if the driver creates additional queues. Signed-off-by: Keith Busch <keith.busch@intel.com> Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
Diffstat (limited to 'drivers/block/nvme-core.c')
-rw-r--r--drivers/block/nvme-core.c35
1 files changed, 25 insertions, 10 deletions
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c
index 2af079e571fc..e0ac1210fe31 100644
--- a/drivers/block/nvme-core.c
+++ b/drivers/block/nvme-core.c
@@ -73,6 +73,7 @@ static LIST_HEAD(dev_list);
73static struct task_struct *nvme_thread; 73static struct task_struct *nvme_thread;
74static struct workqueue_struct *nvme_workq; 74static struct workqueue_struct *nvme_workq;
75static wait_queue_head_t nvme_kthread_wait; 75static wait_queue_head_t nvme_kthread_wait;
76static struct notifier_block nvme_nb;
76 77
77static void nvme_reset_failed_dev(struct work_struct *ws); 78static void nvme_reset_failed_dev(struct work_struct *ws);
78 79
@@ -2115,14 +2116,25 @@ static size_t db_bar_size(struct nvme_dev *dev, unsigned nr_io_queues)
2115 return 4096 + ((nr_io_queues + 1) * 8 * dev->db_stride); 2116 return 4096 + ((nr_io_queues + 1) * 8 * dev->db_stride);
2116} 2117}
2117 2118
2119static void nvme_cpu_workfn(struct work_struct *work)
2120{
2121 struct nvme_dev *dev = container_of(work, struct nvme_dev, cpu_work);
2122 if (dev->initialized)
2123 nvme_assign_io_queues(dev);
2124}
2125
2118static int nvme_cpu_notify(struct notifier_block *self, 2126static int nvme_cpu_notify(struct notifier_block *self,
2119 unsigned long action, void *hcpu) 2127 unsigned long action, void *hcpu)
2120{ 2128{
2121 struct nvme_dev *dev = container_of(self, struct nvme_dev, nb); 2129 struct nvme_dev *dev;
2130
2122 switch (action) { 2131 switch (action) {
2123 case CPU_ONLINE: 2132 case CPU_ONLINE:
2124 case CPU_DEAD: 2133 case CPU_DEAD:
2125 nvme_assign_io_queues(dev); 2134 spin_lock(&dev_list_lock);
2135 list_for_each_entry(dev, &dev_list, node)
2136 schedule_work(&dev->cpu_work);
2137 spin_unlock(&dev_list_lock);
2126 break; 2138 break;
2127 } 2139 }
2128 return NOTIFY_OK; 2140 return NOTIFY_OK;
@@ -2191,11 +2203,6 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
2191 nvme_free_queues(dev, nr_io_queues + 1); 2203 nvme_free_queues(dev, nr_io_queues + 1);
2192 nvme_assign_io_queues(dev); 2204 nvme_assign_io_queues(dev);
2193 2205
2194 dev->nb.notifier_call = &nvme_cpu_notify;
2195 result = register_hotcpu_notifier(&dev->nb);
2196 if (result)
2197 goto free_queues;
2198
2199 return 0; 2206 return 0;
2200 2207
2201 free_queues: 2208 free_queues:
@@ -2495,8 +2502,6 @@ static void nvme_dev_shutdown(struct nvme_dev *dev)
2495 int i; 2502 int i;
2496 2503
2497 dev->initialized = 0; 2504 dev->initialized = 0;
2498 unregister_hotcpu_notifier(&dev->nb);
2499
2500 nvme_dev_list_remove(dev); 2505 nvme_dev_list_remove(dev);
2501 2506
2502 if (!dev->bar || (dev->bar && readl(&dev->bar->csts) == -1)) { 2507 if (!dev->bar || (dev->bar && readl(&dev->bar->csts) == -1)) {
@@ -2767,6 +2772,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2767 INIT_LIST_HEAD(&dev->namespaces); 2772 INIT_LIST_HEAD(&dev->namespaces);
2768 dev->reset_workfn = nvme_reset_failed_dev; 2773 dev->reset_workfn = nvme_reset_failed_dev;
2769 INIT_WORK(&dev->reset_work, nvme_reset_workfn); 2774 INIT_WORK(&dev->reset_work, nvme_reset_workfn);
2775 INIT_WORK(&dev->cpu_work, nvme_cpu_workfn);
2770 dev->pci_dev = pdev; 2776 dev->pci_dev = pdev;
2771 pci_set_drvdata(pdev, dev); 2777 pci_set_drvdata(pdev, dev);
2772 result = nvme_set_instance(dev); 2778 result = nvme_set_instance(dev);
@@ -2836,6 +2842,7 @@ static void nvme_remove(struct pci_dev *pdev)
2836 2842
2837 pci_set_drvdata(pdev, NULL); 2843 pci_set_drvdata(pdev, NULL);
2838 flush_work(&dev->reset_work); 2844 flush_work(&dev->reset_work);
2845 flush_work(&dev->cpu_work);
2839 misc_deregister(&dev->miscdev); 2846 misc_deregister(&dev->miscdev);
2840 nvme_dev_remove(dev); 2847 nvme_dev_remove(dev);
2841 nvme_dev_shutdown(dev); 2848 nvme_dev_shutdown(dev);
@@ -2923,11 +2930,18 @@ static int __init nvme_init(void)
2923 else if (result > 0) 2930 else if (result > 0)
2924 nvme_major = result; 2931 nvme_major = result;
2925 2932
2926 result = pci_register_driver(&nvme_driver); 2933 nvme_nb.notifier_call = &nvme_cpu_notify;
2934 result = register_hotcpu_notifier(&nvme_nb);
2927 if (result) 2935 if (result)
2928 goto unregister_blkdev; 2936 goto unregister_blkdev;
2937
2938 result = pci_register_driver(&nvme_driver);
2939 if (result)
2940 goto unregister_hotcpu;
2929 return 0; 2941 return 0;
2930 2942
2943 unregister_hotcpu:
2944 unregister_hotcpu_notifier(&nvme_nb);
2931 unregister_blkdev: 2945 unregister_blkdev:
2932 unregister_blkdev(nvme_major, "nvme"); 2946 unregister_blkdev(nvme_major, "nvme");
2933 kill_workq: 2947 kill_workq:
@@ -2938,6 +2952,7 @@ static int __init nvme_init(void)
2938static void __exit nvme_exit(void) 2952static void __exit nvme_exit(void)
2939{ 2953{
2940 pci_unregister_driver(&nvme_driver); 2954 pci_unregister_driver(&nvme_driver);
2955 unregister_hotcpu_notifier(&nvme_nb);
2941 unregister_blkdev(nvme_major, "nvme"); 2956 unregister_blkdev(nvme_major, "nvme");
2942 destroy_workqueue(nvme_workq); 2957 destroy_workqueue(nvme_workq);
2943 BUG_ON(nvme_thread && !IS_ERR(nvme_thread)); 2958 BUG_ON(nvme_thread && !IS_ERR(nvme_thread));