← Documents Documentation/PCI/pci-error-recovery.rst GitHub 원문 ↗

Linux 6.18.37 · PCI

PCI error recovery

PCI bus disconnect 뒤 여러 function driver가 error_detected·mmio_enabled·slot_reset·resume callback으로 협력하는 복구 상태 기계를 설명합니다.

Source pathDocumentation/PCI/pci-error-recovery.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

pci-error-recovery.rst:1-457

Platform은 bus error가 발생하면 I/O를 차단하고 모든 관련 driver에 `error_detected()`를 호출한 뒤 응답을 합산해 MMIO enable, link reset, slot reset 또는 permanent failure로 진행합니다.

Driver는 reset 뒤 hardware를 재초기화하되 `resume()` 전에는 정상 I/O를 시작하지 않아야 하며, multi-function card에서는 global initialization을 한 function만 수행하도록 조정해야 합니다.

복구 불가 시 pending I/O를 취소하고 `-EIO`를 반환하며 shutdown과 같은 cleanup을 수행합니다. Error 처리 중 interrupt delivery 여부는 platform policy이므로 ack할 수 없으면 `IRQ_NOTHANDLED`를 반환합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ==================
4 PCI Error Recovery
5 ==================
6
7
8 :Authors: - Linas Vepstas <[email protected]>
9 - Richard Lary <[email protected]>
10 - Mike Mason <[email protected]>
11
12
13 Many PCI bus controllers are able to detect a variety of hardware
14 PCI errors on the bus, such as parity errors on the data and address
15 buses, as well as SERR and PERR errors. Some of the more advanced
16 chipsets are able to deal with these errors; these include PCIe chipsets,
17 and the PCI-host bridges found on IBM Power4, Power5 and Power6-based
18 pSeries boxes. A typical action taken is to disconnect the affected device,
19 halting all I/O to it. The goal of a disconnection is to avoid system
20 corruption; for example, to halt system memory corruption due to DMAs
21 to "wild" addresses. Typically, a reconnection mechanism is also
22 offered, so that the affected PCI device(s) are reset and put back
23 into working condition. The reset phase requires coordination
24 between the affected device drivers and the PCI controller chip.
25 This document describes a generic API for notifying device drivers
26 of a bus disconnection, and then performing error recovery.
27 This API is currently implemented in the 2.6.16 and later kernels.
28
29 Reporting and recovery is performed in several steps. First, when
30 a PCI hardware error has resulted in a bus disconnect, that event
31 is reported as soon as possible to all affected device drivers,
32 including multiple instances of a device driver on multi-function
33 cards. This allows device drivers to avoid deadlocking in spinloops,
34 waiting for some i/o-space register to change, when it never will.
35 It also gives the drivers a chance to defer incoming I/O as
36 needed.
37
38 Next, recovery is performed in several stages. Most of the complexity
39 is forced by the need to handle multi-function devices, that is,
40 devices that have multiple device drivers associated with them.
41 In the first stage, each driver is allowed to indicate what type
42 of reset it desires, the choices being a simple re-enabling of I/O
43 or requesting a slot reset.
44
45 If any driver requests a slot reset, that is what will be done.
46
47 After a reset and/or a re-enabling of I/O, all drivers are
48 again notified, so that they may then perform any device setup/config
49 that may be required. After these have all completed, a final
50 "resume normal operations" event is sent out.
51
52 The biggest reason for choosing a kernel-based implementation rather
53 than a user-space implementation was the need to deal with bus
54 disconnects of PCI devices attached to storage media, and, in particular,
55 disconnects from devices holding the root file system. If the root
56 file system is disconnected, a user-space mechanism would have to go
57 through a large number of contortions to complete recovery. Almost all
58 of the current Linux file systems are not tolerant of disconnection
59 from/reconnection to their underlying block device. By contrast,
60 bus errors are easy to manage in the device driver. Indeed, most
61 device drivers already handle very similar recovery procedures;
62 for example, the SCSI-generic layer already provides significant
63 mechanisms for dealing with SCSI bus errors and SCSI bus resets.
64
65
66 Detailed Design
67 ===============
68
69 Design and implementation details below, based on a chain of
70 public email discussions with Ben Herrenschmidt, circa 5 April 2005.
71
72 The error recovery API support is exposed to the driver in the form of
73 a structure of function pointers pointed to by a new field in struct
74 pci_driver. A driver that fails to provide the structure is "non-aware",
75 and the actual recovery steps taken are platform dependent. The
76 arch/powerpc implementation will simulate a PCI hotplug remove/add.
77
78 This structure has the form::
79
80 struct pci_error_handlers
81 {
82 int (*error_detected)(struct pci_dev *dev, pci_channel_state_t);
83 int (*mmio_enabled)(struct pci_dev *dev);
84 int (*slot_reset)(struct pci_dev *dev);
85 void (*resume)(struct pci_dev *dev);
86 void (*cor_error_detected)(struct pci_dev *dev);
87 };
88
89 The possible channel states are::
90
91 typedef enum {
92 pci_channel_io_normal, /* I/O channel is in normal state */
93 pci_channel_io_frozen, /* I/O to channel is blocked */
94 pci_channel_io_perm_failure, /* PCI card is dead */
95 } pci_channel_state_t;
96
97 Possible return values are::
98
99 enum pci_ers_result {
100 PCI_ERS_RESULT_NONE, /* no result/none/not supported in device driver */
101 PCI_ERS_RESULT_CAN_RECOVER, /* Device driver can recover without slot reset */
102 PCI_ERS_RESULT_NEED_RESET, /* Device driver wants slot to be reset. */
103 PCI_ERS_RESULT_DISCONNECT, /* Device has completely failed, is unrecoverable */
104 PCI_ERS_RESULT_RECOVERED, /* Device driver is fully recovered and operational */
105 };
106
107 A driver does not have to implement all of these callbacks; however,
108 if it implements any, it must implement error_detected(). If a callback
109 is not implemented, the corresponding feature is considered unsupported.
110 For example, if mmio_enabled() and resume() aren't there, then it
111 is assumed that the driver does not need these callbacks
112 for recovery. Typically a driver will want to know about
113 a slot_reset().
114
115 The actual steps taken by a platform to recover from a PCI error
116 event will be platform-dependent, but will follow the general
117 sequence described below.
118
119 STEP 0: Error Event
120 -------------------
121 A PCI bus error is detected by the PCI hardware. On powerpc, the slot
122 is isolated, in that all I/O is blocked: all reads return 0xffffffff,
123 all writes are ignored.
124
125 Similarly, on platforms supporting Downstream Port Containment
126 (PCIe r7.0 sec 6.2.11), the link to the sub-hierarchy with the
127 faulting device is disabled. Any device in the sub-hierarchy
128 becomes inaccessible.
129
130 STEP 1: Notification
131 --------------------
132 Platform calls the error_detected() callback on every instance of
133 every driver affected by the error.
134
135 At this point, the device might not be accessible anymore, depending on
136 the platform (the slot will be isolated on powerpc). The driver may
137 already have "noticed" the error because of a failing I/O, but this
138 is the proper "synchronization point", that is, it gives the driver
139 a chance to cleanup, waiting for pending stuff (timers, whatever, etc...)
140 to complete; it can take semaphores, schedule, etc... everything but
141 touch the device. Within this function and after it returns, the driver
142 shouldn't do any new IOs. Called in task context. This is sort of a
143 "quiesce" point. See note about interrupts at the end of this doc.
144
145 All drivers participating in this system must implement this call.
146 The driver must return one of the following result codes:
147
148 - PCI_ERS_RESULT_RECOVERED
149 Driver returns this if it thinks the device is usable despite
150 the error and does not need further intervention.
151 - PCI_ERS_RESULT_CAN_RECOVER
152 Driver returns this if it thinks it might be able to recover
153 the HW by just banging IOs or if it wants to be given
154 a chance to extract some diagnostic information (see
155 mmio_enable, below).
156 - PCI_ERS_RESULT_NEED_RESET
157 Driver returns this if it can't recover without a
158 slot reset.
159 - PCI_ERS_RESULT_DISCONNECT
160 Driver returns this if it doesn't want to recover at all.
161
162 The next step taken will depend on the result codes returned by the
163 drivers.
164
165 If all drivers on the segment/slot return PCI_ERS_RESULT_CAN_RECOVER,
166 then the platform should re-enable IOs on the slot (or do nothing in
167 particular, if the platform doesn't isolate slots), and recovery
168 proceeds to STEP 2 (MMIO Enable).
169
170 If any driver requested a slot reset (by returning PCI_ERS_RESULT_NEED_RESET),
171 then recovery proceeds to STEP 4 (Slot Reset).
172
173 If the platform is unable to recover the slot, the next step
174 is STEP 6 (Permanent Failure).
175
176 .. note::
177
178 The current powerpc implementation assumes that a device driver will
179 *not* schedule or semaphore in this routine; the current powerpc
180 implementation uses one kernel thread to notify all devices;
181 thus, if one device sleeps/schedules, all devices are affected.
182 Doing better requires complex multi-threaded logic in the error
183 recovery implementation (e.g. waiting for all notification threads
184 to "join" before proceeding with recovery.) This seems excessively
185 complex and not worth implementing.
186
187 The current powerpc implementation doesn't much care if the device
188 attempts I/O at this point, or not. I/Os will fail, returning
189 a value of 0xff on read, and writes will be dropped. If more than
190 EEH_MAX_FAILS I/Os are attempted to a frozen adapter, EEH
191 assumes that the device driver has gone into an infinite loop
192 and prints an error to syslog. A reboot is then required to
193 get the device working again.
194
195 STEP 2: MMIO Enabled
196 --------------------
197 The platform re-enables MMIO to the device (but typically not the
198 DMA), and then calls the mmio_enabled() callback on all affected
199 device drivers.
200
201 This is the "early recovery" call. IOs are allowed again, but DMA is
202 not, with some restrictions. This is NOT a callback for the driver to
203 start operations again, only to peek/poke at the device, extract diagnostic
204 information, if any, and eventually do things like trigger a device local
205 reset or some such, but not restart operations. This callback is made if
206 all drivers on a segment agree that they can try to recover and if no automatic
207 link reset was performed by the HW. If the platform can't just re-enable IOs
208 without a slot reset or a link reset, it will not call this callback, and
209 instead will have gone directly to STEP 3 (Link Reset) or STEP 4 (Slot Reset).
210
211 .. note::
212
213 On platforms supporting Advanced Error Reporting (PCIe r7.0 sec 6.2),
214 the faulting device may already be accessible in STEP 1 (Notification).
215 Drivers should nevertheless defer accesses to STEP 2 (MMIO Enabled)
216 to be compatible with EEH on powerpc and with s390 (where devices are
217 inaccessible until STEP 2).
218
219 On platforms supporting Downstream Port Containment, the link to the
220 sub-hierarchy with the faulting device is re-enabled in STEP 3 (Link
221 Reset). Hence devices in the sub-hierarchy are inaccessible until
222 STEP 4 (Slot Reset).
223
224 For errors such as Surprise Down (PCIe r7.0 sec 6.2.7), the device
225 may not even be accessible in STEP 4 (Slot Reset). Drivers can detect
226 accessibility by checking whether reads from the device return all 1's
227 (PCI_POSSIBLE_ERROR()).
228
229 .. note::
230
231 The following is proposed; no platform implements this yet:
232 Proposal: All I/Os should be done _synchronously_ from within
233 this callback, errors triggered by them will be returned via
234 the normal pci_check_whatever() API, no new error_detected()
235 callback will be issued due to an error happening here. However,
236 such an error might cause IOs to be re-blocked for the whole
237 segment, and thus invalidate the recovery that other devices
238 on the same segment might have done, forcing the whole segment
239 into one of the next states, that is, link reset or slot reset.
240
241 The driver should return one of the following result codes:
242 - PCI_ERS_RESULT_RECOVERED
243 Driver returns this if it thinks the device is fully
244 functional and thinks it is ready to start
245 normal driver operations again. There is no
246 guarantee that the driver will actually be
247 allowed to proceed, as another driver on the
248 same segment might have failed and thus triggered a
249 slot reset on platforms that support it.
250
251 - PCI_ERS_RESULT_NEED_RESET
252 Driver returns this if it thinks the device is not
253 recoverable in its current state and it needs a slot
254 reset to proceed.
255
256 - PCI_ERS_RESULT_DISCONNECT
257 Same as above. Total failure, no recovery even after
258 reset driver dead. (To be defined more precisely)
259
260 The next step taken depends on the results returned by the drivers.
261 If all drivers returned PCI_ERS_RESULT_RECOVERED, then the platform
262 proceeds to either STEP 3 (Link Reset) or to STEP 5 (Resume Operations).
263
264 If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform
265 proceeds to STEP 4 (Slot Reset)
266
267 STEP 3: Link Reset
268 ------------------
269 The platform resets the link. This is a PCIe specific step
270 and is done whenever a fatal error has been detected that can be
271 "solved" by resetting the link.
272
273 STEP 4: Slot Reset
274 ------------------
275
276 In response to a return value of PCI_ERS_RESULT_NEED_RESET, the
277 platform will perform a slot reset on the requesting PCI device(s).
278 The actual steps taken by a platform to perform a slot reset
279 will be platform-dependent. Upon completion of slot reset, the
280 platform will call the device slot_reset() callback.
281
282 Powerpc platforms implement two levels of slot reset:
283 soft reset(default) and fundamental(optional) reset.
284
285 Powerpc soft reset consists of asserting the adapter #RST line and then
286 restoring the PCI BARs and PCI configuration header to a state
287 that is equivalent to what it would be after a fresh system
288 power-on followed by power-on BIOS/system firmware initialization.
289 Soft reset is also known as hot-reset.
290
291 Powerpc fundamental reset is supported by PCIe cards only
292 and results in device's state machines, hardware logic, port states and
293 configuration registers to initialize to their default conditions.
294
295 For most PCI devices, a soft reset will be sufficient for recovery.
296 Optional fundamental reset is provided to support a limited number
297 of PCIe devices for which a soft reset is not sufficient
298 for recovery.
299
300 If the platform supports PCI hotplug, then the reset might be
301 performed by toggling the slot electrical power off/on.
302
303 It is important for the platform to restore the PCI config space
304 to the "fresh poweron" state, rather than the "last state". After
305 a slot reset, the device driver will almost always use its standard
306 device initialization routines, and an unusual config space setup
307 may result in hung devices, kernel panics, or silent data corruption.
308
309 This call gives drivers the chance to re-initialize the hardware
310 (re-download firmware, etc.). At this point, the driver may assume
311 that the card is in a fresh state and is fully functional. The slot
312 is unfrozen and the driver has full access to PCI config space,
313 memory mapped I/O space and DMA. Interrupts (Legacy, MSI, or MSI-X)
314 will also be available.
315
316 Drivers should not restart normal I/O processing operations
317 at this point. If all device drivers report success on this
318 callback, the platform will call resume() to complete the sequence,
319 and let the driver restart normal I/O processing.
320
321 A driver can still return a critical failure for this function if
322 it can't get the device operational after reset. If the platform
323 previously tried a soft reset, it might now try a hard reset (power
324 cycle) and then call slot_reset() again. If the device still can't
325 be recovered, there is nothing more that can be done; the platform
326 will typically report a "permanent failure" in such a case. The
327 device will be considered "dead" in this case.
328
329 Drivers for multi-function cards will need to coordinate among
330 themselves as to which driver instance will perform any "one-shot"
331 or global device initialization. For example, the Symbios sym53cxx2
332 driver performs device init only from PCI function 0::
333
334 + if (PCI_FUNC(pdev->devfn) == 0)
335 + sym_reset_scsi_bus(np, 0);
336
337 Result codes:
338 - PCI_ERS_RESULT_DISCONNECT
339 Same as above.
340
341 Drivers for PCIe cards that require a fundamental reset must
342 set the needs_freset bit in the pci_dev structure in their probe function.
343 For example, the QLogic qla2xxx driver sets the needs_freset bit for certain
344 PCI card types::
345
346 + /* Set EEH reset type to fundamental if required by hba */
347 + if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha))
348 + pdev->needs_freset = 1;
349 +
350
351 Platform proceeds either to STEP 5 (Resume Operations) or STEP 6 (Permanent
352 Failure).
353
354 .. note::
355
356 The current powerpc implementation does not try a power-cycle
357 reset if the driver returned PCI_ERS_RESULT_DISCONNECT.
358 However, it probably should.
359
360
361 STEP 5: Resume Operations
362 -------------------------
363 The platform will call the resume() callback on all affected device
364 drivers if all drivers on the segment have returned
365 PCI_ERS_RESULT_RECOVERED from one of the 3 previous callbacks.
366 The goal of this callback is to tell the driver to restart activity,
367 that everything is back and running. This callback does not return
368 a result code.
369
370 At this point, if a new error happens, the platform will restart
371 a new error recovery sequence.
372
373 STEP 6: Permanent Failure
374 -------------------------
375 A "permanent failure" has occurred, and the platform cannot recover
376 the device. The platform will call error_detected() with a
377 pci_channel_state_t value of pci_channel_io_perm_failure.
378
379 The device driver should, at this point, assume the worst. It should
380 cancel all pending I/O, refuse all new I/O, returning -EIO to
381 higher layers. The device driver should then clean up all of its
382 memory and remove itself from kernel operations, much as it would
383 during system shutdown.
384
385 The platform will typically notify the system operator of the
386 permanent failure in some way. If the device is hotplug-capable,
387 the operator will probably want to remove and replace the device.
388 Note, however, not all failures are truly "permanent". Some are
389 caused by over-heating, some by a poorly seated card. Many
390 PCI error events are caused by software bugs, e.g. DMAs to
391 wild addresses or bogus split transactions due to programming
392 errors. See the discussion in Documentation/arch/powerpc/eeh-pci-error-recovery.rst
393 for additional detail on real-life experience of the causes of
394 software errors.
395
396
397 Conclusion; General Remarks
398 ---------------------------
399 The way the callbacks are called is platform policy. A platform with
400 no slot reset capability may want to just "ignore" drivers that can't
401 recover (disconnect them) and try to let other cards on the same segment
402 recover. Keep in mind that in most real life cases, though, there will
403 be only one driver per segment.
404
405 Now, a note about interrupts. If you get an interrupt and your
406 device is dead or has been isolated, there is a problem :)
407 The current policy is to turn this into a platform policy.
408 That is, the recovery API only requires that:
409
410 - There is no guarantee that interrupt delivery can proceed from any
411 device on the segment starting from the error detection and until the
412 slot_reset callback is called, at which point interrupts are expected
413 to be fully operational.
414
415 - There is no guarantee that interrupt delivery is stopped, that is,
416 a driver that gets an interrupt after detecting an error, or that detects
417 an error within the interrupt handler such that it prevents proper
418 ack'ing of the interrupt (and thus removal of the source) should just
419 return IRQ_NOTHANDLED. It's up to the platform to deal with that
420 condition, typically by masking the IRQ source during the duration of
421 the error handling. It is expected that the platform "knows" which
422 interrupts are routed to error-management capable slots and can deal
423 with temporarily disabling that IRQ number during error processing (this
424 isn't terribly complex). That means some IRQ latency for other devices
425 sharing the interrupt, but there is simply no other way. High end
426 platforms aren't supposed to share interrupts between many devices
427 anyway :)
428
429 .. note::
430
431 Implementation details for the powerpc platform are discussed in
432 the file Documentation/arch/powerpc/eeh-pci-error-recovery.rst
433
434 As of this writing, there is a growing list of device drivers with
435 patches implementing error recovery. Not all of these patches are in
436 mainline yet. These may be used as "examples":
437
438 - drivers/scsi/ipr
439 - drivers/scsi/sym53c8xx_2
440 - drivers/scsi/qla2xxx
441 - drivers/scsi/lpfc
442 - drivers/next/bnx2.c
443 - drivers/next/e100.c
444 - drivers/net/e1000
445 - drivers/net/e1000e
446 - drivers/net/ixgbe
447 - drivers/net/cxgb3
448 - drivers/net/s2io.c
449
450 The cor_error_detected() callback is invoked in handle_error_source() when
451 the error severity is "correctable". The callback is optional and allows
452 additional logging to be done if desired. See example:
453
454 - drivers/cxl/pci.c
455
456 The End
457 -------
458

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

