diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 77efa4c5c960..aa392bb6a482 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -871,6 +871,9 @@ static inline bool pci_dev_binding_disallowed(struct pci_dev *dev)
* @id: Value from register PCI_ERR_ROOT_ERR_SRC
* @severity: AER severity, 0-UNCOR Non-fatal, 1-UNCOR fatal, 2-COR
* @root_ratelimit_print: Flag to log or not log the root's error. 0=NotLog/1=Log
+ * @id_valid: Whether @id is a valid value. Some Root Ports report an invalid
+ * Error Source ID, necessitating a search for the error-signaling device
+ * in the hierarchy below the Root Port.
* @multi_error_valid: If multiple errors are reported
* @first_error: First reported error
* @__pad2: Padding for alignment
@@ -893,7 +896,8 @@ struct aer_err_info {
unsigned int severity:2;
unsigned int root_ratelimit_print:1;
- unsigned int __pad1:4;
+ unsigned int __pad1:3;
+ unsigned int id_valid:1;
unsigned int multi_error_valid:1;
unsigned int first_error:5;@@ -955,6 +959,7 @@ void pcie_link_rcec(struct pci_dev *rcec);
void pcie_walk_rcec(struct pci_dev *rcec,
int (*cb)(struct pci_dev *, void *),
void *userdata);
+bool pci_rcec_associated(struct pci_dev *rcec, u16 id);
#else
static inline void pci_rcec_init(struct pci_dev *dev) { }
static inline void pci_rcec_exit(struct pci_dev *dev) { }diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index d492c7ce01e1..9957c36bf215 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -951,12 +951,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%s from %04x:%02x:%02x.%d%s\n",
+ pci_info(dev, "%s%s Error message%s from %04x:%02x:%02x.%d%s%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),
+ info->id_valid ? "" : " (invalid source)",
found ? "" : " (no details found)");
}
@@ -1146,6 +1147,24 @@ static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
return 0;
}
+static bool source_id_valid(struct pci_dev *root, u16 id)
+{
+ /* The Root Port or RCEC can itself be a valid Error Source */
+ if (pci_dev_id(root) == id)
+ return true;
+
+ /* For RCECs, @id shall be in range of its associated buses/devices */
+ if (pci_pcie_type(root) == PCI_EXP_TYPE_RC_EC)
+ return pci_rcec_associated(root, id);
+
+ /* For Root Ports, @id shall be in range of its subordinate buses */
+ if (!root->subordinate)
+ return false;
+
+ return root->subordinate->busn_res.start <= PCI_BUS_NUM(id) &&
+ root->subordinate->busn_res.end >= PCI_BUS_NUM(id);
+}
+
/**
* is_error_source - check whether the device is source of reported error
* @dev: pointer to pci_dev to be checked@@ -1157,11 +1176,8 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
u32 status, mask;
u16 reg16;
- /*
- * When bus ID is equal to 0, it might be a bad ID
- * reported by Root Port.
- */
- if ((PCI_BUS_NUM(e_info->id) != 0) &&
+ /* Some Root Ports report an invalid Error Source ID */
+ if (e_info->id_valid &&
!(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_AERSID)) {
/* Device ID match? */
if (e_info->id == pci_dev_id(dev))@@ -1174,8 +1190,7 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
/*
* When either
- * 1) bus ID is equal to 0. Some ports might lose the bus
- * ID of error source id;
+ * 1) Error Source ID register is invalid;
* 2) bus flag PCI_BUS_FLAGS_NO_AERSID is set
* 3) There are multiple errors and prior ID comparing fails;
* We check AER status registers to find possible reporter.
@@ -1566,6 +1581,8 @@ static void aer_isr_one_error_type(struct pci_dev *root,
{
bool found;
+ info->id_valid = source_id_valid(root, info->id);
+
found = find_source_device(root, info);