aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeith Busch <keith.busch@intel.com>2013-12-10 15:10:36 -0500
committerMatthew Wilcox <matthew.r.wilcox@intel.com>2013-12-16 15:54:39 -0500
commit9a6b94584de1a0467d85b435df9c744c5c45a270 (patch)
treeeab218e65ce9e224b48b4d721819f871d909efe1
parent68608c268bf265b039daeaafee6c902dfef32024 (diff)
NVMe: Device resume error handling
Adds controller error handling on resume power management. If the device fails to initialize, the device is queued for a reset. If the reset fails, a thread is spawned to remove the pci device. If the device resumes as "busy", the device is responding to admin commands but will not create IO queues. In this case, we need to remove the gendisks and free the IO queues since they can't be used and may be holding bios in their lists. From testing, the dma pools require a pci device so this had to change the pci driver 'remove' to release the dma resources in line with that call instead of after all references to the device are released. Signed-off-by: Keith Busch <keith.busch@intel.com> Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
-rw-r--r--drivers/block/nvme-core.c108
-rw-r--r--include/linux/nvme.h1
2 files changed, 94 insertions, 15 deletions
diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c
index 300766973d76..000bca43c23b 100644
--- a/drivers/block/nvme-core.c
+++ b/drivers/block/nvme-core.c
@@ -58,6 +58,7 @@ module_param(use_threaded_interrupts, int, 0);
58static DEFINE_SPINLOCK(dev_list_lock); 58static DEFINE_SPINLOCK(dev_list_lock);
59static LIST_HEAD(dev_list); 59static LIST_HEAD(dev_list);
60static struct task_struct *nvme_thread; 60static struct task_struct *nvme_thread;
61static struct workqueue_struct *nvme_workq;
61 62
62/* 63/*
63 * An NVM Express queue. Each device has at least two (one for admin 64 * An NVM Express queue. Each device has at least two (one for admin
@@ -1968,7 +1969,6 @@ static int nvme_dev_map(struct nvme_dev *dev)
1968 dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) 1969 dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
1969 goto disable; 1970 goto disable;
1970 1971
1971 pci_set_drvdata(pdev, dev);
1972 dev->bar = ioremap(pci_resource_start(pdev, 0), 8192); 1972 dev->bar = ioremap(pci_resource_start(pdev, 0), 8192);
1973 if (!dev->bar) 1973 if (!dev->bar)
1974 goto disable; 1974 goto disable;
@@ -1995,9 +1995,9 @@ static void nvme_dev_unmap(struct nvme_dev *dev)
1995 if (dev->bar) { 1995 if (dev->bar) {
1996 iounmap(dev->bar); 1996 iounmap(dev->bar);
1997 dev->bar = NULL; 1997 dev->bar = NULL;
1998 pci_release_regions(dev->pci_dev);
1998 } 1999 }
1999 2000
2000 pci_release_regions(dev->pci_dev);
2001 if (pci_is_enabled(dev->pci_dev)) 2001 if (pci_is_enabled(dev->pci_dev))
2002 pci_disable_device(dev->pci_dev); 2002 pci_disable_device(dev->pci_dev);
2003} 2003}
@@ -2085,11 +2085,6 @@ static void nvme_release_instance(struct nvme_dev *dev)
2085static void nvme_free_dev(struct kref *kref) 2085static void nvme_free_dev(struct kref *kref)
2086{ 2086{
2087 struct nvme_dev *dev = container_of(kref, struct nvme_dev, kref); 2087 struct nvme_dev *dev = container_of(kref, struct nvme_dev, kref);
2088 nvme_dev_remove(dev);
2089 nvme_dev_shutdown(dev);
2090 nvme_free_queues(dev);
2091 nvme_release_instance(dev);
2092 nvme_release_prp_pools(dev);
2093 kfree(dev->queues); 2088 kfree(dev->queues);
2094 kfree(dev->entry); 2089 kfree(dev->entry);
2095 kfree(dev); 2090 kfree(dev);
@@ -2161,6 +2156,70 @@ static int nvme_dev_start(struct nvme_dev *dev)
2161 return result; 2156 return result;
2162} 2157}
2163 2158
2159static int nvme_remove_dead_ctrl(void *arg)
2160{
2161 struct nvme_dev *dev = (struct nvme_dev *)arg;
2162 struct pci_dev *pdev = dev->pci_dev;
2163
2164 if (pci_get_drvdata(pdev))
2165 pci_stop_and_remove_bus_device(pdev);
2166 kref_put(&dev->kref, nvme_free_dev);
2167 return 0;
2168}
2169
2170static void nvme_remove_disks(struct work_struct *ws)
2171{
2172 int i;
2173 struct nvme_dev *dev = container_of(ws, struct nvme_dev, reset_work);
2174
2175 nvme_dev_remove(dev);
2176 spin_lock(&dev_list_lock);
2177 for (i = dev->queue_count - 1; i > 0; i--) {
2178 BUG_ON(!dev->queues[i] || !dev->queues[i]->q_suspended);
2179 nvme_free_queue(dev->queues[i]);
2180 dev->queue_count--;
2181 dev->queues[i] = NULL;
2182 }
2183 spin_unlock(&dev_list_lock);
2184}
2185
2186static int nvme_dev_resume(struct nvme_dev *dev)
2187{
2188 int ret;
2189
2190 ret = nvme_dev_start(dev);
2191 if (ret && ret != -EBUSY)
2192 return ret;
2193 if (ret == -EBUSY) {
2194 spin_lock(&dev_list_lock);
2195 INIT_WORK(&dev->reset_work, nvme_remove_disks);
2196 queue_work(nvme_workq, &dev->reset_work);
2197 spin_unlock(&dev_list_lock);
2198 }
2199 return 0;
2200}
2201
2202static void nvme_dev_reset(struct nvme_dev *dev)
2203{
2204 nvme_dev_shutdown(dev);
2205 if (nvme_dev_resume(dev)) {
2206 dev_err(&dev->pci_dev->dev, "Device failed to resume\n");
2207 kref_get(&dev->kref);
2208 if (IS_ERR(kthread_run(nvme_remove_dead_ctrl, dev, "nvme%d",
2209 dev->instance))) {
2210 dev_err(&dev->pci_dev->dev,
2211 "Failed to start controller remove task\n");
2212 kref_put(&dev->kref, nvme_free_dev);
2213 }
2214 }
2215}
2216
2217static void nvme_reset_failed_dev(struct work_struct *ws)
2218{
2219 struct nvme_dev *dev = container_of(ws, struct nvme_dev, reset_work);
2220 nvme_dev_reset(dev);
2221}
2222
2164static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) 2223static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2165{ 2224{
2166 int result = -ENOMEM; 2225 int result = -ENOMEM;
@@ -2180,7 +2239,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2180 2239
2181 INIT_LIST_HEAD(&dev->namespaces); 2240 INIT_LIST_HEAD(&dev->namespaces);
2182 dev->pci_dev = pdev; 2241 dev->pci_dev = pdev;
2183 2242 pci_set_drvdata(pdev, dev);
2184 result = nvme_set_instance(dev); 2243 result = nvme_set_instance(dev);
2185 if (result) 2244 if (result)
2186 goto free; 2245 goto free;
@@ -2232,7 +2291,19 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
2232static void nvme_remove(struct pci_dev *pdev) 2291static void nvme_remove(struct pci_dev *pdev)
2233{ 2292{
2234 struct nvme_dev *dev = pci_get_drvdata(pdev); 2293 struct nvme_dev *dev = pci_get_drvdata(pdev);
2294
2295 spin_lock(&dev_list_lock);
2296 list_del_init(&dev->node);
2297 spin_unlock(&dev_list_lock);
2298
2299 pci_set_drvdata(pdev, NULL);
2300 flush_work(&dev->reset_work);
2235 misc_deregister(&dev->miscdev); 2301 misc_deregister(&dev->miscdev);
2302 nvme_dev_remove(dev);
2303 nvme_dev_shutdown(dev);
2304 nvme_free_queues(dev);
2305 nvme_release_instance(dev);
2306 nvme_release_prp_pools(dev);
2236 kref_put(&dev->kref, nvme_free_dev); 2307 kref_put(&dev->kref, nvme_free_dev);
2237} 2308}
2238 2309
@@ -2256,13 +2327,12 @@ static int nvme_resume(struct device *dev)
2256{ 2327{
2257 struct pci_dev *pdev = to_pci_dev(dev); 2328 struct pci_dev *pdev = to_pci_dev(dev);
2258 struct nvme_dev *ndev = pci_get_drvdata(pdev); 2329 struct nvme_dev *ndev = pci_get_drvdata(pdev);
2259 int ret;
2260 2330
2261 ret = nvme_dev_start(ndev); 2331 if (nvme_dev_resume(ndev) && !work_busy(&ndev->reset_work)) {
2262 /* XXX: should remove gendisks if resume fails */ 2332 INIT_WORK(&ndev->reset_work, nvme_reset_failed_dev);
2263 if (ret) 2333 queue_work(nvme_workq, &ndev->reset_work);
2264 nvme_free_queues(ndev); 2334 }
2265 return ret; 2335 return 0;
2266} 2336}
2267 2337
2268static SIMPLE_DEV_PM_OPS(nvme_dev_pm_ops, nvme_suspend, nvme_resume); 2338static SIMPLE_DEV_PM_OPS(nvme_dev_pm_ops, nvme_suspend, nvme_resume);
@@ -2303,9 +2373,14 @@ static int __init nvme_init(void)
2303 if (IS_ERR(nvme_thread)) 2373 if (IS_ERR(nvme_thread))
2304 return PTR_ERR(nvme_thread); 2374 return PTR_ERR(nvme_thread);
2305 2375
2376 result = -ENOMEM;
2377 nvme_workq = create_singlethread_workqueue("nvme");
2378 if (!nvme_workq)
2379 goto kill_kthread;
2380
2306 result = register_blkdev(nvme_major, "nvme"); 2381 result = register_blkdev(nvme_major, "nvme");
2307 if (result < 0) 2382 if (result < 0)
2308 goto kill_kthread; 2383 goto kill_workq;
2309 else if (result > 0) 2384 else if (result > 0)
2310 nvme_major = result; 2385 nvme_major = result;
2311 2386
@@ -2316,6 +2391,8 @@ static int __init nvme_init(void)
2316 2391
2317 unregister_blkdev: 2392 unregister_blkdev:
2318 unregister_blkdev(nvme_major, "nvme"); 2393 unregister_blkdev(nvme_major, "nvme");
2394 kill_workq:
2395 destroy_workqueue(nvme_workq);
2319 kill_kthread: 2396 kill_kthread:
2320 kthread_stop(nvme_thread); 2397 kthread_stop(nvme_thread);
2321 return result; 2398 return result;
@@ -2325,6 +2402,7 @@ static void __exit nvme_exit(void)
2325{ 2402{
2326 pci_unregister_driver(&nvme_driver); 2403 pci_unregister_driver(&nvme_driver);
2327 unregister_blkdev(nvme_major, "nvme"); 2404 unregister_blkdev(nvme_major, "nvme");
2405 destroy_workqueue(nvme_workq);
2328 kthread_stop(nvme_thread); 2406 kthread_stop(nvme_thread);
2329} 2407}
2330 2408
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 5bc29197d90e..eed81cc56d7e 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -87,6 +87,7 @@ struct nvme_dev {
87 struct list_head namespaces; 87 struct list_head namespaces;
88 struct kref kref; 88 struct kref kref;
89 struct miscdevice miscdev; 89 struct miscdevice miscdev;
90 struct work_struct reset_work;
90 char name[12]; 91 char name[12];
91 char serial[20]; 92 char serial[20];
92 char model[40]; 93 char model[40];