목적과 적용 범위

1-28

저자는 Linas Vepstas, Richard Lary, Mike Mason입니다.

많은 PCI bus controller는 data·address bus의 parity error와 SERR·PERR 같은 hardware PCI error를 감지할 수 있습니다. PCIe chipset과 IBM Power4·Power5·Power6 기반 pSeries의 PCI host bridge 같은 고급 chipset은 이러한 error를 처리할 수 있습니다.

일반적인 조치는 영향받은 device를 disconnect하여 모든 I/O를 중단하는 것입니다. 이는 잘못된 address로 향하는 DMA처럼 system memory를 손상시킬 수 있는 상황을 막습니다.

대개 영향받은 PCI device를 reset하고 다시 동작시키는 reconnect mechanism도 제공합니다. Reset 단계에는 device driver와 PCI controller chip의 협력이 필요합니다.

이 문서는 bus disconnect를 device driver에 알리고 error recovery를 수행하는 generic API를 설명합니다. 이 API는 kernel 2.6.16 이후에 구현되어 있습니다.

.. SPDX-License-Identifier: GPL-2.0

==================
PCI Error Recovery
==================


:Authors: - Linas Vepstas <[email protected]>
          - Richard Lary <[email protected]>
          - Mike Mason <[email protected]>


Many PCI bus controllers are able to detect a variety of hardware
PCI errors on the bus, such as parity errors on the data and address
buses, as well as SERR and PERR errors.  Some of the more advanced
chipsets are able to deal with these errors; these include PCIe chipsets,
and the PCI-host bridges found on IBM Power4, Power5 and Power6-based
pSeries boxes. A typical action taken is to disconnect the affected device,
halting all I/O to it.  The goal of a disconnection is to avoid system
corruption; for example, to halt system memory corruption due to DMAs
to "wild" addresses. Typically, a reconnection mechanism is also
offered, so that the affected PCI device(s) are reset and put back
into working condition. The reset phase requires coordination
between the affected device drivers and the PCI controller chip.
This document describes a generic API for notifying device drivers
of a bus disconnection, and then performing error recovery.
This API is currently implemented in the 2.6.16 and later kernels.

