]>
Commit | Line | Data |
---|---|---|
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 |
23 | static 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 | 48 | static 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 |
82 | static int report_frozen_detected(struct pci_dev *dev, void *data) |
83 | { | |
84 | return report_error_detected(dev, pci_channel_io_frozen, data); | |
85 | } | |
86 | ||
87 | static 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 |
92 | static 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 |
106 | out: |
107 | device_unlock(&dev->dev); | |
108 | return 0; | |
109 | } | |
110 | ||
111 | static 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 |
125 | out: |
126 | device_unlock(&dev->dev); | |
127 | return 0; | |
128 | } | |
129 | ||
130 | static 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 | 143 | out: |
7b42d97e | 144 | pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED); |
2e28bc84 OP |
145 | device_unlock(&dev->dev); |
146 | return 0; | |
147 | } | |
148 | ||
e8e5ff2a | 149 | pci_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 | |
207 | failed: | |
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 | } |