blob: 4da2a62b4f77821ab696cddbc6fdc5058f0d9496 [file] [log] [blame]
Oza Pawandeep2e28bc82018-05-17 16:44:15 -05001// 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#include <linux/pci.h>
14#include <linux/module.h>
15#include <linux/pci.h>
16#include <linux/kernel.h>
17#include <linux/errno.h>
18#include <linux/aer.h>
19#include "portdrv.h"
20#include "../pci.h"
21
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050022static pci_ers_result_t merge_result(enum pci_ers_result orig,
23 enum pci_ers_result new)
24{
25 if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
26 return PCI_ERS_RESULT_NO_AER_DRIVER;
27
28 if (new == PCI_ERS_RESULT_NONE)
29 return orig;
30
31 switch (orig) {
32 case PCI_ERS_RESULT_CAN_RECOVER:
33 case PCI_ERS_RESULT_RECOVERED:
34 orig = new;
35 break;
36 case PCI_ERS_RESULT_DISCONNECT:
37 if (new == PCI_ERS_RESULT_NEED_RESET)
38 orig = PCI_ERS_RESULT_NEED_RESET;
39 break;
40 default:
41 break;
42 }
43
44 return orig;
45}
46
Keith Busch542aeb92018-09-20 10:27:14 -060047static int report_error_detected(struct pci_dev *dev,
48 enum pci_channel_state state,
49 enum pci_ers_result *result)
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050050{
51 pci_ers_result_t vote;
52 const struct pci_error_handlers *err_handler;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050053
54 device_lock(&dev->dev);
Keith Buscha6bd1012018-09-20 10:27:16 -060055 if (!pci_dev_set_io_state(dev, state) ||
56 !dev->driver ||
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050057 !dev->driver->err_handler ||
58 !dev->driver->err_handler->error_detected) {
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050059 /*
Keith Buschbfcb79fc2018-09-20 10:27:13 -060060 * If any device in the subtree does not have an error_detected
61 * callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent
62 * error callbacks of "any" device in the subtree, and will
63 * exit in the disconnected error state.
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050064 */
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050065 if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
66 vote = PCI_ERS_RESULT_NO_AER_DRIVER;
67 else
68 vote = PCI_ERS_RESULT_NONE;
69 } else {
70 err_handler = dev->driver->err_handler;
Keith Busch542aeb92018-09-20 10:27:14 -060071 vote = err_handler->error_detected(dev, state);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050072 }
Keith Busch7b42d972018-09-20 10:27:15 -060073 pci_uevent_ers(dev, vote);
Keith Busch542aeb92018-09-20 10:27:14 -060074 *result = merge_result(*result, vote);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050075 device_unlock(&dev->dev);
76 return 0;
77}
78
Keith Busch542aeb92018-09-20 10:27:14 -060079static int report_frozen_detected(struct pci_dev *dev, void *data)
80{
81 return report_error_detected(dev, pci_channel_io_frozen, data);
82}
83
84static int report_normal_detected(struct pci_dev *dev, void *data)
85{
86 return report_error_detected(dev, pci_channel_io_normal, data);
87}
88
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050089static int report_mmio_enabled(struct pci_dev *dev, void *data)
90{
Keith Busch542aeb92018-09-20 10:27:14 -060091 pci_ers_result_t vote, *result = data;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050092 const struct pci_error_handlers *err_handler;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050093
94 device_lock(&dev->dev);
95 if (!dev->driver ||
96 !dev->driver->err_handler ||
97 !dev->driver->err_handler->mmio_enabled)
98 goto out;
99
100 err_handler = dev->driver->err_handler;
101 vote = err_handler->mmio_enabled(dev);
Keith Busch542aeb92018-09-20 10:27:14 -0600102 *result = merge_result(*result, vote);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500103out:
104 device_unlock(&dev->dev);
105 return 0;
106}
107
108static int report_slot_reset(struct pci_dev *dev, void *data)
109{
Keith Busch542aeb92018-09-20 10:27:14 -0600110 pci_ers_result_t vote, *result = data;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500111 const struct pci_error_handlers *err_handler;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500112
113 device_lock(&dev->dev);
114 if (!dev->driver ||
115 !dev->driver->err_handler ||
116 !dev->driver->err_handler->slot_reset)
117 goto out;
118
119 err_handler = dev->driver->err_handler;
120 vote = err_handler->slot_reset(dev);
Keith Busch542aeb92018-09-20 10:27:14 -0600121 *result = merge_result(*result, vote);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500122out:
123 device_unlock(&dev->dev);
124 return 0;
125}
126
127static int report_resume(struct pci_dev *dev, void *data)
128{
129 const struct pci_error_handlers *err_handler;
130
131 device_lock(&dev->dev);
Keith Buscha6bd1012018-09-20 10:27:16 -0600132 if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||
133 !dev->driver ||
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500134 !dev->driver->err_handler ||
135 !dev->driver->err_handler->resume)
136 goto out;
137
138 err_handler = dev->driver->err_handler;
139 err_handler->resume(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500140out:
Keith Busch7b42d972018-09-20 10:27:15 -0600141 pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500142 device_unlock(&dev->dev);
143 return 0;
144}
145
146/**
147 * default_reset_link - default reset function
148 * @dev: pointer to pci_dev data structure
149 *
150 * Invoked when performing link reset on a Downstream Port or a
151 * Root Port with no aer driver.
152 */
153static pci_ers_result_t default_reset_link(struct pci_dev *dev)
154{
Sinan Kaya18426232018-07-19 18:04:09 -0500155 int rc;
156
Keith Buschc4eed622018-09-20 10:27:11 -0600157 rc = pci_bus_error_reset(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500158 pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
Sinan Kaya18426232018-07-19 18:04:09 -0500159 return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500160}
161
Oza Pawandeep0b914392018-05-17 16:44:19 -0500162static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500163{
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500164 pci_ers_result_t status;
165 struct pcie_port_service_driver *driver = NULL;
166
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600167 driver = pcie_port_find_service(dev, service);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500168 if (driver && driver->reset_link) {
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600169 status = driver->reset_link(dev);
170 } else if (dev->has_secondary_link) {
171 status = default_reset_link(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500172 } else {
173 pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600174 pci_name(dev));
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500175 return PCI_ERS_RESULT_DISCONNECT;
176 }
177
178 if (status != PCI_ERS_RESULT_RECOVERED) {
179 pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600180 pci_name(dev));
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500181 return PCI_ERS_RESULT_DISCONNECT;
182 }
183
184 return status;
185}
186
Keith Buschbdb5ac852018-09-20 10:27:12 -0600187void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
188 u32 service)
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500189{
Keith Busch542aeb92018-09-20 10:27:14 -0600190 pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
191 struct pci_bus *bus;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500192
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600193 /*
194 * Error recovery runs on all subordinates of the first downstream port.
195 * If the downstream port detected the error, it is cleared at the end.
196 */
197 if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
198 pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
199 dev = dev->bus->self;
Keith Busch542aeb92018-09-20 10:27:14 -0600200 bus = dev->subordinate;
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600201
Keith Busch542aeb92018-09-20 10:27:14 -0600202 pci_dbg(dev, "broadcast error_detected message\n");
203 if (state == pci_channel_io_frozen)
204 pci_walk_bus(bus, report_frozen_detected, &status);
205 else
206 pci_walk_bus(bus, report_normal_detected, &status);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500207
Keith Buschbdb5ac852018-09-20 10:27:12 -0600208 if (state == pci_channel_io_frozen &&
209 reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED)
210 goto failed;
211
Keith Busch542aeb92018-09-20 10:27:14 -0600212 if (status == PCI_ERS_RESULT_CAN_RECOVER) {
213 status = PCI_ERS_RESULT_RECOVERED;
214 pci_dbg(dev, "broadcast mmio_enabled message\n");
215 pci_walk_bus(bus, report_mmio_enabled, &status);
216 }
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500217
218 if (status == PCI_ERS_RESULT_NEED_RESET) {
219 /*
220 * TODO: Should call platform-specific
221 * functions to reset slot before calling
222 * drivers' slot_reset callbacks?
223 */
Keith Busch542aeb92018-09-20 10:27:14 -0600224 status = PCI_ERS_RESULT_RECOVERED;
225 pci_dbg(dev, "broadcast slot_reset message\n");
226 pci_walk_bus(bus, report_slot_reset, &status);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500227 }
228
229 if (status != PCI_ERS_RESULT_RECOVERED)
230 goto failed;
231
Keith Busch542aeb92018-09-20 10:27:14 -0600232 pci_dbg(dev, "broadcast resume message\n");
233 pci_walk_bus(bus, report_resume, &status);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500234
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600235 pci_aer_clear_device_status(dev);
236 pci_cleanup_aer_uncorrect_error_status(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500237 pci_info(dev, "AER: Device recovery successful\n");
238 return;
239
240failed:
241 pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
242
243 /* TODO: Should kernel panic here? */
244 pci_info(dev, "AER: Device recovery failed\n");
245}