보고와 복구 단계

29-51

PCI hardware error가 bus disconnect를 일으키면 multi-function card의 여러 driver instance를 포함해 영향받은 모든 device driver에 가능한 빨리 보고합니다.

이 알림으로 driver는 영원히 바뀌지 않을 I/O-space register를 기다리며 spinloop에서 deadlock되는 일을 피하고, 들어오는 I/O를 필요한 만큼 미룰 수 있습니다.

복구가 여러 단계인 가장 큰 이유는 여러 device driver가 연결된 multi-function device를 처리해야 하기 때문입니다. 첫 단계에서 각 driver는 단순 I/O 재활성화 또는 slot reset 중 원하는 reset 유형을 표시합니다.

Driver 하나라도 slot reset을 요청하면 slot reset을 수행합니다. Reset 또는 I/O 재활성화 뒤에는 모든 driver에 다시 알려 필요한 device setup·configuration을 수행하게 하고, 모두 끝나면 마지막으로 정상 동작 재개 event를 보냅니다.

PCI error recovery 개요
Bus error + disconnect모든 driver에 error_detected()I/O enable 또는 slot reset 합의Driver setup/configresume()

모든 function의 응답을 모아 가장 강한 복구 요구를 적용합니다.

Reporting and recovery is performed in several steps. First, when
a PCI hardware error has resulted in a bus disconnect, that event
is reported as soon as possible to all affected device drivers,
including multiple instances of a device driver on multi-function
cards. This allows device drivers to avoid deadlocking in spinloops,
waiting for some i/o-space register to change, when it never will.
It also gives the drivers a chance to defer incoming I/O as
needed.

