blob: 773197a12568e1e9d2ebcb1dd4e707de059ccec5 [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>
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050015#include <linux/kernel.h>
16#include <linux/errno.h>
17#include <linux/aer.h>
18#include "portdrv.h"
19#include "../pci.h"
20
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050021static pci_ers_result_t merge_result(enum pci_ers_result orig,
22 enum pci_ers_result new)
23{
24 if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
25 return PCI_ERS_RESULT_NO_AER_DRIVER;
26
27 if (new == PCI_ERS_RESULT_NONE)
28 return orig;
29
30 switch (orig) {
31 case PCI_ERS_RESULT_CAN_RECOVER:
32 case PCI_ERS_RESULT_RECOVERED:
33 orig = new;
34 break;
35 case PCI_ERS_RESULT_DISCONNECT:
36 if (new == PCI_ERS_RESULT_NEED_RESET)
37 orig = PCI_ERS_RESULT_NEED_RESET;
38 break;
39 default:
40 break;
41 }
42
43 return orig;
44}
45
Keith Busch542aeb92018-09-20 10:27:14 -060046static int report_error_detected(struct pci_dev *dev,
47 enum pci_channel_state state,
48 enum pci_ers_result *result)
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050049{
50 pci_ers_result_t vote;
51 const struct pci_error_handlers *err_handler;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050052
53 device_lock(&dev->dev);
Keith Buscha6bd1012018-09-20 10:27:16 -060054 if (!pci_dev_set_io_state(dev, state) ||
55 !dev->driver ||
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050056 !dev->driver->err_handler ||
57 !dev->driver->err_handler->error_detected) {
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050058 /*
Keith Buschbfcb79fc2018-09-20 10:27:13 -060059 * 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.
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050063 */
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050064 if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
65 vote = PCI_ERS_RESULT_NO_AER_DRIVER;
66 else
67 vote = PCI_ERS_RESULT_NONE;
68 } else {
69 err_handler = dev->driver->err_handler;
Keith Busch542aeb92018-09-20 10:27:14 -060070 vote = err_handler->error_detected(dev, state);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050071 }
Keith Busch7b42d972018-09-20 10:27:15 -060072 pci_uevent_ers(dev, vote);
Keith Busch542aeb92018-09-20 10:27:14 -060073 *result = merge_result(*result, vote);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050074 device_unlock(&dev->dev);
75 return 0;
76}
77
Keith Busch542aeb92018-09-20 10:27:14 -060078static int report_frozen_detected(struct pci_dev *dev, void *data)
79{
80 return report_error_detected(dev, pci_channel_io_frozen, data);
81}
82
83static int report_normal_detected(struct pci_dev *dev, void *data)
84{
85 return report_error_detected(dev, pci_channel_io_normal, data);
86}
87
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050088static int report_mmio_enabled(struct pci_dev *dev, void *data)
89{
Keith Busch542aeb92018-09-20 10:27:14 -060090 pci_ers_result_t vote, *result = data;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050091 const struct pci_error_handlers *err_handler;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -050092
93 device_lock(&dev->dev);
94 if (!dev->driver ||
95 !dev->driver->err_handler ||
96 !dev->driver->err_handler->mmio_enabled)
97 goto out;
98
99 err_handler = dev->driver->err_handler;
100 vote = err_handler->mmio_enabled(dev);
Keith Busch542aeb92018-09-20 10:27:14 -0600101 *result = merge_result(*result, vote);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500102out:
103 device_unlock(&dev->dev);
104 return 0;
105}
106
107static int report_slot_reset(struct pci_dev *dev, void *data)
108{
Keith Busch542aeb92018-09-20 10:27:14 -0600109 pci_ers_result_t vote, *result = data;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500110 const struct pci_error_handlers *err_handler;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500111
112 device_lock(&dev->dev);
113 if (!dev->driver ||
114 !dev->driver->err_handler ||
115 !dev->driver->err_handler->slot_reset)
116 goto out;
117
118 err_handler = dev->driver->err_handler;
119 vote = err_handler->slot_reset(dev);
Keith Busch542aeb92018-09-20 10:27:14 -0600120 *result = merge_result(*result, vote);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500121out:
122 device_unlock(&dev->dev);
123 return 0;
124}
125
126static int report_resume(struct pci_dev *dev, void *data)
127{
128 const struct pci_error_handlers *err_handler;
129
130 device_lock(&dev->dev);
Keith Buscha6bd1012018-09-20 10:27:16 -0600131 if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||
132 !dev->driver ||
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500133 !dev->driver->err_handler ||
134 !dev->driver->err_handler->resume)
135 goto out;
136
137 err_handler = dev->driver->err_handler;
138 err_handler->resume(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500139out:
Keith Busch7b42d972018-09-20 10:27:15 -0600140 pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500141 device_unlock(&dev->dev);
142 return 0;
143}
144
145/**
146 * default_reset_link - default reset function
147 * @dev: pointer to pci_dev data structure
148 *
149 * Invoked when performing link reset on a Downstream Port or a
150 * Root Port with no aer driver.
151 */
152static pci_ers_result_t default_reset_link(struct pci_dev *dev)
153{
Sinan Kaya18426232018-07-19 18:04:09 -0500154 int rc;
155
Keith Buschc4eed622018-09-20 10:27:11 -0600156 rc = pci_bus_error_reset(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500157 pci_printk(KERN_DEBUG, dev, "downstream link has been reset\n");
Sinan Kaya18426232018-07-19 18:04:09 -0500158 return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500159}
160
Oza Pawandeep0b914392018-05-17 16:44:19 -0500161static pci_ers_result_t reset_link(struct pci_dev *dev, u32 service)
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500162{
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500163 pci_ers_result_t status;
164 struct pcie_port_service_driver *driver = NULL;
165
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600166 driver = pcie_port_find_service(dev, service);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500167 if (driver && driver->reset_link) {
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600168 status = driver->reset_link(dev);
169 } else if (dev->has_secondary_link) {
170 status = default_reset_link(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500171 } else {
172 pci_printk(KERN_DEBUG, dev, "no link-reset support at upstream device %s\n",
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600173 pci_name(dev));
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500174 return PCI_ERS_RESULT_DISCONNECT;
175 }
176
177 if (status != PCI_ERS_RESULT_RECOVERED) {
178 pci_printk(KERN_DEBUG, dev, "link reset at upstream device %s failed\n",
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600179 pci_name(dev));
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500180 return PCI_ERS_RESULT_DISCONNECT;
181 }
182
183 return status;
184}
185
Keith Buschbdb5ac852018-09-20 10:27:12 -0600186void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
187 u32 service)
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500188{
Keith Busch542aeb92018-09-20 10:27:14 -0600189 pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
190 struct pci_bus *bus;
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500191
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600192 /*
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.
195 */
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;
Keith Busch542aeb92018-09-20 10:27:14 -0600199 bus = dev->subordinate;
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600200
Keith Busch542aeb92018-09-20 10:27:14 -0600201 pci_dbg(dev, "broadcast error_detected message\n");
202 if (state == pci_channel_io_frozen)
203 pci_walk_bus(bus, report_frozen_detected, &status);
204 else
205 pci_walk_bus(bus, report_normal_detected, &status);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500206
Keith Buschbdb5ac852018-09-20 10:27:12 -0600207 if (state == pci_channel_io_frozen &&
208 reset_link(dev, service) != PCI_ERS_RESULT_RECOVERED)
209 goto failed;
210
Keith Busch542aeb92018-09-20 10:27:14 -0600211 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);
215 }
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500216
217 if (status == PCI_ERS_RESULT_NEED_RESET) {
218 /*
219 * TODO: Should call platform-specific
220 * functions to reset slot before calling
221 * drivers' slot_reset callbacks?
222 */
Keith Busch542aeb92018-09-20 10:27:14 -0600223 status = PCI_ERS_RESULT_RECOVERED;
224 pci_dbg(dev, "broadcast slot_reset message\n");
225 pci_walk_bus(bus, report_slot_reset, &status);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500226 }
227
228 if (status != PCI_ERS_RESULT_RECOVERED)
229 goto failed;
230
Keith Busch542aeb92018-09-20 10:27:14 -0600231 pci_dbg(dev, "broadcast resume message\n");
232 pci_walk_bus(bus, report_resume, &status);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500233
Keith Buschbfcb79fc2018-09-20 10:27:13 -0600234 pci_aer_clear_device_status(dev);
235 pci_cleanup_aer_uncorrect_error_status(dev);
Oza Pawandeep2e28bc82018-05-17 16:44:15 -0500236 pci_info(dev, "AER: Device recovery successful\n");
237 return;
238
239failed:
240 pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
241
242 /* TODO: Should kernel panic here? */
243 pci_info(dev, "AER: Device recovery failed\n");
244}