1 // SPDX-License-Identifier: GPL-2.0
3 * This file implements the error recovery as a core part of PCIe error
4 * reporting. When a PCIe error is delivered, an error message will be
5 * collected and printed to console, then, an error recovery procedure
6 * will be executed by following the PCI error recovery rules.
8 * Copyright (C) 2006 Intel Corp.
9 * Tom Long Nguyen (tom.l.nguyen@intel.com)
10 * Zhang Yanmin (yanmin.zhang@intel.com)
13 #include <linux/pci.h>
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/errno.h>
17 #include <linux/aer.h>
21 static pci_ers_result_t merge_result(enum pci_ers_result orig,
22 enum pci_ers_result new)
24 if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
25 return PCI_ERS_RESULT_NO_AER_DRIVER;
27 if (new == PCI_ERS_RESULT_NONE)
31 case PCI_ERS_RESULT_CAN_RECOVER:
32 case PCI_ERS_RESULT_RECOVERED:
35 case PCI_ERS_RESULT_DISCONNECT:
36 if (new == PCI_ERS_RESULT_NEED_RESET)
37 orig = PCI_ERS_RESULT_NEED_RESET;
46 static int report_error_detected(struct pci_dev *dev,
47 enum pci_channel_state state,
48 enum pci_ers_result *result)
50 pci_ers_result_t vote;
51 const struct pci_error_handlers *err_handler;
53 device_lock(&dev->dev);
54 if (!pci_dev_set_io_state(dev, state) ||
56 !dev->driver->err_handler ||
57 !dev->driver->err_handler->error_detected) {
59 * If any device in the subtree does not have an error_detected
60 * callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent
61 * error callbacks of "any" device in the subtree, and will
62 * exit in the disconnected error state.
64 if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
65 vote = PCI_ERS_RESULT_NO_AER_DRIVER;
67 vote = PCI_ERS_RESULT_NONE;
69 err_handler = dev->driver->err_handler;
70 vote = err_handler->error_detected(dev, state);
72 pci_uevent_ers(dev, vote);
73 *result = merge_result(*result, vote);
74 device_unlock(&dev->dev);
78 static int report_frozen_detected(struct pci_dev *dev, void *data)
80 return report_error_detected(dev, pci_channel_io_frozen, data);
83 static int report_normal_detected(struct pci_dev *dev, void *data)
85 return report_error_detected(dev, pci_channel_io_normal, data);
88 static int report_mmio_enabled(struct pci_dev *dev, void *data)
90 pci_ers_result_t vote, *result = data;
91 const struct pci_error_handlers *err_handler;
93 device_lock(&dev->dev);
95 !dev->driver->err_handler ||
96 !dev->driver->err_handler->mmio_enabled)
99 err_handler = dev->driver->err_handler;
100 vote = err_handler->mmio_enabled(dev);
101 *result = merge_result(*result, vote);
103 device_unlock(&dev->dev);
107 static int report_slot_reset(struct pci_dev *dev, void *data)
109 pci_ers_result_t vote, *result = data;
110 const struct pci_error_handlers *err_handler;
112 device_lock(&dev->dev);
114 !dev->driver->err_handler ||
115 !dev->driver->err_handler->slot_reset)
118 err_handler = dev->driver->err_handler;
119 vote = err_handler->slot_reset(dev);
120 *result = merge_result(*result, vote);
122 device_unlock(&dev->dev);
126 static int report_resume(struct pci_dev *dev, void *data)
128 const struct pci_error_handlers *err_handler;
130 device_lock(&dev->dev);
131 if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||
133 !dev->driver->err_handler ||
134 !dev->driver->err_handler->resume)
137 err_handler = dev->driver->err_handler;
138 err_handler->resume(dev);
140 pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
141 device_unlock(&dev->dev);
146 * default_reset_link - default reset function
147 * @dev: pointer to pci_dev data structure
149 * Invoked when performing link reset on a Downstream Port or a
150 * Root Port with no aer driver.
152 static pci_ers_result_t default_reset_link(struct pci_dev *dev)
156 rc = pci_bus_error_reset(dev);
157 pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
158 return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
161 static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
163 pci_ers_result_t status;
164 struct pcie_port_service_driver *driver = NULL;
166 driver = pcie_port_find_service(dev, service);
167 if (driver && driver->reset_link) {
168 status = driver->reset_link(dev);
169 } else if (pcie_downstream_port(dev)) {
170 status = default_reset_link(dev);
172 pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
174 return PCI_ERS_RESULT_DISCONNECT;
177 if (status != PCI_ERS_RESULT_RECOVERED) {
178 pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
180 return PCI_ERS_RESULT_DISCONNECT;
186 void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
189 pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
193 * Error recovery runs on all subordinates of the first downstream port.
194 * If the downstream port detected the error, it is cleared at the end.
196 if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
197 pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
198 dev = dev->bus->self;
199 bus = dev->subordinate;
201 pci_dbg(dev, "broadcast error_detected message\n");
202 if (state == pci_channel_io_frozen)
203 pci_walk_bus(bus, report_frozen_detected, &status);
205 pci_walk_bus(bus, report_normal_detected, &status);
207 if (state == pci_channel_io_frozen &&
208 reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED)
211 if (status == PCI_ERS_RESULT_CAN_RECOVER) {
212 status = PCI_ERS_RESULT_RECOVERED;
213 pci_dbg(dev, "broadcast mmio_enabled message\n");
214 pci_walk_bus(bus, report_mmio_enabled, &status);
217 if (status == PCI_ERS_RESULT_NEED_RESET) {
219 * TODO: Should call platform-specific
220 * functions to reset slot before calling
221 * drivers' slot_reset callbacks?
223 status = PCI_ERS_RESULT_RECOVERED;
224 pci_dbg(dev, "broadcast slot_reset message\n");
225 pci_walk_bus(bus, report_slot_reset, &status);
228 if (status != PCI_ERS_RESULT_RECOVERED)
231 pci_dbg(dev, "broadcast resume message\n");
232 pci_walk_bus(bus, report_resume, &status);
234 pci_aer_clear_device_status(dev);
235 pci_cleanup_aer_uncorrect_error_status(dev);
236 pci_info(dev, "AER: Device recovery successful\n");
240 pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
242 /* TODO: Should kernel panic here? */
243 pci_info(dev, "AER: Device recovery failed\n");