Next, recovery is performed in several stages. Most of the complexity
is forced by the need to handle multi-function devices, that is,
devices that have multiple device drivers associated with them.
In the first stage, each driver is allowed to indicate what type
of reset it desires, the choices being a simple re-enabling of I/O
or requesting a slot reset.

If any driver requests a slot reset, that is what will be done.

After a reset and/or a re-enabling of I/O, all drivers are
again notified, so that they may then perform any device setup/config
that may be required.  After these have all completed, a final
"resume normal operations" event is sent out.

Kernel 기반 구현을 선택한 이유

52-65

User space가 아니라 kernel 기반 구현을 선택한 가장 큰 이유는 storage media에 연결된 PCI device, 특히 root filesystem을 가진 device의 bus disconnect를 처리해야 하기 때문입니다.

Root filesystem이 끊기면 user-space mechanism은 복구를 완료하기 위해 매우 복잡한 우회가 필요합니다. 현재 Linux filesystem 대부분은 기반 block device의 disconnect와 reconnect를 견디지 못합니다.

반면 bus error는 device driver에서 다루기 쉽고, 많은 driver가 이미 비슷한 복구 절차를 처리합니다. 예를 들어 SCSI generic layer에는 SCSI bus error와 bus reset을 다루는 상당한 mechanism이 있습니다.

The biggest reason for choosing a kernel-based implementation rather
than a user-space implementation was the need to deal with bus
disconnects of PCI devices attached to storage media, and, in particular,
disconnects from devices holding the root file system.  If the root
file system is disconnected, a user-space mechanism would have to go
through a large number of contortions to complete recovery. Almost all
of the current Linux file systems are not tolerant of disconnection
from/reconnection to their underlying block device. By contrast,
bus errors are easy to manage in the device driver. Indeed, most
device drivers already handle very similar recovery procedures;
for example, the SCSI-generic layer already provides significant
mechanisms for dealing with SCSI bus errors and SCSI bus resets.

pci_error_handlers callback

66-88

아래 설계와 구현 상세는 2005년 4월 5일 무렵 Ben Herrenschmidt와의 공개 email 논의를 바탕으로 합니다.

Error recovery API는 `struct pci_driver`의 새 field가 가리키는 function pointer 구조체로 driver에 노출됩니다. 이 구조체를 제공하지 않는 driver는 non-aware이며 실제 복구 단계는 platform에 따라 달라집니다. `arch/powerpc` 구현은 PCI hotplug remove/add를 모의합니다.

struct pci_error_handlers {
        int (*error_detected)(struct pci_dev *dev, pci_channel_state_t);
        int (*mmio_enabled)(struct pci_dev *dev);
        int (*slot_reset)(struct pci_dev *dev);
        void (*resume)(struct pci_dev *dev);
        void (*cor_error_detected)(struct pci_dev *dev);
};
Error recovery callback
Callback시점과 목적
error_detected()Error 알림과 quiesce
mmio_enabled()MMIO만 복구된 early recovery
slot_reset()Slot reset 뒤 hardware 재초기화
resume()정상 I/O 재개
cor_error_detected()Correctable error 추가 logging

복구 단계별 driver 진입점입니다.

Detailed Design
===============

Design and implementation details below, based on a chain of
public email discussions with Ben Herrenschmidt, circa 5 April 2005.

The error recovery API support is exposed to the driver in the form of
a structure of function pointers pointed to by a new field in struct
pci_driver. A driver that fails to provide the structure is "non-aware",
and the actual recovery steps taken are platform dependent.  The
arch/powerpc implementation will simulate a PCI hotplug remove/add.

This structure has the form::

        struct pci_error_handlers
        {
                int (*error_detected)(struct pci_dev *dev, pci_channel_state_t);
                int (*mmio_enabled)(struct pci_dev *dev);
                int (*slot_reset)(struct pci_dev *dev);
                void (*resume)(struct pci_dev *dev);
                void (*cor_error_detected)(struct pci_dev *dev);
        };

Channel state와 result code

89-118

`pci_channel_state_t`는 I/O channel이 정상인지, 차단됐는지, card가 완전히 죽었는지를 나타냅니다.

pci_channel_state_t
State의미
pci_channel_io_normalI/O channel 정상
pci_channel_io_frozenChannel I/O 차단
pci_channel_io_perm_failurePCI card가 죽은 영구 실패

Platform이 driver에 전달하는 channel 상태입니다.

`enum pci_ers_result`는 callback이 복구 가능성과 필요한 조치를 platform에 반환하는 값입니다.

pci_ers_result
Result의미
PCI_ERS_RESULT_NONE결과 없음 또는 driver가 미지원
PCI_ERS_RESULT_CAN_RECOVERSlot reset 없이 복구 가능
PCI_ERS_RESULT_NEED_RESETSlot reset 필요
PCI_ERS_RESULT_DISCONNECT완전 실패, 복구 불가
PCI_ERS_RESULT_RECOVERED완전히 복구되어 동작 가능

Driver 응답 중 더 강한 실패·reset 요구가 전체 segment의 다음 단계를 결정합니다.

