From 1017599755b8b578a671f8fac175bce56189d01c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Jul 2026 17:24:01 +0200 Subject: [PATCH 1/6] PCI/AER: Fix mapping of errors to agent & layer PCIe r7.0 sec 6.2.7 documents the agent and layer of each Correctable and Uncorrectable Error. Based on this spec section, the AER driver maps detected errors to an agent and layer using a set of macros and logs them. Most errors listed in sec 6.2.7 map to the "Receiver" agent and "Transaction Layer", so the macros use these as defaults unless an error maps to something else. However the macros have not been amended since their introduction in 2006 with commit 6c2b374d7485 ("PCI-Express AER implemetation: AER core and aerdriver"). They are still based on PCIe r1.0 sec 7.2.5 (renumbered to 6.2.7 in PCIe r1.1 and newer). Amend the macros to map errors introduced since then to the appropriate agent and layer. PCIe r2.1 introduced a new "Component" agent and "General" layer for Internal Errors and Header Log Overflow. Add them to the macros. Unsupported Request is currently mapped to the "Requester" agent, even though it is reported by the "Receiver". Fix the incorrect mapping. Sec 6.2.7 neglects to list an agent for Data Link Protocol Error and Surprise Down Error. Map the latter to "Component" because PCIe r7.0 sec 3.2.1 states that the error is "associated with the detecting Port". Map the former to "Receiver" because every occurrence of Data Link Protocol Error in the spec refers to it being logged in the Receiving Port. I have had these errata reported to the PCI-SIG Protocol Working Group. (There's also a layout erratum in the REPLAY_NUM Rollover row wherein columns are shifted to the left, but that's already corrected in the PCIe r7.1 draft as of 2026-04-07.) Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org Link: https://patch.msgid.link/aec4820a75e949b332585a08cb1808fda7f40ea4.1784905909.git.lukas@wunner.de --- drivers/pci/pcie/aer.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index c4fd9c0b2a54..e07d0e05b5d6 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -428,23 +428,32 @@ void pci_aer_exit(struct pci_dev *dev) #define AER_AGENT_REQUESTER 1 #define AER_AGENT_COMPLETER 2 #define AER_AGENT_TRANSMITTER 3 +#define AER_AGENT_COMPONENT 4 #define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) + 0 : PCI_ERR_UNC_COMP_TIME) #define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ 0 : PCI_ERR_UNC_COMP_ABORT) #define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ - (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) + (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : \ + (PCI_ERR_UNC_POISON_BLK|PCI_ERR_UNC_ATOMEG| \ + PCI_ERR_UNC_DMWR_BLK|PCI_ERR_UNC_XLAT_BLK| \ + PCI_ERR_UNC_TLPPRE)) +#define AER_AGENT_COMPONENT_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : \ + (PCI_ERR_UNC_INTN|PCI_ERR_UNC_SURPDN)) #define AER_GET_AGENT(t, e) \ ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ + (e & AER_AGENT_COMPONENT_MASK(t)) ? AER_AGENT_COMPONENT : \ AER_AGENT_RECEIVER) #define AER_PHYSICAL_LAYER_ERROR 0 #define AER_DATA_LINK_LAYER_ERROR 1 #define AER_TRANSACTION_LAYER_ERROR 2 +#define AER_GENERAL_ERROR 3 #define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ PCI_ERR_COR_RCVR : 0) @@ -452,11 +461,14 @@ void pci_aer_exit(struct pci_dev *dev) (PCI_ERR_COR_BAD_TLP| \ PCI_ERR_COR_BAD_DLLP| \ PCI_ERR_COR_REP_ROLL| \ - PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) + PCI_ERR_COR_REP_TIMER) : (PCI_ERR_UNC_DLP|PCI_ERR_UNC_SURPDN)) +#define AER_GENERAL_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : PCI_ERR_UNC_INTN) #define AER_GET_LAYER_ERROR(t, e) \ ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ + (e & AER_GENERAL_ERROR_MASK(t)) ? AER_GENERAL_ERROR : \ AER_TRANSACTION_LAYER_ERROR) /* @@ -471,7 +483,8 @@ static const char * const aer_error_severity_string[] = { static const char *aer_error_layer[] = { "Physical Layer", "Data Link Layer", - "Transaction Layer" + "Transaction Layer", + "General", }; static const char *aer_correctable_error_string[] = { @@ -548,7 +561,8 @@ static const char *aer_agent_string[] = { "Receiver ID", "Requester ID", "Completer ID", - "Transmitter ID" + "Transmitter ID", + "Component ID", }; #define aer_stats_dev_attr(name, stats_array, strings_array, \ From 9247e45c00cb93eb8fce7051517545cb3ff4a6b4 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Jul 2026 17:24:02 +0200 Subject: [PATCH 2/6] PCI/AER: Log agent & layer for each individual error The AER driver maps detected errors to the corresponding agent and layer per PCIe r7.0 sec 6.2.7 and logs both. If multiple errors were detected, their agent and layer may differ. However the AER driver only logs one agent and one layer for all of them, which seems nonsensical. Log the agent and layer for each individual error instead. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/d983b813043c518d098e2919161e816b91f15862.1784905909.git.lukas@wunner.de --- drivers/pci/pcie/aer.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index e07d0e05b5d6..9cdac1c8c52e 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -558,11 +558,11 @@ static const char *aer_uncorrectable_error_string[] = { }; static const char *aer_agent_string[] = { - "Receiver ID", - "Requester ID", - "Completer ID", - "Transmitter ID", - "Component ID", + "Receiver", + "Requester", + "Completer", + "Transmitter", + "Component", }; #define aer_stats_dev_attr(name, stats_array, strings_array, \ @@ -844,8 +844,8 @@ static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { const char **strings; unsigned long status = info->status & ~info->mask; + const char *errmsg, *agent, *layer; const char *level = info->level; - const char *errmsg; int i; if (info->severity == AER_CORRECTABLE) @@ -855,10 +855,17 @@ static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) for_each_set_bit(i, &status, 32) { errmsg = strings[i]; - if (!errmsg) + agent = aer_agent_string[AER_GET_AGENT(info->severity, BIT(i))]; + layer = aer_error_layer[AER_GET_LAYER_ERROR(info->severity, + BIT(i))]; + if (!errmsg) { errmsg = "Unknown Error Bit"; + agent = ""; + layer = ""; + } - aer_printk(level, dev, " [%2d] %-22s%s\n", i, errmsg, + aer_printk(level, dev, " [%2d] %-17s | %-11s | %-17s%s\n", + i, errmsg, agent, layer, info->first_error == i ? " (First)" : ""); } } @@ -879,9 +886,9 @@ static void aer_print_source(struct pci_dev *dev, struct aer_err_info *info, void aer_print_error(struct aer_err_info *info, int i) { struct pci_dev *dev; - int layer, agent, id; const char *level = info->level; const char *bus_type = aer_err_bus(info); + int id; if (WARN_ON_ONCE(i >= AER_MAX_MULTI_ERR_DEVICES)) return; @@ -897,17 +904,13 @@ void aer_print_error(struct aer_err_info *info, int i) return; if (!info->status) { - pci_err(dev, "%s Bus Error: severity=%s, type=Inaccessible, (Unregistered Agent ID)\n", + pci_err(dev, "%s Bus Error: severity=%s (Inaccessible)\n", bus_type, aer_error_severity_string[info->severity]); goto out; } - layer = AER_GET_LAYER_ERROR(info->severity, info->status); - agent = AER_GET_AGENT(info->severity, info->status); - - aer_printk(level, dev, "%s Bus Error: severity=%s, type=%s, (%s)\n", - bus_type, aer_error_severity_string[info->severity], - aer_error_layer[layer], aer_agent_string[agent]); + aer_printk(level, dev, "%s Bus Error: severity=%s\n", + bus_type, aer_error_severity_string[info->severity]); aer_printk(level, dev, " device [%04x:%04x] error status/mask=%08x/%08x\n", dev->vendor, dev->device, info->status, info->mask); @@ -940,8 +943,8 @@ EXPORT_SYMBOL_GPL(cper_severity_to_aer); void pci_print_aer(struct pci_dev *dev, int aer_severity, struct aer_capability_regs *aer) { + int tlp_header_valid = 0; const char *bus_type; - int layer, agent, tlp_header_valid = 0; u32 status, mask; struct aer_err_info info = { .severity = aer_severity, @@ -972,14 +975,9 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, if (!aer_ratelimit(dev, info.severity)) return; - layer = AER_GET_LAYER_ERROR(aer_severity, status); - agent = AER_GET_AGENT(aer_severity, status); - aer_printk(info.level, dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", status, mask); __aer_print_error(dev, &info); - aer_printk(info.level, dev, "aer_layer=%s, aer_agent=%s\n", - aer_error_layer[layer], aer_agent_string[agent]); if (aer_severity != AER_CORRECTABLE) aer_printk(info.level, dev, "aer_uncor_severity: 0x%08x\n", From 8446e1147f65563d374ffa54dc3ba81adb1342c5 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Jul 2026 17:24:03 +0200 Subject: [PATCH 3/6] PCI/AER: Deduplicate logging of Error Source Identification aer_print_source() already logs the Error Source Identification Register: AER: Multiple Correctable error message received from 0000:b7:02.0 However aer_print_error() subsequently identifies the Error Source once more by emitting an "Error of this Agent is reported first" message. The additional message was introduced by commit 0d465f23502e ("PCI: pcie, aer: fix report of multiple errors") because it deemed the message emitted by aer_print_source() confusing: When the Multiple ERR_COR Received or Multiple ERR_FATAL/NONFATAL Received bit in the Root Error Status Register is set, it doesn't mean that all errors originated from the device in the Error Source Identification Register. Rather, the errors may have come from multiple distinct devices. The commit sought to make that clearer. Achieve the commit's objective by rephrasing the message emitted by aer_print_source() and drop the additional message logged by aer_print_error() to reduce dmesg noisiness and simplify the code. While modifying the log message anyway, fix minor grammatical issues: Append a plural "s" to "message", add a missing closing brace to "(no details found" and capitalize "Error" to match the spec. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/3a5d1624d6912db1bc8c4e89e7a6a72ac510f4dc.1784905909.git.lukas@wunner.de --- drivers/pci/pcie/aer.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 9cdac1c8c52e..c21139b9079b 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -875,12 +875,13 @@ static void aer_print_source(struct pci_dev *dev, struct aer_err_info *info, { u16 source = info->id; - pci_info(dev, "%s%s error message received from %04x:%02x:%02x.%d%s\n", + pci_info(dev, "%s%s Error message%s from %04x:%02x:%02x.%d%s\n", info->multi_error_valid ? "Multiple " : "", aer_error_severity_string[info->severity], + info->multi_error_valid ? "s received, first one" : " received", pci_domain_nr(dev->bus), PCI_BUS_NUM(source), PCI_SLOT(source), PCI_FUNC(source), - found ? "" : " (no details found"); + found ? "" : " (no details found)"); } void aer_print_error(struct aer_err_info *info, int i) @@ -888,13 +889,11 @@ void aer_print_error(struct aer_err_info *info, int i) struct pci_dev *dev; const char *level = info->level; const char *bus_type = aer_err_bus(info); - int id; if (WARN_ON_ONCE(i >= AER_MAX_MULTI_ERR_DEVICES)) return; dev = info->dev[i]; - id = pci_dev_id(dev); pci_dev_aer_stats_incr(dev, info); trace_aer_event(pci_name(dev), (info->status & ~info->mask), @@ -906,7 +905,7 @@ void aer_print_error(struct aer_err_info *info, int i) if (!info->status) { pci_err(dev, "%s Bus Error: severity=%s (Inaccessible)\n", bus_type, aer_error_severity_string[info->severity]); - goto out; + return; } aer_printk(level, dev, "%s Bus Error: severity=%s\n", @@ -919,10 +918,6 @@ void aer_print_error(struct aer_err_info *info, int i) if (info->tlp_header_valid) pcie_print_tlp_log(dev, &info->tlp, level, dev_fmt(" ")); - -out: - if (info->id && info->error_dev_num > 1 && info->id == id) - pci_err(dev, " Error of this Agent is reported first\n"); } #ifdef CONFIG_ACPI_APEI_PCIEAER From a8bf2dd750de7d682fdaa2127f4e3217ce9c4a82 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Jul 2026 17:24:04 +0200 Subject: [PATCH 4/6] PCI/AER: Emit TLP Log only for unmasked errors Per PCIe r7.0 sec 6.2.5, the prefix and header of an offending TLP is only recorded for unmasked Uncorrectable Errors. Yet when the AER driver determines whether a prefix and header has been logged, it does not take the Uncorrectable Error Mask Register into account. Fix it. Fixes: 6c2b374d7485 ("PCI-Express AER implemetation: AER core and aerdriver") Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org # v2.6.19+ Link: https://patch.msgid.link/2e712b96ba5bfc729d78bfc23f7fb7d285aa3d6d.1784905909.git.lukas@wunner.de --- drivers/pci/pcie/aer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index c21139b9079b..4d1d99662086 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -954,7 +954,8 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, status = aer->uncor_status; mask = aer->uncor_mask; info.level = KERN_ERR; - tlp_header_valid = tlp_header_logged(status, aer->cap_control); + tlp_header_valid = tlp_header_logged(status & ~mask, + aer->cap_control); } info.status = status; @@ -1343,7 +1344,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc); info->first_error = PCI_ERR_CAP_FEP(aercc); - if (tlp_header_logged(info->status, aercc)) { + if (tlp_header_logged(info->status & ~info->mask, aercc)) { info->tlp_header_valid = 1; pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, aer + PCI_ERR_PREFIX_LOG, From f141f74c45c6f774eebdb7e45bd609be5122bfa8 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Jul 2026 17:24:05 +0200 Subject: [PATCH 5/6] PCI/AER: Move retrieval of FEP and TLP Log into helper When aer_get_device_error_info() gathers information on Uncorrectable Errors from a device, it reads the First Error Pointer and TLP Prefix/ Header Log and caches them in struct aer_err_info. Those two fields will also need to be read for Advisory Non-Fatal Errors (which are signaled as Correctable Errors). Move their retrieval into a new aer_get_uncor_info() helper for reuse by the imminent Advisory Non-Fatal Error support. No functional change intended. Signed-off-by: Lukas Wunner Signed-off-by: Bjorn Helgaas Link: https://patch.msgid.link/0f2f037c7ccf099f0c253cbc4ad9be526c68c5af.1784905909.git.lukas@wunner.de --- drivers/pci/pcie/aer.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 4d1d99662086..c196c94f43d7 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -1289,6 +1289,27 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, EXPORT_SYMBOL_GPL(aer_recover_queue); #endif +static void aer_get_uncor_info(struct pci_dev *dev, struct aer_err_info *info, + u32 status) +{ + u16 aer = dev->aer_cap; + u32 aercc; + + /* Get First Error Pointer */ + pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc); + info->first_error = PCI_ERR_CAP_FEP(aercc); + + /* Get TLP Prefix/Header Log */ + if (tlp_header_logged(status, aercc)) { + info->tlp_header_valid = 1; + pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, + aer + PCI_ERR_PREFIX_LOG, + aer_tlp_log_len(dev, aercc), + aercc & PCI_ERR_CAP_TLP_LOG_FLIT, + &info->tlp); + } +} + /** * aer_get_device_error_info - read error status from dev and store it to info * @info: pointer to structure to store the error record @@ -1302,7 +1323,6 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) { struct pci_dev *dev; int type, aer; - u32 aercc; if (i >= AER_MAX_MULTI_ERR_DEVICES) return 0; @@ -1340,18 +1360,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) if (!(info->status & ~info->mask)) return 0; - /* Get First Error Pointer */ - pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc); - info->first_error = PCI_ERR_CAP_FEP(aercc); - - if (tlp_header_logged(info->status & ~info->mask, aercc)) { - info->tlp_header_valid = 1; - pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, - aer + PCI_ERR_PREFIX_LOG, - aer_tlp_log_len(dev, aercc), - aercc & PCI_ERR_CAP_TLP_LOG_FLIT, - &info->tlp); - } + aer_get_uncor_info(dev, info, info->status & ~info->mask); } return 1; From eddba19b8b5f76d57424ee328a68fd495c5db857 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Jul 2026 17:24:06 +0200 Subject: [PATCH 6/6] PCI/AER: Support Advisory Non-Fatal Errors Per PCIe r7.0 sec 6.2.4.3, certain Non-Fatal Errors may be signaled using ERR_COR instead of ERR_NONFATAL. These "Advisory Non-Fatal Errors" are listed in sec 6.2.7 and explained in detail in sec 6.2.3.2.4. Advisory Non-Fatal Errors set bits in the Uncorrectable Error Status Register as well as one bit in the Correctable Error Status Register (Advisory Non-Fatal Error Status, bit 13). The latter is masked by default, hence these errors are currently not signaled at all (except on non-compliant products which choose to unmask the bit). Unmask Advisory Non-Fatal Errors on device enumeration. Some Non-Fatal Errors are always Advisory, others may be Advisory at the discretion of the detecting agent. If multiple errors occur, the agent may qualify a portion as non-Advisory and signal ERR_NONFATAL in addition to ERR_COR. In this case, there's no way to determine which Non-Fatal Error was Advisory. Assume none is to ensure that the Uncorrectable Error code path is taken to recover from the errors. Introduce aer_compute_anfe_status() to compute Advisory Non-Fatal Error bits from AER registers, based on this policy. Use it for Firmware First error handling in pci_print_aer(), which receives an AER register dump from the platform (UEFI r2.11 sec N.2.7). Introduce aer_get_anfe_status() to read AER registers from a device and feed them to aer_compute_anfe_status(). Use it for native error handling in aer_get_device_error_info(), which gathers registers from the device and caches the computed Advisory Non-Fatal Error bits in a new anfe_status field in struct aer_err_info. Regardless whether error handling is native or Firmware First, the AER driver needs to increment error counters, signal a trace event and log each error. When Advisory Non-Fatal Errors occur, these steps must be performed for Correctable Errors and for Uncorrectable Errors. Achieve this through a recursive invocation of aer_print_error() (for native error handling) and pci_print_aer() (for Firmware First error handling). The recursive invocation reports the (Advisory) Uncorrectable Errors after reporting the Correctable Errors. Note that the First Error Pointer and TLP Prefix Log is only meaningful for Uncorrectable Errors, but when Advisory Non-Fatal Errors occur, aer_get_device_error_info() has to populate the first_error and tlp_header_valid fields in struct aer_err_info for a Correctable Error. Avoid incorrectly logging those fields for Correctable Errors by amending __aer_print_error() and aer_print_error() with conditionals. Sample log output for an Advisory Unsupported Request Error: pcieport 0001:00:00.4: AER: Multiple Correctable Error messages received, first one from 0001:0e:00.0 idxd 0001:0e:00.0: PCIe Bus Error: severity=Correctable idxd 0001:0e:00.0: device [8086:1216] error status/mask=00002000/00000000 idxd 0001:0e:00.0: [13] NonFatalErr | | idxd 0001:0e:00.0: PCIe Bus Error: severity=Uncorrectable (Non-Fatal) idxd 0001:0e:00.0: device [8086:1216] error status/mask=00100000/00000000 idxd 0001:0e:00.0: [20] UnsupReq | Receiver | Transaction Layer (First) idxd 0001:0e:00.0: AER: TLP Header (Flit): 0x01000104 0x00000000 0x0000080e 0x0f800001 This commit takes inspiration (but differs significantly) from an earlier submission by Zhenzhong Duan, which in turn was based on a submission by Qingshun Wang: https://lore.kernel.org/r/20240620025857.206647-1-zhenzhong.duan@intel.com/ Prior attempts at supporting Advisory Non-Fatal Errors were submitted by Yicong Yang and Dio Sun: https://lore.kernel.org/r/1614689994-10925-1-git-send-email-yangyicong@hisilicon.com/ https://lore.kernel.org/r/BJXPR01MB0614C01A9523786117B1F1CBCEC8A@BJXPR01MB0614.CHNPR01.prod.partner.outlook.cn/ Signed-off-by: Lukas Wunner [bhelgaas: fold in https://lore.kernel.org/all/amdnMg_J6T3Sys45@wunner.de, https://lore.kernel.org/all/120da0565eac0157ffd913423c7cfa66e985ff59.1786800931.git.lukas@wunner.de] Signed-off-by: Bjorn Helgaas Link: https://lore.kernel.org/r/20240620025857.206647-1-zhenzhong.duan@intel.com/ Link: https://lore.kernel.org/r/1614689994-10925-1-git-send-email-yangyicong@hisilicon.com/ Link: https://lore.kernel.org/r/BJXPR01MB0614C01A9523786117B1F1CBCEC8A@BJXPR01MB0614.CHNPR01.prod.partner.outlook.cn/ Link: https://patch.msgid.link/1b62915ffe06ee5b08e846531c42392e5f244337.1784905909.git.lukas@wunner.de --- drivers/pci/pci.h | 4 + drivers/pci/pcie/aer.c | 166 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 157 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4469e1a77f3c..02ee26ee9206 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -828,6 +828,9 @@ static inline bool pci_dev_binding_disallowed(struct pci_dev *dev) * @tlp_header_valid: Indicates if TLP field contains error information * @status: COR/UNCOR error status * @mask: COR/UNCOR mask + * @anfe_status: Advisory Non-Fatal Errors, i.e. Uncorrectable Errors signaled + * as Correctable Errors (PCIe r7.0 sec 6.2.4.3). Only used if @severity + * is AER_CORRECTABLE and @status has Advisory Non-Fatal Error Status set. * @tlp: Transaction packet information */ struct aer_err_info { @@ -850,6 +853,7 @@ struct aer_err_info { unsigned int status; unsigned int mask; + u32 anfe_status; struct pcie_tlp_log tlp; }; diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index c196c94f43d7..d8dcd238fda1 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -96,6 +96,20 @@ struct aer_info { struct ratelimit_state nonfatal_ratelimit; }; +#define AER_ANFE (PCI_ERR_UNC_UNX_COMP | \ + PCI_ERR_UNC_ATOMEG | \ + PCI_ERR_UNC_DMWR_BLK) + +#define AER_POSSIBLE_ANFE (PCI_ERR_UNC_POISON_TLP | \ + PCI_ERR_UNC_POISON_BLK | \ + PCI_ERR_UNC_ECRC | \ + PCI_ERR_UNC_UNSUP | \ + PCI_ERR_UNC_COMP_TIME | \ + PCI_ERR_UNC_COMP_ABORT | \ + PCI_ERR_UNC_ACSV | \ + PCI_ERR_UNC_TLPPRE | \ + PCI_ERR_UNC_PCRC_CHECK) + #define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ PCI_ERR_UNC_POISON_BLK | \ PCI_ERR_UNC_ECRC| \ @@ -410,6 +424,15 @@ void pci_aer_init(struct pci_dev *dev) n = pcie_cap_has_rtctl(dev) ? 5 : 4; pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_ERR, sizeof(u32) * n); + /* + * Advisory Non-Fatal Errors are masked by default (PCIe r7.0, sec + * 7.8.4.6). + */ + if (pcie_aer_is_native(dev) && dev->devcap & PCI_EXP_DEVCAP_RBER) + pci_clear_and_set_config_dword(dev, + dev->aer_cap + PCI_ERR_COR_MASK, + PCI_ERR_COR_ADV_NFAT, 0); + pci_aer_clear_status(dev); if (pci_aer_available()) @@ -429,6 +452,7 @@ void pci_aer_exit(struct pci_dev *dev) #define AER_AGENT_COMPLETER 2 #define AER_AGENT_TRANSMITTER 3 #define AER_AGENT_COMPONENT 4 +#define AER_AGENT_UNDEF 5 #define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ 0 : PCI_ERR_UNC_COMP_TIME) @@ -442,18 +466,23 @@ void pci_aer_exit(struct pci_dev *dev) #define AER_AGENT_COMPONENT_MASK(t) ((t == AER_CORRECTABLE) ? \ (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : \ (PCI_ERR_UNC_INTN|PCI_ERR_UNC_SURPDN)) +#define AER_AGENT_UNDEF_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_ADV_NFAT : 0) + #define AER_GET_AGENT(t, e) \ ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ (e & AER_AGENT_COMPONENT_MASK(t)) ? AER_AGENT_COMPONENT : \ + (e & AER_AGENT_UNDEF_MASK(t)) ? AER_AGENT_UNDEF : \ AER_AGENT_RECEIVER) #define AER_PHYSICAL_LAYER_ERROR 0 #define AER_DATA_LINK_LAYER_ERROR 1 #define AER_TRANSACTION_LAYER_ERROR 2 #define AER_GENERAL_ERROR 3 +#define AER_UNDEF_ERROR 4 #define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ PCI_ERR_COR_RCVR : 0) @@ -464,11 +493,14 @@ void pci_aer_exit(struct pci_dev *dev) PCI_ERR_COR_REP_TIMER) : (PCI_ERR_UNC_DLP|PCI_ERR_UNC_SURPDN)) #define AER_GENERAL_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ (PCI_ERR_COR_INTERNAL|PCI_ERR_COR_LOG_OVER) : PCI_ERR_UNC_INTN) +#define AER_UNDEF_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_ADV_NFAT : 0) #define AER_GET_LAYER_ERROR(t, e) \ ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ (e & AER_GENERAL_ERROR_MASK(t)) ? AER_GENERAL_ERROR : \ + (e & AER_UNDEF_ERROR_MASK(t)) ? AER_UNDEF_ERROR : \ AER_TRANSACTION_LAYER_ERROR) /* @@ -485,6 +517,7 @@ static const char *aer_error_layer[] = { "Data Link Layer", "Transaction Layer", "General", + "", }; static const char *aer_correctable_error_string[] = { @@ -563,6 +596,7 @@ static const char *aer_agent_string[] = { "Completer", "Transmitter", "Component", + "", }; #define aer_stats_dev_attr(name, stats_array, strings_array, \ @@ -826,6 +860,46 @@ static int aer_ratelimit(struct pci_dev *dev, unsigned int severity) } } +static u32 aer_compute_anfe_status(u16 devsta, u32 uncor_status, + u32 uncor_mask, u32 uncor_severity) +{ + u32 anfe_status; + + /* + * Uncorrectable Errors must be unmasked and have Non-Fatal severity + * to qualify as Advisory Non-Fatal Errors (PCIe r7.0 sec 6.2.4.3). + */ + uncor_status &= ~uncor_mask & ~uncor_severity; + + /* Some Non-Fatal Errors are always Advisory (PCIe r7.0 sec 6.2.7). */ + anfe_status = uncor_status & AER_ANFE; + + /* + * Others may be Advisory at the discretion of the detecting agent. + * That's impossible to discern if the agent signaled ERR_NONFATAL + * in addition to ERR_COR. Assume none are Advisory in that case + * to ensure that the Uncorrectable Error code path is taken. + */ + if (!(devsta & PCI_EXP_DEVSTA_NFED)) + anfe_status |= uncor_status & AER_POSSIBLE_ANFE; + + return anfe_status; +} + +static u32 aer_get_anfe_status(struct pci_dev *dev) +{ + u32 uncor_status, uncor_mask, uncor_severity; + u16 devsta, aer = dev->aer_cap; + + pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &uncor_status); + pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &uncor_mask); + pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_SEVER, &uncor_severity); + pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &devsta); + + return aer_compute_anfe_status(devsta, uncor_status, uncor_mask, + uncor_severity); +} + static bool tlp_header_logged(u32 status, u32 capctl) { /* Errors for which a header is always logged (PCIe r7.0 sec 6.2.7) */ @@ -866,6 +940,7 @@ static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) aer_printk(level, dev, " [%2d] %-17s | %-11s | %-17s%s\n", i, errmsg, agent, layer, + info->severity != AER_CORRECTABLE && info->first_error == i ? " (First)" : ""); } } @@ -897,10 +972,16 @@ void aer_print_error(struct aer_err_info *info, int i) pci_dev_aer_stats_incr(dev, info); trace_aer_event(pci_name(dev), (info->status & ~info->mask), - info->severity, info->tlp_header_valid, &info->tlp, bus_type); + info->severity, info->tlp_header_valid && + info->severity != AER_CORRECTABLE, &info->tlp, + bus_type); + /* + * For Advisory Non-Fatal Errors, record statistics and tracing + * even if ratelimited + */ if (!info->ratelimit_print[i]) - return; + goto anfe; if (!info->status) { pci_err(dev, "%s Bus Error: severity=%s (Inaccessible)\n", @@ -916,8 +997,20 @@ void aer_print_error(struct aer_err_info *info, int i) __aer_print_error(dev, info); - if (info->tlp_header_valid) + if (info->tlp_header_valid && info->severity != AER_CORRECTABLE) pcie_print_tlp_log(dev, &info->tlp, level, dev_fmt(" ")); + +anfe: + /* Recursive invocation for Advisory Non-Fatal Errors */ + if (info->anfe_status && info->severity == AER_CORRECTABLE) { + info->severity = AER_NONFATAL; + info->status = info->anfe_status; + info->mask = 0; + + aer_print_error(info, i); + + info->severity = AER_CORRECTABLE; + } } #ifdef CONFIG_ACPI_APEI_PCIEAER @@ -935,13 +1028,15 @@ int cper_severity_to_aer(int cper_severity) EXPORT_SYMBOL_GPL(cper_severity_to_aer); #endif -void pci_print_aer(struct pci_dev *dev, int aer_severity, - struct aer_capability_regs *aer) +static void __pci_print_aer(struct pci_dev *dev, int aer_severity, + struct aer_capability_regs *aer, + bool ratelimit_print, const char *level) { + const char *bus_type, *sev; int tlp_header_valid = 0; - const char *bus_type; u32 status, mask; struct aer_err_info info = { + .level = level, .severity = aer_severity, .first_error = PCI_ERR_CAP_FEP(aer->cap_control), }; @@ -949,11 +1044,11 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, if (aer_severity == AER_CORRECTABLE) { status = aer->cor_status; mask = aer->cor_mask; - info.level = KERN_WARNING; + sev = "cor"; } else { status = aer->uncor_status; mask = aer->uncor_mask; - info.level = KERN_ERR; + sev = "uncor"; tlp_header_valid = tlp_header_logged(status & ~mask, aer->cap_control); } @@ -968,11 +1063,16 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, trace_aer_event(pci_name(dev), (status & ~mask), aer_severity, tlp_header_valid, &aer->header_log, bus_type); - if (!aer_ratelimit(dev, info.severity)) - return; + /* + * For Advisory Non-Fatal Errors, record statistics and tracing + * even if ratelimited + */ + if (!ratelimit_print) + goto anfe; - aer_printk(info.level, dev, "aer_status: 0x%08x, aer_mask: 0x%08x\n", - status, mask); + aer_printk(info.level, dev, + "aer_%s_status: 0x%08x, aer_%s_mask: 0x%08x\n", + sev, status, sev, mask); __aer_print_error(dev, &info); if (aer_severity != AER_CORRECTABLE) @@ -982,6 +1082,36 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, if (tlp_header_valid) pcie_print_tlp_log(dev, &aer->header_log, info.level, dev_fmt(" ")); + +anfe: + /* Recursive invocation for Advisory Non-Fatal Errors */ + if (aer_severity == AER_CORRECTABLE && + info.status & ~info.mask & PCI_ERR_COR_ADV_NFAT) { + u32 anfe_status = aer_compute_anfe_status(PCI_EXP_DEVSTA_CED, + aer->uncor_status, + aer->uncor_mask, + aer->uncor_severity); + if (anfe_status) { + aer->uncor_status = anfe_status; + aer->uncor_mask = 0; + __pci_print_aer(dev, AER_NONFATAL, aer, + ratelimit_print, level); + } + } +} + +void pci_print_aer(struct pci_dev *dev, int aer_severity, + struct aer_capability_regs *aer) +{ + /* + * Precalculate ratelimit counter and log level so that Advisory + * Non-Fatal Errors are treated like the accompanying Correctable Error + */ + bool ratelimit_print = aer_ratelimit(dev, aer_severity); + const char *level = aer_severity == AER_CORRECTABLE ? KERN_WARNING + : KERN_ERR; + + __pci_print_aer(dev, aer_severity, aer, ratelimit_print, level); } EXPORT_SYMBOL_GPL(pci_print_aer); @@ -1184,9 +1314,14 @@ static void pci_aer_handle_error(struct pci_dev *dev, struct aer_err_info *info) * Correctable error does not need software intervention. * No need to go through error recovery process. */ - if (aer) + if (aer) { pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS, info->status); + if (info->anfe_status) + pci_write_config_dword(dev, + aer + PCI_ERR_UNCOR_STATUS, + info->anfe_status); + } if (pcie_aer_is_native(dev)) { struct pci_driver *pdrv = dev->driver; @@ -1333,6 +1468,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) /* Must reset in this function */ info->status = 0; + info->anfe_status = 0; info->tlp_header_valid = 0; info->is_cxl = pcie_is_cxl(dev); @@ -1347,6 +1483,10 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) &info->mask); if (!(info->status & ~info->mask)) return 0; + if (info->status & ~info->mask & PCI_ERR_COR_ADV_NFAT) { + info->anfe_status = aer_get_anfe_status(dev); + aer_get_uncor_info(dev, info, info->anfe_status); + } } else if (type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_DOWNSTREAM ||