diff options
author | Huang Ying <ying.huang@intel.com> | 2011-02-21 00:54:43 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2011-03-21 22:59:08 -0400 |
commit | c413d7682020a127f54744a1b30f597692aea1fd (patch) | |
tree | b495af23b2f81b6ab0080925aa988ea9a8068e4e | |
parent | b64a44146540a4761bb1cf8047fffd9dbf0c3090 (diff) |
ACPI, APEI, Add PCIe AER error information printing support
The AER error information printing support is implemented in
drivers/pci/pcie/aer/aer_print.c. So some string constants, functions
and macros definitions can be re-used without being exported.
The original PCIe AER error information printing function is not
re-used directly because the overall format is quite different. And
changing the original printing format may make some original users'
scripts broken.
Signed-off-by: Huang Ying <ying.huang@intel.com>
CC: Jesse Barnes <jbarnes@virtuousgeek.org>
CC: Zhang Yanmin <yanmin.zhang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | Documentation/acpi/apei/output_format.txt | 25 | ||||
-rw-r--r-- | drivers/acpi/apei/Kconfig | 7 | ||||
-rw-r--r-- | drivers/acpi/apei/cper.c | 18 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv.h | 9 | ||||
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_errprint.c | 59 | ||||
-rw-r--r-- | include/linux/aer.h | 24 | ||||
-rw-r--r-- | include/linux/cper.h | 2 |
7 files changed, 132 insertions, 12 deletions
diff --git a/Documentation/acpi/apei/output_format.txt b/Documentation/acpi/apei/output_format.txt index 9146952c612a..0c49c197c47a 100644 --- a/Documentation/acpi/apei/output_format.txt +++ b/Documentation/acpi/apei/output_format.txt | |||
@@ -92,6 +92,11 @@ vendor_id: <integer>, device_id: <integer> | |||
92 | class_code: <integer>] | 92 | class_code: <integer>] |
93 | [serial number: <integer>, <integer>] | 93 | [serial number: <integer>, <integer>] |
94 | [bridge: secondary_status: <integer>, control: <integer>] | 94 | [bridge: secondary_status: <integer>, control: <integer>] |
95 | [aer_status: <integer>, aer_mask: <integer> | ||
96 | <aer status string> | ||
97 | [aer_uncor_severity: <integer>] | ||
98 | aer_layer=<aer layer string>, aer_agent=<aer agent string> | ||
99 | aer_tlp_header: <integer> <integer> <integer> <integer>] | ||
95 | 100 | ||
96 | <pcie port type string>* := PCIe end point | legacy PCI end point | \ | 101 | <pcie port type string>* := PCIe end point | legacy PCI end point | \ |
97 | unknown | unknown | root port | upstream switch port | \ | 102 | unknown | unknown | root port | upstream switch port | \ |
@@ -99,6 +104,26 @@ downstream switch port | PCIe to PCI/PCI-X bridge | \ | |||
99 | PCI/PCI-X to PCIe bridge | root complex integrated endpoint device | \ | 104 | PCI/PCI-X to PCIe bridge | root complex integrated endpoint device | \ |
100 | root complex event collector | 105 | root complex event collector |
101 | 106 | ||
107 | if section severity is fatal or recoverable | ||
108 | <aer status string># := | ||
109 | unknown | unknown | unknown | unknown | Data Link Protocol | \ | ||
110 | unknown | unknown | unknown | unknown | unknown | unknown | unknown | \ | ||
111 | Poisoned TLP | Flow Control Protocol | Completion Timeout | \ | ||
112 | Completer Abort | Unexpected Completion | Receiver Overflow | \ | ||
113 | Malformed TLP | ECRC | Unsupported Request | ||
114 | else | ||
115 | <aer status string># := | ||
116 | Receiver Error | unknown | unknown | unknown | unknown | unknown | \ | ||
117 | Bad TLP | Bad DLLP | RELAY_NUM Rollover | unknown | unknown | unknown | \ | ||
118 | Replay Timer Timeout | Advisory Non-Fatal | ||
119 | fi | ||
120 | |||
121 | <aer layer string> := | ||
122 | Physical Layer | Data Link Layer | Transaction Layer | ||
123 | |||
124 | <aer agent string> := | ||
125 | Receiver ID | Requester ID | Completer ID | Transmitter ID | ||
126 | |||
102 | Where, [] designate corresponding content is optional | 127 | Where, [] designate corresponding content is optional |
103 | 128 | ||
104 | All <field string> description with * has the following format: | 129 | All <field string> description with * has the following format: |
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index fca34ccfd294..9ecf6feae830 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig | |||
@@ -21,6 +21,13 @@ config ACPI_APEI_GHES | |||
21 | by firmware to produce more valuable hardware error | 21 | by firmware to produce more valuable hardware error |
22 | information for Linux. | 22 | information for Linux. |
23 | 23 | ||
24 | config ACPI_APEI_PCIEAER | ||
25 | bool "APEI PCIe AER logging/recovering support" | ||
26 | depends on ACPI_APEI && PCIEAER | ||
27 | help | ||
28 | PCIe AER errors may be reported via APEI firmware first mode. | ||
29 | Turn on this option to enable the corresponding support. | ||
30 | |||
24 | config ACPI_APEI_EINJ | 31 | config ACPI_APEI_EINJ |
25 | tristate "APEI Error INJection (EINJ)" | 32 | tristate "APEI Error INJection (EINJ)" |
26 | depends on ACPI_APEI && DEBUG_FS | 33 | depends on ACPI_APEI && DEBUG_FS |
diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 31464a006d76..5d4189464d63 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/time.h> | 29 | #include <linux/time.h> |
30 | #include <linux/cper.h> | 30 | #include <linux/cper.h> |
31 | #include <linux/acpi.h> | 31 | #include <linux/acpi.h> |
32 | #include <linux/aer.h> | ||
32 | 33 | ||
33 | /* | 34 | /* |
34 | * CPER record ID need to be unique even after reboot, because record | 35 | * CPER record ID need to be unique even after reboot, because record |
@@ -70,8 +71,8 @@ static const char *cper_severity_str(unsigned int severity) | |||
70 | * If the output length is longer than 80, multiple line will be | 71 | * If the output length is longer than 80, multiple line will be |
71 | * printed, with @pfx is printed at the beginning of each line. | 72 | * printed, with @pfx is printed at the beginning of each line. |
72 | */ | 73 | */ |
73 | static void cper_print_bits(const char *pfx, unsigned int bits, | 74 | void cper_print_bits(const char *pfx, unsigned int bits, |
74 | const char *strs[], unsigned int strs_size) | 75 | const char *strs[], unsigned int strs_size) |
75 | { | 76 | { |
76 | int i, len = 0; | 77 | int i, len = 0; |
77 | const char *str; | 78 | const char *str; |
@@ -81,6 +82,8 @@ static void cper_print_bits(const char *pfx, unsigned int bits, | |||
81 | if (!(bits & (1U << i))) | 82 | if (!(bits & (1U << i))) |
82 | continue; | 83 | continue; |
83 | str = strs[i]; | 84 | str = strs[i]; |
85 | if (!str) | ||
86 | continue; | ||
84 | if (len && len + strlen(str) + 2 > 80) { | 87 | if (len && len + strlen(str) + 2 > 80) { |
85 | printk("%s\n", buf); | 88 | printk("%s\n", buf); |
86 | len = 0; | 89 | len = 0; |
@@ -243,7 +246,8 @@ static const char *cper_pcie_port_type_strs[] = { | |||
243 | "root complex event collector", | 246 | "root complex event collector", |
244 | }; | 247 | }; |
245 | 248 | ||
246 | static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie) | 249 | static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, |
250 | const struct acpi_hest_generic_data *gdata) | ||
247 | { | 251 | { |
248 | if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) | 252 | if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) |
249 | printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, | 253 | printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, |
@@ -276,6 +280,12 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie) | |||
276 | printk( | 280 | printk( |
277 | "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", | 281 | "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", |
278 | pfx, pcie->bridge.secondary_status, pcie->bridge.control); | 282 | pfx, pcie->bridge.secondary_status, pcie->bridge.control); |
283 | #ifdef CONFIG_ACPI_APEI_PCIEAER | ||
284 | if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) { | ||
285 | struct aer_capability_regs *aer_regs = (void *)pcie->aer_info; | ||
286 | cper_print_aer(pfx, gdata->error_severity, aer_regs); | ||
287 | } | ||
288 | #endif | ||
279 | } | 289 | } |
280 | 290 | ||
281 | static const char *apei_estatus_section_flag_strs[] = { | 291 | static const char *apei_estatus_section_flag_strs[] = { |
@@ -322,7 +332,7 @@ static void apei_estatus_print_section( | |||
322 | struct cper_sec_pcie *pcie = (void *)(gdata + 1); | 332 | struct cper_sec_pcie *pcie = (void *)(gdata + 1); |
323 | printk("%s""section_type: PCIe error\n", pfx); | 333 | printk("%s""section_type: PCIe error\n", pfx); |
324 | if (gdata->error_data_length >= sizeof(*pcie)) | 334 | if (gdata->error_data_length >= sizeof(*pcie)) |
325 | cper_print_pcie(pfx, pcie); | 335 | cper_print_pcie(pfx, pcie, gdata); |
326 | else | 336 | else |
327 | goto err_section_too_small; | 337 | goto err_section_too_small; |
328 | } else | 338 | } else |
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 80c11d131499..3eb77080366a 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h | |||
@@ -35,13 +35,6 @@ | |||
35 | PCI_ERR_UNC_UNX_COMP| \ | 35 | PCI_ERR_UNC_UNX_COMP| \ |
36 | PCI_ERR_UNC_MALF_TLP) | 36 | PCI_ERR_UNC_MALF_TLP) |
37 | 37 | ||
38 | struct header_log_regs { | ||
39 | unsigned int dw0; | ||
40 | unsigned int dw1; | ||
41 | unsigned int dw2; | ||
42 | unsigned int dw3; | ||
43 | }; | ||
44 | |||
45 | #define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ | 38 | #define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ |
46 | struct aer_err_info { | 39 | struct aer_err_info { |
47 | struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; | 40 | struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; |
@@ -59,7 +52,7 @@ struct aer_err_info { | |||
59 | 52 | ||
60 | unsigned int status; /* COR/UNCOR Error Status */ | 53 | unsigned int status; /* COR/UNCOR Error Status */ |
61 | unsigned int mask; /* COR/UNCOR Error Mask */ | 54 | unsigned int mask; /* COR/UNCOR Error Mask */ |
62 | struct header_log_regs tlp; /* TLP Header */ | 55 | struct aer_header_log_regs tlp; /* TLP Header */ |
63 | }; | 56 | }; |
64 | 57 | ||
65 | struct aer_err_source { | 58 | struct aer_err_source { |
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 7a237f67129e..b07a42e0b350 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/errno.h> | 19 | #include <linux/errno.h> |
20 | #include <linux/pm.h> | 20 | #include <linux/pm.h> |
21 | #include <linux/suspend.h> | 21 | #include <linux/suspend.h> |
22 | #include <linux/cper.h> | ||
22 | 23 | ||
23 | #include "aerdrv.h" | 24 | #include "aerdrv.h" |
24 | 25 | ||
@@ -201,3 +202,61 @@ void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) | |||
201 | info->multi_error_valid ? "Multiple " : "", | 202 | info->multi_error_valid ? "Multiple " : "", |
202 | aer_error_severity_string[info->severity], info->id); | 203 | aer_error_severity_string[info->severity], info->id); |
203 | } | 204 | } |
205 | |||
206 | #ifdef CONFIG_ACPI_APEI_PCIEAER | ||
207 | static int cper_severity_to_aer(int cper_severity) | ||
208 | { | ||
209 | switch (cper_severity) { | ||
210 | case CPER_SEV_RECOVERABLE: | ||
211 | return AER_NONFATAL; | ||
212 | case CPER_SEV_FATAL: | ||
213 | return AER_FATAL; | ||
214 | default: | ||
215 | return AER_CORRECTABLE; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | void cper_print_aer(const char *prefix, int cper_severity, | ||
220 | struct aer_capability_regs *aer) | ||
221 | { | ||
222 | int aer_severity, layer, agent, status_strs_size, tlp_header_valid = 0; | ||
223 | u32 status, mask; | ||
224 | const char **status_strs; | ||
225 | |||
226 | aer_severity = cper_severity_to_aer(cper_severity); | ||
227 | if (aer_severity == AER_CORRECTABLE) { | ||
228 | status = aer->cor_status; | ||
229 | mask = aer->cor_mask; | ||
230 | status_strs = aer_correctable_error_string; | ||
231 | status_strs_size = ARRAY_SIZE(aer_correctable_error_string); | ||
232 | } else { | ||
233 | status = aer->uncor_status; | ||
234 | mask = aer->uncor_mask; | ||
235 | status_strs = aer_uncorrectable_error_string; | ||
236 | status_strs_size = ARRAY_SIZE(aer_uncorrectable_error_string); | ||
237 | tlp_header_valid = status & AER_LOG_TLP_MASKS; | ||
238 | } | ||
239 | layer = AER_GET_LAYER_ERROR(aer_severity, status); | ||
240 | agent = AER_GET_AGENT(aer_severity, status); | ||
241 | printk("%s""aer_status: 0x%08x, aer_mask: 0x%08x\n", | ||
242 | prefix, status, mask); | ||
243 | cper_print_bits(prefix, status, status_strs, status_strs_size); | ||
244 | printk("%s""aer_layer=%s, aer_agent=%s\n", prefix, | ||
245 | aer_error_layer[layer], aer_agent_string[agent]); | ||
246 | if (aer_severity != AER_CORRECTABLE) | ||
247 | printk("%s""aer_uncor_severity: 0x%08x\n", | ||
248 | prefix, aer->uncor_severity); | ||
249 | if (tlp_header_valid) { | ||
250 | const unsigned char *tlp; | ||
251 | tlp = (const unsigned char *)&aer->header_log; | ||
252 | printk("%s""aer_tlp_header:" | ||
253 | " %02x%02x%02x%02x %02x%02x%02x%02x" | ||
254 | " %02x%02x%02x%02x %02x%02x%02x%02x\n", | ||
255 | prefix, *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, | ||
256 | *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), | ||
257 | *(tlp + 11), *(tlp + 10), *(tlp + 9), | ||
258 | *(tlp + 8), *(tlp + 15), *(tlp + 14), | ||
259 | *(tlp + 13), *(tlp + 12)); | ||
260 | } | ||
261 | } | ||
262 | #endif | ||
diff --git a/include/linux/aer.h b/include/linux/aer.h index f7df1eefc107..8414de22a779 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h | |||
@@ -7,6 +7,28 @@ | |||
7 | #ifndef _AER_H_ | 7 | #ifndef _AER_H_ |
8 | #define _AER_H_ | 8 | #define _AER_H_ |
9 | 9 | ||
10 | struct aer_header_log_regs { | ||
11 | unsigned int dw0; | ||
12 | unsigned int dw1; | ||
13 | unsigned int dw2; | ||
14 | unsigned int dw3; | ||
15 | }; | ||
16 | |||
17 | struct aer_capability_regs { | ||
18 | u32 header; | ||
19 | u32 uncor_status; | ||
20 | u32 uncor_mask; | ||
21 | u32 uncor_severity; | ||
22 | u32 cor_status; | ||
23 | u32 cor_mask; | ||
24 | u32 cap_control; | ||
25 | struct aer_header_log_regs header_log; | ||
26 | u32 root_command; | ||
27 | u32 root_status; | ||
28 | u16 cor_err_source; | ||
29 | u16 uncor_err_source; | ||
30 | }; | ||
31 | |||
10 | #if defined(CONFIG_PCIEAER) | 32 | #if defined(CONFIG_PCIEAER) |
11 | /* pci-e port driver needs this function to enable aer */ | 33 | /* pci-e port driver needs this function to enable aer */ |
12 | extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); | 34 | extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); |
@@ -27,5 +49,7 @@ static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) | |||
27 | } | 49 | } |
28 | #endif | 50 | #endif |
29 | 51 | ||
52 | extern void cper_print_aer(const char *prefix, int cper_severity, | ||
53 | struct aer_capability_regs *aer); | ||
30 | #endif //_AER_H_ | 54 | #endif //_AER_H_ |
31 | 55 | ||
diff --git a/include/linux/cper.h b/include/linux/cper.h index 3104aaff5dd0..372a25839fd1 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h | |||
@@ -388,5 +388,7 @@ struct cper_sec_pcie { | |||
388 | #pragma pack() | 388 | #pragma pack() |
389 | 389 | ||
390 | u64 cper_next_record_id(void); | 390 | u64 cper_next_record_id(void); |
391 | void cper_print_bits(const char *prefix, unsigned int bits, | ||
392 | const char *strs[], unsigned int strs_size); | ||
391 | 393 | ||
392 | #endif | 394 | #endif |