Driver가 모든 callback을 구현할 필요는 없지만 하나라도 구현하면 `error_detected()`는 반드시 구현해야 합니다. 구현하지 않은 callback의 기능은 미지원으로 간주합니다. 예를 들어 `mmio_enabled()`와 `resume()`이 없으면 복구에 필요하지 않은 것으로 봅니다. 보통 driver는 `slot_reset()`을 알고 싶어 합니다.

실제 platform 동작은 platform별로 다르지만 아래의 일반 순서를 따릅니다.

The possible channel states are::

        typedef enum {
                pci_channel_io_normal,  /* I/O channel is in normal state */
                pci_channel_io_frozen,  /* I/O to channel is blocked */
                pci_channel_io_perm_failure, /* PCI card is dead */
        } pci_channel_state_t;

Possible return values are::

        enum pci_ers_result {
                PCI_ERS_RESULT_NONE,        /* no result/none/not supported in device driver */
                PCI_ERS_RESULT_CAN_RECOVER, /* Device driver can recover without slot reset */
                PCI_ERS_RESULT_NEED_RESET,  /* Device driver wants slot to be reset. */
                PCI_ERS_RESULT_DISCONNECT,  /* Device has completely failed, is unrecoverable */
                PCI_ERS_RESULT_RECOVERED,   /* Device driver is fully recovered and operational */
        };

A driver does not have to implement all of these callbacks; however,
if it implements any, it must implement error_detected(). If a callback
is not implemented, the corresponding feature is considered unsupported.
For example, if mmio_enabled() and resume() aren't there, then it
is assumed that the driver does not need these callbacks
for recovery.  Typically a driver will want to know about
a slot_reset().

The actual steps taken by a platform to recover from a PCI error
event will be platform-dependent, but will follow the general
sequence described below.

STEP 0: Error event

119-129

PCI hardware가 PCI bus error를 감지합니다. PowerPC에서는 slot을 isolate하여 모든 I/O를 차단하고, 모든 read는 `0xffffffff`, 모든 write는 무시됩니다.

Downstream Port Containment를 지원하는 platform에서는 faulting device가 속한 sub-hierarchy의 link를 disable합니다. 그 sub-hierarchy의 모든 device가 접근 불가능해집니다. 규격 위치는 PCIe r7.0 section 6.2.11입니다.

STEP 0: Error Event
-------------------
A PCI bus error is detected by the PCI hardware.  On powerpc, the slot
is isolated, in that all I/O is blocked: all reads return 0xffffffff,
all writes are ignored.

Similarly, on platforms supporting Downstream Port Containment
(PCIe r7.0 sec 6.2.11), the link to the sub-hierarchy with the
faulting device is disabled. Any device in the sub-hierarchy
becomes inaccessible.

STEP 1: Notification

130-175

Platform은 error의 영향을 받는 모든 driver instance에 `error_detected()`를 호출합니다.

Platform에 따라 device가 이미 접근 불가능할 수 있습니다. Driver가 실패한 I/O로 error를 먼저 알아챘더라도 이 callback이 올바른 synchronization point입니다.

Driver는 pending timer 등 작업이 끝나기를 기다리고 cleanup할 수 있으며 semaphore를 잡거나 schedule할 수 있지만 device에는 접근하면 안 됩니다. Callback 안과 반환 뒤에는 새 I/O를 시작하지 않아야 합니다. Task context에서 호출되며 일종의 quiesce 지점입니다. Interrupt에 관한 주의는 문서 끝에 있습니다.

참여하는 모든 driver는 이 callback을 구현해야 합니다. Device가 error에도 사용 가능하면 `PCI_ERS_RESULT_RECOVERED`, I/O를 시도해 hardware를 복구하거나 diagnostic 정보를 얻고 싶으면 `PCI_ERS_RESULT_CAN_RECOVER`, slot reset 없이는 복구할 수 없으면 `PCI_ERS_RESULT_NEED_RESET`, 전혀 복구하지 않으려면 `PCI_ERS_RESULT_DISCONNECT`를 반환합니다.

Segment 또는 slot의 모든 driver가 `CAN_RECOVER`를 반환하면 platform이 slot I/O를 다시 enable하고 STEP 2로 갑니다. 하나라도 `NEED_RESET`이면 STEP 4로 가며, platform이 slot을 복구할 수 없으면 STEP 6으로 갑니다.

error_detected() 결과 분기
모두 CAN_RECOVERI/O 재활성화STEP 2 MMIO Enable
하나라도 NEED_RESETSTEP 4 Slot Reset
Platform 복구 불가STEP 6 Permanent Failure

Multi-function device의 모든 driver 응답을 합산합니다.

STEP 1: Notification
--------------------
Platform calls the error_detected() callback on every instance of
every driver affected by the error.

At this point, the device might not be accessible anymore, depending on
the platform (the slot will be isolated on powerpc). The driver may
already have "noticed" the error because of a failing I/O, but this
is the proper "synchronization point", that is, it gives the driver
a chance to cleanup, waiting for pending stuff (timers, whatever, etc...)
to complete; it can take semaphores, schedule, etc... everything but
touch the device. Within this function and after it returns, the driver
shouldn't do any new IOs. Called in task context. This is sort of a
"quiesce" point. See note about interrupts at the end of this doc.

All drivers participating in this system must implement this call.
The driver must return one of the following result codes:

  - PCI_ERS_RESULT_RECOVERED
      Driver returns this if it thinks the device is usable despite
      the error and does not need further intervention.
  - PCI_ERS_RESULT_CAN_RECOVER
      Driver returns this if it thinks it might be able to recover
      the HW by just banging IOs or if it wants to be given
      a chance to extract some diagnostic information (see
      mmio_enable, below).
  - PCI_ERS_RESULT_NEED_RESET
      Driver returns this if it can't recover without a
      slot reset.
  - PCI_ERS_RESULT_DISCONNECT
      Driver returns this if it doesn't want to recover at all.

The next step taken will depend on the result codes returned by the
drivers.

