]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/pci/pcie/err.c
PCI/ERR: Simplify by computing pci_pcie_type() once
[mirror_ubuntu-jammy-kernel.git] / drivers / pci / pcie / err.c
CommitLineData
2e28bc84
OP
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
8d077c3c
BH
13#define dev_fmt(fmt) "AER: " fmt
14
2e28bc84
OP
15#include <linux/pci.h>
16#include <linux/module.h>
2e28bc84
OP
17#include <linux/kernel.h>
18#include <linux/errno.h>
19#include <linux/aer.h>
20#include "portdrv.h"
21#include "../pci.h"
22
2e28bc84
OP
23static pci_ers_result_t merge_result(enum pci_ers_result orig,
24 enum pci_ers_result new)
25{
26 if (new == PCI_ERS_RESULT_NO_AER_DRIVER)
27 return PCI_ERS_RESULT_NO_AER_DRIVER;
28
29 if (new == PCI_ERS_RESULT_NONE)
30 return orig;
31
32 switch (orig) {
33 case PCI_ERS_RESULT_CAN_RECOVER:
34 case PCI_ERS_RESULT_RECOVERED:
35 orig = new;
36 break;
37 case PCI_ERS_RESULT_DISCONNECT:
38 if (new == PCI_ERS_RESULT_NEED_RESET)
39 orig = PCI_ERS_RESULT_NEED_RESET;
40 break;
41 default:
42 break;
43 }
44
45 return orig;
46}
47
542aeb9c 48static int report_error_detected(struct pci_dev *dev,
16d79cd4 49 pci_channel_state_t state,
542aeb9c 50 enum pci_ers_result *result)
2e28bc84
OP
51{
52 pci_ers_result_t vote;
53 const struct pci_error_handlers *err_handler;
2e28bc84
OP
54
55 device_lock(&dev->dev);
a6bd101b
KB
56 if (!pci_dev_set_io_state(dev, state) ||
57 !dev->driver ||
2e28bc84
OP
58 !dev->driver->err_handler ||
59 !dev->driver->err_handler->error_detected) {
2e28bc84 60 /*
bfcb79fc
KB
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.
2e28bc84 65 */
01daacfb 66 if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
2e28bc84 67 vote = PCI_ERS_RESULT_NO_AER_DRIVER;
8d077c3c 68 pci_info(dev, "can't recover (no error_detected callback)\n");
01daacfb 69 } else {
2e28bc84 70 vote = PCI_ERS_RESULT_NONE;
01daacfb 71 }
2e28bc84
OP
72 } else {
73 err_handler = dev->driver->err_handler;
542aeb9c 74 vote = err_handler->error_detected(dev, state);
2e28bc84 75 }
7b42d97e 76 pci_uevent_ers(dev, vote);
542aeb9c 77 *result = merge_result(*result, vote);
2e28bc84
OP
78 device_unlock(&dev->dev);
79 return 0;
80}
81
542aeb9c
KB
82static int report_frozen_detected(struct pci_dev *dev, void *data)
83{
84 return report_error_detected(dev, pci_channel_io_frozen, data);
85}
86
87static int report_normal_detected(struct pci_dev *dev, void *data)
88{
89 return report_error_detected(dev, pci_channel_io_normal, data);
90}
91
2e28bc84
OP
92static int report_mmio_enabled(struct pci_dev *dev, void *data)
93{
542aeb9c 94 pci_ers_result_t vote, *result = data;
2e28bc84 95 const struct pci_error_handlers *err_handler;
2e28bc84
OP
96
97 device_lock(&dev->dev);
98 if (!dev->driver ||
99 !dev->driver->err_handler ||
100 !dev->driver->err_handler->mmio_enabled)
101 goto out;
102
103 err_handler = dev->driver->err_handler;
104 vote = err_handler->mmio_enabled(dev);
542aeb9c 105 *result = merge_result(*result, vote);
2e28bc84
OP
106out:
107 device_unlock(&dev->dev);
108 return 0;
109}
110
111static int report_slot_reset(struct pci_dev *dev, void *data)
112{
542aeb9c 113 pci_ers_result_t vote, *result = data;
2e28bc84 114 const struct pci_error_handlers *err_handler;
2e28bc84
OP
115
116 device_lock(&dev->dev);
117 if (!dev->driver ||
118 !dev->driver->err_handler ||
119 !dev->driver->err_handler->slot_reset)
120 goto out;
121
122 err_handler = dev->driver->err_handler;
123 vote = err_handler->slot_reset(dev);
542aeb9c 124 *result = merge_result(*result, vote);
2e28bc84
OP
125out:
126 device_unlock(&dev->dev);
127 return 0;
128}
129
130static int report_resume(struct pci_dev *dev, void *data)
131{
132 const struct pci_error_handlers *err_handler;
133
134 device_lock(&dev->dev);
a6bd101b
KB
135 if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||
136 !dev->driver ||
2e28bc84
OP
137 !dev->driver->err_handler ||
138 !dev->driver->err_handler->resume)
139 goto out;
140
141 err_handler = dev->driver->err_handler;
142 err_handler->resume(dev);
2e28bc84 143out:
7b42d97e 144 pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);
2e28bc84
OP
145 device_unlock(&dev->dev);
146 return 0;
147}
148
e8e5ff2a 149pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
8f1bbfbc
SK
150 pci_channel_state_t state,
151 pci_ers_result_t (*reset_subordinates)(struct pci_dev *pdev))
2e28bc84 152{
480ef7cb 153 int type = pci_pcie_type(dev);
542aeb9c
KB
154 pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
155 struct pci_bus *bus;
2e28bc84 156
bfcb79fc
KB
157 /*
158 * Error recovery runs on all subordinates of the first downstream port.
159 * If the downstream port detected the error, it is cleared at the end.
160 */
480ef7cb
SK
161 if (!(type == PCI_EXP_TYPE_ROOT_PORT ||
162 type == PCI_EXP_TYPE_DOWNSTREAM))
5d69dcc9 163 dev = pci_upstream_bridge(dev);
542aeb9c 164 bus = dev->subordinate;
bfcb79fc 165
542aeb9c 166 pci_dbg(dev, "broadcast error_detected message\n");
b5dfbeac 167 if (state == pci_channel_io_frozen) {
542aeb9c 168 pci_walk_bus(bus, report_frozen_detected, &status);
8f1bbfbc 169 status = reset_subordinates(dev);
b6cf1a42 170 if (status != PCI_ERS_RESULT_RECOVERED) {
8f1bbfbc 171 pci_warn(dev, "subordinate device reset failed\n");
b5dfbeac 172 goto failed;
b6cf1a42 173 }
b5dfbeac 174 } else {
542aeb9c 175 pci_walk_bus(bus, report_normal_detected, &status);
b5dfbeac 176 }
bdb5ac85 177
542aeb9c
KB
178 if (status == PCI_ERS_RESULT_CAN_RECOVER) {
179 status = PCI_ERS_RESULT_RECOVERED;
180 pci_dbg(dev, "broadcast mmio_enabled message\n");
181 pci_walk_bus(bus, report_mmio_enabled, &status);
182 }
2e28bc84
OP
183
184 if (status == PCI_ERS_RESULT_NEED_RESET) {
185 /*
186 * TODO: Should call platform-specific
187 * functions to reset slot before calling
188 * drivers' slot_reset callbacks?
189 */
542aeb9c
KB
190 status = PCI_ERS_RESULT_RECOVERED;
191 pci_dbg(dev, "broadcast slot_reset message\n");
192 pci_walk_bus(bus, report_slot_reset, &status);
2e28bc84
OP
193 }
194
195 if (status != PCI_ERS_RESULT_RECOVERED)
196 goto failed;
197
542aeb9c
KB
198 pci_dbg(dev, "broadcast resume message\n");
199 pci_walk_bus(bus, report_resume, &status);
2e28bc84 200
068c29a2
JC
201 if (pcie_aer_is_native(dev))
202 pcie_clear_device_status(dev);
894020fd 203 pci_aer_clear_nonfatal_status(dev);
8d077c3c 204 pci_info(dev, "device recovery successful\n");
e8e5ff2a 205 return status;
2e28bc84
OP
206
207failed:
208 pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
209
210 /* TODO: Should kernel panic here? */
8d077c3c 211 pci_info(dev, "device recovery failed\n");
e8e5ff2a
KS
212
213 return status;
2e28bc84 214}