Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
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.
7 : : *
8 : : * Copyright (C) 2006 Intel Corp.
9 : : * Tom Long Nguyen (tom.l.nguyen@intel.com)
10 : : * Zhang Yanmin (yanmin.zhang@intel.com)
11 : : */
12 : :
13 : : #define dev_fmt(fmt) "AER: " fmt
14 : :
15 : : #include <linux/pci.h>
16 : : #include <linux/module.h>
17 : : #include <linux/kernel.h>
18 : : #include <linux/errno.h>
19 : : #include <linux/aer.h>
20 : : #include "portdrv.h"
21 : : #include "../pci.h"
22 : :
23 : 0 : static pci_ers_result_t merge_result(enum pci_ers_result orig,
24 : : enum pci_ers_result new)
25 : : {
26 : 0 : if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
27 : : return PCI_ERS_RESULT_NO_AER_DRIVER;
28 : :
29 [ # # # # : 0 : if (new == PCI_ERS_RESULT_NONE)
# # ]
30 : : return orig;
31 : :
32 [ # # # # : 0 : switch (orig) {
# # # #
# ]
33 : 0 : case PCI_ERS_RESULT_CAN_RECOVER:
34 : : case PCI_ERS_RESULT_RECOVERED:
35 : 0 : orig = new;
36 : 0 : break;
37 : 0 : case PCI_ERS_RESULT_DISCONNECT:
38 [ # # # # : 0 : if (new == PCI_ERS_RESULT_NEED_RESET)
# # ]
39 : 0 : orig = PCI_ERS_RESULT_NEED_RESET;
40 : : break;
41 : : default:
42 : : break;
43 : : }
44 : :
45 : : return orig;
46 : : }
47 : :
48 : 0 : static int report_error_detected(struct pci_dev *dev,
49 : : enum pci_channel_state state,
50 : : enum pci_ers_result *result)
51 : : {
52 : 0 : pci_ers_result_t vote;
53 : 0 : const struct pci_error_handlers *err_handler;
54 : :
55 : 0 : device_lock(&dev->dev);
56 [ # # ]: 0 : if (!pci_dev_set_io_state(dev, state) ||
57 [ # # ]: 0 : !dev->driver ||
58 [ # # ]: 0 : !dev->driver->err_handler ||
59 [ # # ]: 0 : !dev->driver->err_handler->error_detected) {
60 : : /*
61 : : * If any device in the subtree does not have an error_detected
62 : : * callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent
63 : : * error callbacks of "any" device in the subtree, and will
64 : : * exit in the disconnected error state.
65 : : */
66 [ # # ]: 0 : if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
67 : 0 : vote = PCI_ERS_RESULT_NO_AER_DRIVER;
68 : 0 : pci_info(dev, "can't recover (no error_detected callback)\n");
69 : : } else {
70 : : vote = PCI_ERS_RESULT_NONE;
71 : : }
72 : : } else {
73 : 0 : err_handler = dev->driver->err_handler;
74 : 0 : vote = err_handler->error_detected(dev, state);
75 : : }
76 : 0 : pci_uevent_ers(dev, vote);
77 [ # # ]: 0 : *result = merge_result(*result, vote);
78 : 0 : device_unlock(&dev->dev);
79 : 0 : return 0;
80 : : }
81 : :
82 : 0 : static int report_frozen_detected(struct pci_dev *dev, void *data)
83 : : {
84 : 0 : return report_error_detected(dev, pci_channel_io_frozen, data);
85 : : }
86 : :
87 : 0 : static int report_normal_detected(struct pci_dev *dev, void *data)
88 : : {
89 : 0 : return report_error_detected(dev, pci_channel_io_normal, data);
90 : : }
91 : :
92 : 0 : static int report_mmio_enabled(struct pci_dev *dev, void *data)
93 : : {
94 : 0 : pci_ers_result_t vote, *result = data;
95 : 0 : const struct pci_error_handlers *err_handler;
96 : :
97 : 0 : device_lock(&dev->dev);
98 [ # # ]: 0 : if (!dev->driver ||
99 [ # # ]: 0 : !dev->driver->err_handler ||
100 [ # # ]: 0 : !dev->driver->err_handler->mmio_enabled)
101 : 0 : goto out;
102 : :
103 : 0 : err_handler = dev->driver->err_handler;
104 : 0 : vote = err_handler->mmio_enabled(dev);
105 [ # # ]: 0 : *result = merge_result(*result, vote);
106 : 0 : out:
107 : 0 : device_unlock(&dev->dev);
108 : 0 : return 0;
109 : : }
110 : :
111 : 0 : static int report_slot_reset(struct pci_dev *dev, void *data)
112 : : {
113 : 0 : pci_ers_result_t vote, *result = data;
114 : 0 : const struct pci_error_handlers *err_handler;
115 : :
116 : 0 : device_lock(&dev->dev);
117 [ # # ]: 0 : if (!dev->driver ||
118 [ # # ]: 0 : !dev->driver->err_handler ||
119 [ # # ]: 0 : !dev->driver->err_handler->slot_reset)
120 : 0 : goto out;
121 : :
122 : 0 : err_handler = dev->driver->err_handler;
123 : 0 : vote = err_handler->slot_reset(dev);
124 [ # # ]: 0 : *result = merge_result(*result, vote);
125 : 0 : out:
126 : 0 : device_unlock(&dev->dev);
127 : 0 : return 0;
128 : : }
129 : :
130 : 0 : static int report_resume(struct pci_dev *dev, void *data)
131 : : {
132 : 0 : const struct pci_error_handlers *err_handler;
133 : :
134 : 0 : device_lock(&dev->dev);
135 [ # # ]: 0 : if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||
136 [ # # ]: 0 : !dev->driver ||
137 [ # # ]: 0 : !dev->driver->err_handler ||
138 [ # # ]: 0 : !dev->driver->err_handler->resume)
139 : 0 : goto out;
140 : :
141 : 0 : err_handler = dev->driver->err_handler;
142 : 0 : err_handler->resume(dev);
143 : 0 : out:
144 : 0 : pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
145 : 0 : device_unlock(&dev->dev);
146 : 0 : return 0;
147 : : }
148 : :
149 : : /**
150 : : * default_reset_link - default reset function
151 : : * @dev: pointer to pci_dev data structure
152 : : *
153 : : * Invoked when performing link reset on a Downstream Port or a
154 : : * Root Port with no aer driver.
155 : : */
156 : 0 : static pci_ers_result_t default_reset_link(struct pci_dev *dev)
157 : : {
158 : 0 : int rc;
159 : :
160 : 0 : rc = pci_bus_error_reset(dev);
161 : 0 : pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
162 [ # # ]: 0 : return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
163 : : }
164 : :
165 : 0 : static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
166 : : {
167 : 0 : pci_ers_result_t status;
168 : 0 : struct pcie_port_service_driver *driver = NULL;
169 : :
170 : 0 : driver = pcie_port_find_service(dev, service);
171 [ # # # # ]: 0 : if (driver && driver->reset_link) {
172 : 0 : status = driver->reset_link(dev);
173 [ # # # # ]: 0 : } else if (pcie_downstream_port(dev)) {
174 : 0 : status = default_reset_link(dev);
175 : : } else {
176 [ # # ]: 0 : pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
177 : : pci_name(dev));
178 : 0 : return PCI_ERS_RESULT_DISCONNECT;
179 : : }
180 : :
181 [ # # ]: 0 : if (status != PCI_ERS_RESULT_RECOVERED) {
182 [ # # ]: 0 : pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
183 : : pci_name(dev));
184 : 0 : return PCI_ERS_RESULT_DISCONNECT;
185 : : }
186 : :
187 : : return status;
188 : : }
189 : :
190 : 0 : void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
191 : : u32 service)
192 : : {
193 : 0 : pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
194 : 0 : struct pci_bus *bus;
195 : :
196 : : /*
197 : : * Error recovery runs on all subordinates of the first downstream port.
198 : : * If the downstream port detected the error, it is cleared at the end.
199 : : */
200 [ # # # # ]: 0 : if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
201 : : pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
202 : 0 : dev = dev->bus->self;
203 : 0 : bus = dev->subordinate;
204 : :
205 : 0 : pci_dbg(dev, "broadcast error_detected message\n");
206 [ # # ]: 0 : if (state == pci_channel_io_frozen)
207 : 0 : pci_walk_bus(bus, report_frozen_detected, &status);
208 : : else
209 : 0 : pci_walk_bus(bus, report_normal_detected, &status);
210 : :
211 [ # # # # ]: 0 : if (state == pci_channel_io_frozen &&
212 : 0 : reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED)
213 : 0 : goto failed;
214 : :
215 [ # # ]: 0 : if (status == PCI_ERS_RESULT_CAN_RECOVER) {
216 : 0 : status = PCI_ERS_RESULT_RECOVERED;
217 : 0 : pci_dbg(dev, "broadcast mmio_enabled message\n");
218 : 0 : pci_walk_bus(bus, report_mmio_enabled, &status);
219 : : }
220 : :
221 [ # # ]: 0 : if (status == PCI_ERS_RESULT_NEED_RESET) {
222 : : /*
223 : : * TODO: Should call platform-specific
224 : : * functions to reset slot before calling
225 : : * drivers' slot_reset callbacks?
226 : : */
227 : 0 : status = PCI_ERS_RESULT_RECOVERED;
228 : 0 : pci_dbg(dev, "broadcast slot_reset message\n");
229 : 0 : pci_walk_bus(bus, report_slot_reset, &status);
230 : : }
231 : :
232 [ # # ]: 0 : if (status != PCI_ERS_RESULT_RECOVERED)
233 : 0 : goto failed;
234 : :
235 : 0 : pci_dbg(dev, "broadcast resume message\n");
236 : 0 : pci_walk_bus(bus, report_resume, &status);
237 : :
238 : 0 : pci_aer_clear_device_status(dev);
239 : 0 : pci_cleanup_aer_uncorrect_error_status(dev);
240 : 0 : pci_info(dev, "device recovery successful\n");
241 : 0 : return;
242 : :
243 : 0 : failed:
244 : 0 : pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
245 : :
246 : : /* TODO: Should kernel panic here? */
247 : 0 : pci_info(dev, "device recovery failed\n");
248 : : }
|