If all drivers on the segment/slot return PCI_ERS_RESULT_CAN_RECOVER,
then the platform should re-enable IOs on the slot (or do nothing in
particular, if the platform doesn't isolate slots), and recovery
proceeds to STEP 2 (MMIO Enable).

If any driver requested a slot reset (by returning PCI_ERS_RESULT_NEED_RESET),
then recovery proceeds to STEP 4 (Slot Reset).

If the platform is unable to recover the slot, the next step
is STEP 6 (Permanent Failure).

STEP 1 PowerPC 구현 주의

176-194

현재 PowerPC 구현은 이 routine에서 driver가 schedule하거나 semaphore를 사용하지 않는다고 가정합니다. Kernel thread 하나가 모든 device에 알리므로 device 하나가 sleep하거나 schedule하면 모두가 영향을 받습니다.

더 나은 구현은 모든 notification thread가 합류할 때까지 기다리는 복잡한 multi-thread logic이 필요하며, 문서는 이를 지나치게 복잡하고 구현 가치가 낮다고 봅니다.

현재 PowerPC에서는 이 시점의 I/O 여부를 크게 신경 쓰지 않습니다. Read는 `0xff`, write는 drop됩니다. Frozen adapter에 `EEH_MAX_FAILS`보다 많은 I/O를 시도하면 EEH가 driver의 infinite loop로 판단해 syslog에 error를 출력하며, device를 다시 동작시키려면 reboot해야 합니다.

.. note::

   The current powerpc implementation assumes that a device driver will
   *not* schedule or semaphore in this routine; the current powerpc
   implementation uses one kernel thread to notify all devices;
   thus, if one device sleeps/schedules, all devices are affected.
   Doing better requires complex multi-threaded logic in the error
   recovery implementation (e.g. waiting for all notification threads
   to "join" before proceeding with recovery.)  This seems excessively
   complex and not worth implementing.

   The current powerpc implementation doesn't much care if the device
   attempts I/O at this point, or not.  I/Os will fail, returning
   a value of 0xff on read, and writes will be dropped. If more than
   EEH_MAX_FAILS I/Os are attempted to a frozen adapter, EEH
   assumes that the device driver has gone into an infinite loop
   and prints an error to syslog.  A reboot is then required to
   get the device working again.

STEP 2: MMIO enabled

195-228

Platform은 device의 MMIO를 다시 enable하지만 보통 DMA는 enable하지 않고, 영향받은 모든 driver에 `mmio_enabled()`를 호출합니다.

이는 early recovery callback입니다. 제한된 I/O는 가능하지만 DMA는 불가능합니다. 정상 동작을 시작하는 지점이 아니라 device를 peek/poke하고 diagnostic 정보를 추출하거나 device-local reset을 trigger하는 데 사용합니다.

Segment의 모든 driver가 복구를 시도할 수 있다고 합의했고 hardware가 자동 link reset을 하지 않았을 때 호출합니다. Slot 또는 link reset 없이 I/O만 enable할 수 없다면 이 callback을 건너뛰고 STEP 3 또는 STEP 4로 갑니다.

AER platform에서는 STEP 1부터 faulting device가 접근 가능할 수 있지만 PowerPC EEH와 s390 호환성을 위해 STEP 2까지 접근을 미뤄야 합니다.

DPC platform은 STEP 3에서 sub-hierarchy link를 다시 enable하므로 device는 STEP 4까지 접근할 수 없습니다. Surprise Down 같은 error에서는 STEP 4에도 device가 접근 불가능할 수 있으며, read가 모두 1인지 `PCI_POSSIBLE_ERROR()`로 확인할 수 있습니다.

복구 단계별 접근성
환경접근 가능 시점
PowerPC EEH / s390STEP 2부터
AERSTEP 1일 수도 있으나 STEP 2까지 미루는 것이 안전
DPCLink가 STEP 3에 복구되고 device는 STEP 4까지 불가
Surprise DownSTEP 4에도 불가할 수 있어 PCI_POSSIBLE_ERROR() 확인

Platform 기능에 따라 device 접근 가능 시점이 다릅니다.

STEP 2: MMIO Enabled
--------------------
The platform re-enables MMIO to the device (but typically not the
DMA), and then calls the mmio_enabled() callback on all affected
device drivers.

This is the "early recovery" call. IOs are allowed again, but DMA is
not, with some restrictions. This is NOT a callback for the driver to
start operations again, only to peek/poke at the device, extract diagnostic
information, if any, and eventually do things like trigger a device local
reset or some such, but not restart operations. This callback is made if
all drivers on a segment agree that they can try to recover and if no automatic
link reset was performed by the HW. If the platform can't just re-enable IOs
without a slot reset or a link reset, it will not call this callback, and
instead will have gone directly to STEP 3 (Link Reset) or STEP 4 (Slot Reset).

.. note::

   On platforms supporting Advanced Error Reporting (PCIe r7.0 sec 6.2),
   the faulting device may already be accessible in STEP 1 (Notification).
   Drivers should nevertheless defer accesses to STEP 2 (MMIO Enabled)
   to be compatible with EEH on powerpc and with s390 (where devices are
   inaccessible until STEP 2).

   On platforms supporting Downstream Port Containment, the link to the
   sub-hierarchy with the faulting device is re-enabled in STEP 3 (Link
   Reset). Hence devices in the sub-hierarchy are inaccessible until
   STEP 4 (Slot Reset).

   For errors such as Surprise Down (PCIe r7.0 sec 6.2.7), the device
   may not even be accessible in STEP 4 (Slot Reset). Drivers can detect
   accessibility by checking whether reads from the device return all 1's
   (PCI_POSSIBLE_ERROR()).

STEP 2 제안과 결과 분기

229-266

아직 어떤 platform도 구현하지 않은 제안으로, 이 callback의 모든 I/O를 synchronous하게 수행하고 error는 일반 `pci_check_whatever()` API로 반환하며 여기서 생긴 error 때문에 새 `error_detected()`를 호출하지 않도록 합니다.

그러나 이런 error가 segment 전체 I/O를 다시 차단하면 같은 segment의 다른 device가 수행한 복구를 무효화할 수 있으므로 전체 segment가 link reset 또는 slot reset으로 넘어가야 할 수 있습니다.

Driver가 완전히 동작 가능하고 정상 operation을 시작할 준비가 됐다고 판단하면 `RECOVERED`를 반환합니다. 같은 segment의 다른 driver 실패가 slot reset을 일으킬 수 있어 실제 진행은 보장되지 않습니다.

현재 상태에서 복구할 수 없어 slot reset이 필요하면 `NEED_RESET`, reset 뒤에도 복구할 수 없는 완전 실패라면 `DISCONNECT`를 반환합니다.

모든 driver가 `RECOVERED`이면 STEP 3 Link Reset 또는 STEP 5 Resume Operations로 갑니다. 하나라도 `NEED_RESET`이면 STEP 4 Slot Reset으로 갑니다.

.. note::

   The following is proposed; no platform implements this yet:
   Proposal: All I/Os should be done _synchronously_ from within
   this callback, errors triggered by them will be returned via
   the normal pci_check_whatever() API, no new error_detected()
   callback will be issued due to an error happening here. However,
   such an error might cause IOs to be re-blocked for the whole
   segment, and thus invalidate the recovery that other devices
   on the same segment might have done, forcing the whole segment
   into one of the next states, that is, link reset or slot reset.

The driver should return one of the following result codes:
  - PCI_ERS_RESULT_RECOVERED
      Driver returns this if it thinks the device is fully
      functional and thinks it is ready to start
      normal driver operations again. There is no
      guarantee that the driver will actually be
      allowed to proceed, as another driver on the
      same segment might have failed and thus triggered a
      slot reset on platforms that support it.

  - PCI_ERS_RESULT_NEED_RESET
      Driver returns this if it thinks the device is not
      recoverable in its current state and it needs a slot
      reset to proceed.

  - PCI_ERS_RESULT_DISCONNECT
      Same as above. Total failure, no recovery even after
      reset driver dead. (To be defined more precisely)

The next step taken depends on the results returned by the drivers.
If all drivers returned PCI_ERS_RESULT_RECOVERED, then the platform
proceeds to either STEP 3 (Link Reset) or to STEP 5 (Resume Operations).

If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform
proceeds to STEP 4 (Slot Reset)

STEP 3: Link reset

267-272

Platform이 link를 reset합니다. 이는 PCIe 전용 단계이며 link reset으로 해결할 수 있는 fatal error를 감지했을 때 수행합니다.

STEP 3: Link Reset
------------------
The platform resets the link.  This is a PCIe specific step
and is done whenever a fatal error has been detected that can be
"solved" by resetting the link.

STEP 4: Slot reset 방식

273-308

`PCI_ERS_RESULT_NEED_RESET` 응답을 받으면 platform은 요청한 PCI device의 slot reset을 수행합니다. 실제 절차는 platform에 따라 다르며 완료 후 `slot_reset()` callback을 호출합니다.

PowerPC는 기본 soft reset과 선택적 fundamental reset의 두 수준을 구현합니다.

Soft reset은 adapter `#RST` line을 assert한 뒤 PCI BAR와 configuration header를 새 power-on 및 BIOS/system firmware 초기화 직후와 같은 상태로 복원합니다. Hot reset이라고도 합니다.

PCIe card에서만 지원하는 fundamental reset은 device state machine, hardware logic, port state, configuration register를 기본 상태로 초기화합니다.

대부분의 PCI device는 soft reset으로 충분하며, 선택적 fundamental reset은 soft reset으로 복구되지 않는 일부 PCIe device용입니다. PCI hotplug를 지원하면 slot 전원을 껐다 켜 reset할 수도 있습니다.

Platform은 PCI config space를 마지막 상태가 아니라 fresh power-on 상태로 복원해야 합니다. Driver가 표준 초기화 routine을 다시 사용하므로 비정상 config 상태는 device hang, kernel panic, silent data corruption을 일으킬 수 있습니다.

Slot reset 유형
유형동작용도
Soft / hot reset#RST와 BAR·config header 복원대부분의 PCI device
Fundamental resetState machine·logic·port·register 기본화Soft reset이 부족한 PCIe device
Power cycleSlot 전원 off/onPCI hotplug platform

PowerPC와 hotplug platform의 reset 강도를 비교합니다.

STEP 4: Slot Reset
------------------

In response to a return value of PCI_ERS_RESULT_NEED_RESET, the
platform will perform a slot reset on the requesting PCI device(s).
The actual steps taken by a platform to perform a slot reset
will be platform-dependent. Upon completion of slot reset, the
platform will call the device slot_reset() callback.

Powerpc platforms implement two levels of slot reset:
soft reset(default) and fundamental(optional) reset.

Powerpc soft reset consists of asserting the adapter #RST line and then
restoring the PCI BARs and PCI configuration header to a state
that is equivalent to what it would be after a fresh system
power-on followed by power-on BIOS/system firmware initialization.
Soft reset is also known as hot-reset.

Powerpc fundamental reset is supported by PCIe cards only
and results in device's state machines, hardware logic, port states and
configuration registers to initialize to their default conditions.

For most PCI devices, a soft reset will be sufficient for recovery.
Optional fundamental reset is provided to support a limited number
of PCIe devices for which a soft reset is not sufficient
for recovery.

If the platform supports PCI hotplug, then the reset might be
performed by toggling the slot electrical power off/on.

It is important for the platform to restore the PCI config space
to the "fresh poweron" state, rather than the "last state". After
a slot reset, the device driver will almost always use its standard
device initialization routines, and an unusual config space setup
may result in hung devices, kernel panics, or silent data corruption.

slot_reset() callback과 multi-function 조정

309-336

`slot_reset()`은 driver가 firmware를 다시 내려받는 등 hardware를 재초기화할 기회를 줍니다. Card는 fresh state이며 완전히 동작한다고 가정할 수 있습니다.

Slot은 unfrozen이고 PCI config space, MMIO, DMA에 완전히 접근할 수 있으며 Legacy·MSI·MSI-X interrupt도 사용할 수 있습니다.

이 시점에는 정상 I/O를 다시 시작하면 안 됩니다. 모든 driver가 callback 성공을 보고하면 platform이 `resume()`을 호출해 정상 I/O 재개를 허용합니다.

Reset 뒤에도 device를 동작시킬 수 없다면 critical failure를 반환할 수 있습니다. Soft reset을 이미 시도했다면 hard reset(power cycle) 뒤 `slot_reset()`을 다시 부를 수 있습니다. 그래도 실패하면 permanent failure이며 device는 dead로 간주합니다.

Multi-function card의 driver들은 one-shot 또는 global device initialization을 어느 instance가 수행할지 조정해야 합니다. Symbios `sym53cxx2` driver는 PCI function 0에서만 device를 초기화합니다.

if (PCI_FUNC(pdev->devfn) == 0)
        sym_reset_scsi_bus(np, 0);
This call gives drivers the chance to re-initialize the hardware
(re-download firmware, etc.).  At this point, the driver may assume
that the card is in a fresh state and is fully functional. The slot
is unfrozen and the driver has full access to PCI config space,
memory mapped I/O space and DMA. Interrupts (Legacy, MSI, or MSI-X)
will also be available.

Drivers should not restart normal I/O processing operations
at this point.  If all device drivers report success on this
callback, the platform will call resume() to complete the sequence,
and let the driver restart normal I/O processing.

A driver can still return a critical failure for this function if
it can't get the device operational after reset.  If the platform
previously tried a soft reset, it might now try a hard reset (power
cycle) and then call slot_reset() again.  If the device still can't
be recovered, there is nothing more that can be done;  the platform
will typically report a "permanent failure" in such a case.  The
device will be considered "dead" in this case.

Drivers for multi-function cards will need to coordinate among
themselves as to which driver instance will perform any "one-shot"
or global device initialization. For example, the Symbios sym53cxx2
driver performs device init only from PCI function 0::

        +       if (PCI_FUNC(pdev->devfn) == 0)
        +               sym_reset_scsi_bus(np, 0);

Fundamental reset 요청과 다음 단계

337-360

`slot_reset()`의 실패 결과는 `PCI_ERS_RESULT_DISCONNECT`입니다.

Fundamental reset이 필요한 PCIe card driver는 probe function에서 `struct pci_dev`의 `needs_freset` bit를 설정해야 합니다. QLogic `qla2xxx`는 특정 card type에 이를 설정합니다.

if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha))
        pdev->needs_freset = 1;

