diff options
author | Keith Busch <keith.busch@intel.com> | 2015-12-07 17:30:31 -0500 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2015-12-22 12:09:06 -0500 |
commit | a0a3408ee614848c27b0d36c2fe490da3b387b8d (patch) | |
tree | 2b045db6bf91c40535e2d49b3b923d779f60834c | |
parent | bbc758ec04c2f30805ce0fcdfbaa4c3445fafbae (diff) |
NVMe: Add pci error handlers
Requests enabling pcie aer support. Shuts down the controller on error
detected with io frozen state prior to requesting slot reset; resumes
controller after reset completes.
Signed-off-by: Keith Busch <keith.busch@intel.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r-- | drivers/nvme/host/pci.c | 54 |
1 files changed, 44 insertions, 10 deletions
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index b88708affad8..b82bbea909cd 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c | |||
@@ -12,6 +12,7 @@ | |||
12 | * more details. | 12 | * more details. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/aer.h> | ||
15 | #include <linux/bitops.h> | 16 | #include <linux/bitops.h> |
16 | #include <linux/blkdev.h> | 17 | #include <linux/blkdev.h> |
17 | #include <linux/blk-mq.h> | 18 | #include <linux/blk-mq.h> |
@@ -1670,6 +1671,8 @@ static int nvme_dev_map(struct nvme_dev *dev) | |||
1670 | if (readl(dev->bar + NVME_REG_VS) >= NVME_VS(1, 2)) | 1671 | if (readl(dev->bar + NVME_REG_VS) >= NVME_VS(1, 2)) |
1671 | dev->cmb = nvme_map_cmb(dev); | 1672 | dev->cmb = nvme_map_cmb(dev); |
1672 | 1673 | ||
1674 | pci_enable_pcie_error_reporting(pdev); | ||
1675 | pci_save_state(pdev); | ||
1673 | return 0; | 1676 | return 0; |
1674 | 1677 | ||
1675 | unmap: | 1678 | unmap: |
@@ -1697,8 +1700,10 @@ static void nvme_dev_unmap(struct nvme_dev *dev) | |||
1697 | pci_release_regions(pdev); | 1700 | pci_release_regions(pdev); |
1698 | } | 1701 | } |
1699 | 1702 | ||
1700 | if (pci_is_enabled(pdev)) | 1703 | if (pci_is_enabled(pdev)) { |
1704 | pci_disable_pcie_error_reporting(pdev); | ||
1701 | pci_disable_device(pdev); | 1705 | pci_disable_device(pdev); |
1706 | } | ||
1702 | } | 1707 | } |
1703 | 1708 | ||
1704 | struct nvme_delq_ctx { | 1709 | struct nvme_delq_ctx { |
@@ -2225,13 +2230,6 @@ static void nvme_remove(struct pci_dev *pdev) | |||
2225 | nvme_put_ctrl(&dev->ctrl); | 2230 | nvme_put_ctrl(&dev->ctrl); |
2226 | } | 2231 | } |
2227 | 2232 | ||
2228 | /* These functions are yet to be implemented */ | ||
2229 | #define nvme_error_detected NULL | ||
2230 | #define nvme_dump_registers NULL | ||
2231 | #define nvme_link_reset NULL | ||
2232 | #define nvme_slot_reset NULL | ||
2233 | #define nvme_error_resume NULL | ||
2234 | |||
2235 | #ifdef CONFIG_PM_SLEEP | 2233 | #ifdef CONFIG_PM_SLEEP |
2236 | static int nvme_suspend(struct device *dev) | 2234 | static int nvme_suspend(struct device *dev) |
2237 | { | 2235 | { |
@@ -2254,10 +2252,46 @@ static int nvme_resume(struct device *dev) | |||
2254 | 2252 | ||
2255 | static SIMPLE_DEV_PM_OPS(nvme_dev_pm_ops, nvme_suspend, nvme_resume); | 2253 | static SIMPLE_DEV_PM_OPS(nvme_dev_pm_ops, nvme_suspend, nvme_resume); |
2256 | 2254 | ||
2255 | static pci_ers_result_t nvme_error_detected(struct pci_dev *pdev, | ||
2256 | pci_channel_state_t state) | ||
2257 | { | ||
2258 | struct nvme_dev *dev = pci_get_drvdata(pdev); | ||
2259 | |||
2260 | /* | ||
2261 | * A frozen channel requires a reset. When detected, this method will | ||
2262 | * shutdown the controller to quiesce. The controller will be restarted | ||
2263 | * after the slot reset through driver's slot_reset callback. | ||
2264 | */ | ||
2265 | dev_warn(&pdev->dev, "error detected: state:%d\n", state); | ||
2266 | switch (state) { | ||
2267 | case pci_channel_io_normal: | ||
2268 | return PCI_ERS_RESULT_CAN_RECOVER; | ||
2269 | case pci_channel_io_frozen: | ||
2270 | nvme_dev_shutdown(dev); | ||
2271 | return PCI_ERS_RESULT_NEED_RESET; | ||
2272 | case pci_channel_io_perm_failure: | ||
2273 | return PCI_ERS_RESULT_DISCONNECT; | ||
2274 | } | ||
2275 | return PCI_ERS_RESULT_NEED_RESET; | ||
2276 | } | ||
2277 | |||
2278 | static pci_ers_result_t nvme_slot_reset(struct pci_dev *pdev) | ||
2279 | { | ||
2280 | struct nvme_dev *dev = pci_get_drvdata(pdev); | ||
2281 | |||
2282 | dev_info(&pdev->dev, "restart after slot reset\n"); | ||
2283 | pci_restore_state(pdev); | ||
2284 | queue_work(nvme_workq, &dev->reset_work); | ||
2285 | return PCI_ERS_RESULT_RECOVERED; | ||
2286 | } | ||
2287 | |||
2288 | static void nvme_error_resume(struct pci_dev *pdev) | ||
2289 | { | ||
2290 | pci_cleanup_aer_uncorrect_error_status(pdev); | ||
2291 | } | ||
2292 | |||
2257 | static const struct pci_error_handlers nvme_err_handler = { | 2293 | static const struct pci_error_handlers nvme_err_handler = { |
2258 | .error_detected = nvme_error_detected, | 2294 | .error_detected = nvme_error_detected, |
2259 | .mmio_enabled = nvme_dump_registers, | ||
2260 | .link_reset = nvme_link_reset, | ||
2261 | .slot_reset = nvme_slot_reset, | 2295 | .slot_reset = nvme_slot_reset, |
2262 | .resume = nvme_error_resume, | 2296 | .resume = nvme_error_resume, |
2263 | .reset_notify = nvme_reset_notify, | 2297 | .reset_notify = nvme_reset_notify, |