Platform은 STEP 5 Resume Operations 또는 STEP 6 Permanent Failure로 갑니다.

현재 PowerPC 구현은 driver가 `PCI_ERS_RESULT_DISCONNECT`를 반환하면 power-cycle reset을 시도하지 않지만, 문서는 시도하는 편이 맞을 수 있다고 지적합니다.

Result codes:
        - PCI_ERS_RESULT_DISCONNECT
          Same as above.

Drivers for PCIe cards that require a fundamental reset must
set the needs_freset bit in the pci_dev structure in their probe function.
For example, the QLogic qla2xxx driver sets the needs_freset bit for certain
PCI card types::

        +        /* Set EEH reset type to fundamental if required by hba  */
        +        if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha))
        +                pdev->needs_freset = 1;
        +

Platform proceeds either to STEP 5 (Resume Operations) or STEP 6 (Permanent
Failure).

.. note::

   The current powerpc implementation does not try a power-cycle
   reset if the driver returned PCI_ERS_RESULT_DISCONNECT.
   However, it probably should.

STEP 5: Resume operations

361-372

앞선 세 callback 중 하나에서 segment의 모든 driver가 `PCI_ERS_RESULT_RECOVERED`를 반환하면 platform은 영향받은 모든 driver에 `resume()`을 호출합니다.

이 callback은 모든 것이 다시 동작함을 알려 driver가 activity를 재개하게 하며 result code를 반환하지 않습니다.

이후 새 error가 발생하면 platform은 새로운 error recovery sequence를 처음부터 시작합니다.

STEP 5: Resume Operations
-------------------------
The platform will call the resume() callback on all affected device
drivers if all drivers on the segment have returned
PCI_ERS_RESULT_RECOVERED from one of the 3 previous callbacks.
The goal of this callback is to tell the driver to restart activity,
that everything is back and running. This callback does not return
a result code.

At this point, if a new error happens, the platform will restart
a new error recovery sequence.

STEP 6: Permanent failure

373-396

Platform이 device를 복구할 수 없는 permanent failure에서는 `pci_channel_io_perm_failure` state로 `error_detected()`를 호출합니다.

Driver는 최악을 가정해 pending I/O를 모두 취소하고 새 I/O를 거부하며 상위 layer에 `-EIO`를 반환해야 합니다. System shutdown 때처럼 memory를 정리하고 kernel operation에서 자신을 제거합니다.

Platform은 보통 system operator에게 permanent failure를 알립니다. Hotplug device라면 제거·교체할 수 있습니다.

모든 failure가 실제로 영구적인 것은 아닙니다. Overheating이나 card 접촉 불량일 수 있고, 잘못된 address DMA 또는 programming error로 생긴 bogus split transaction 같은 software bug가 많은 PCI error event를 일으킵니다.

실제 software error 원인 경험은 `Documentation/arch/powerpc/eeh-pci-error-recovery.rst`를 참조하십시오.

STEP 6: Permanent Failure
-------------------------
A "permanent failure" has occurred, and the platform cannot recover
the device.  The platform will call error_detected() with a
pci_channel_state_t value of pci_channel_io_perm_failure.

The device driver should, at this point, assume the worst. It should
cancel all pending I/O, refuse all new I/O, returning -EIO to
higher layers. The device driver should then clean up all of its
memory and remove itself from kernel operations, much as it would
during system shutdown.

The platform will typically notify the system operator of the
permanent failure in some way.  If the device is hotplug-capable,
the operator will probably want to remove and replace the device.
Note, however, not all failures are truly "permanent". Some are
caused by over-heating, some by a poorly seated card. Many
PCI error events are caused by software bugs, e.g. DMAs to
wild addresses or bogus split transactions due to programming
errors. See the discussion in Documentation/arch/powerpc/eeh-pci-error-recovery.rst
for additional detail on real-life experience of the causes of
software errors.

Platform policy

397-404

Callback 호출 방식은 platform policy입니다. Slot reset capability가 없는 platform은 복구하지 못하는 driver를 disconnect하고 같은 segment의 다른 card 복구를 시도할 수 있습니다.

다만 실제 환경에서는 segment당 driver가 하나뿐인 경우가 대부분입니다.

Conclusion; General Remarks
---------------------------
The way the callbacks are called is platform policy. A platform with
no slot reset capability may want to just "ignore" drivers that can't
recover (disconnect them) and try to let other cards on the same segment
recover. Keep in mind that in most real life cases, though, there will
be only one driver per segment.

Error 처리 중 interrupt 규칙

405-428

Device가 죽거나 isolate된 상태에서 interrupt가 들어오는 문제도 platform policy로 다룹니다. Recovery API는 두 가지만 요구합니다.

첫째, error detection부터 `slot_reset()` 호출 전까지 segment의 어느 device에서도 interrupt delivery가 계속된다는 보장은 없습니다. `slot_reset()` 시점에는 interrupt가 완전히 동작해야 합니다.

둘째, interrupt delivery가 중단된다는 보장도 없습니다. Error 감지 뒤 interrupt를 받거나 handler 안에서 error 때문에 interrupt source를 제대로 acknowledge하지 못한 driver는 `IRQ_NOTHANDLED`를 반환해야 합니다.

Platform은 보통 error 처리 동안 IRQ source를 mask합니다. Error-management 가능 slot으로 route되는 interrupt를 알고 해당 IRQ number를 임시 disable할 수 있어야 합니다.

Interrupt를 공유하는 다른 device에는 IRQ latency가 생기지만 다른 방법이 없습니다. High-end platform은 원래 많은 device가 interrupt를 공유하지 않아야 합니다.

Interrupt 보장
항목보장
전달 지속보장 없음
전달 중단보장 없음
slot_reset() 이후완전 동작 기대
Ack 불가 interruptIRQ_NOTHANDLED 반환
Platform 조치Error 처리 동안 IRQ source mask

Error detection과 slot reset 사이에 driver가 가정할 수 있는 범위입니다.

Now, a note about interrupts. If you get an interrupt and your
device is dead or has been isolated, there is a problem :)
The current policy is to turn this into a platform policy.
That is, the recovery API only requires that:

 - There is no guarantee that interrupt delivery can proceed from any
   device on the segment starting from the error detection and until the
   slot_reset callback is called, at which point interrupts are expected
   to be fully operational.

 - There is no guarantee that interrupt delivery is stopped, that is,
   a driver that gets an interrupt after detecting an error, or that detects
   an error within the interrupt handler such that it prevents proper
   ack'ing of the interrupt (and thus removal of the source) should just
   return IRQ_NOTHANDLED. It's up to the platform to deal with that
   condition, typically by masking the IRQ source during the duration of
   the error handling. It is expected that the platform "knows" which
   interrupts are routed to error-management capable slots and can deal
   with temporarily disabling that IRQ number during error processing (this
   isn't terribly complex). That means some IRQ latency for other devices
   sharing the interrupt, but there is simply no other way. High end
   platforms aren't supposed to share interrupts between many devices
   anyway :)

PowerPC 상세와 driver 예

429-449

PowerPC platform 구현 상세는 `Documentation/arch/powerpc/eeh-pci-error-recovery.rst`에 있습니다.

작성 당시 error recovery patch가 있는 driver 목록은 아래와 같으며 모두 mainline에 들어간 것은 아닙니다. 구현 예로 사용할 수 있습니다.

Error recovery driver 예
SubsystemDriver path
SCSIdrivers/scsi/ipr
SCSIdrivers/scsi/sym53c8xx_2
SCSIdrivers/scsi/qla2xxx
SCSIdrivers/scsi/lpfc
Networkdrivers/next/bnx2.c
Networkdrivers/next/e100.c
Networkdrivers/net/e1000
Networkdrivers/net/e1000e
Networkdrivers/net/ixgbe
Networkdrivers/net/cxgb3
Networkdrivers/net/s2io.c

원문 source path를 그대로 보존합니다.

.. note::

   Implementation details for the powerpc platform are discussed in
   the file Documentation/arch/powerpc/eeh-pci-error-recovery.rst

   As of this writing, there is a growing list of device drivers with
   patches implementing error recovery. Not all of these patches are in
   mainline yet. These may be used as "examples":

   - drivers/scsi/ipr
   - drivers/scsi/sym53c8xx_2
   - drivers/scsi/qla2xxx
   - drivers/scsi/lpfc
   - drivers/next/bnx2.c
   - drivers/next/e100.c
   - drivers/net/e1000
   - drivers/net/e1000e
   - drivers/net/ixgbe
   - drivers/net/cxgb3
   - drivers/net/s2io.c

Correctable error callback

450-457

Error severity가 `correctable`이면 `handle_error_source()`가 선택적 `cor_error_detected()` callback을 호출합니다. Driver가 원하면 추가 logging을 수행할 수 있습니다.

구현 예는 `drivers/cxl/pci.c`입니다. 이것으로 문서를 마칩니다.

   The cor_error_detected() callback is invoked in handle_error_source() when
   the error severity is "correctable". The callback is optional and allows
   additional logging to be done if desired. See example:

   - drivers/cxl/pci.c

The End
-------