[0] ACPI PCI Express Base Specification 4.0 1.3.2.3 Root Complex
Integrated
Endpoint Rules.
[1] ACPI PCI Express Base Specification 4.0 6.2 Error Signalling and
Logging
[2] ACPI Specification 6.3 Chapter 18 ACPI Platform Error Interface
(APEI)
[3] ACPI Sepcification 6.3 18.2.3.7 Generic Hardware Error Source
[4] UEFI Specification 2.8, N.2.7 PCI Express Error Section
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx>
---
Changes since v1:
* Separated from the largely unrelated fix so the two can move
forwards separately.
* Instead of separate path for RCiEP handling use the method
suggested
by Bjorn
and Sathyanarayanan with an adjusted pci_bus_walk.
Thanks all for reviews of V1.
drivers/pci/bus.c | 28 ++++++++++++++++++++++++++++
drivers/pci/pcie/err.c | 29 +++++++++++++++++++----------
include/linux/pci.h | 2 ++
3 files changed, 49 insertions(+), 10 deletions(-)
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 8e40b3e6da77..7cbe1ed2db3d 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -411,6 +411,34 @@ void pci_walk_bus(struct pci_bus *top, int
(*cb)(struct pci_dev *, void *),
}
EXPORT_SYMBOL_GPL(pci_walk_bus);
+/** pci_walk_below_dev - walk devices below (or on) another device
+ * @dev device for which we should walk below, include device
when not a port.
+ * @cb callback to be called for each device found
+ * @userdata arbitrary pointer to be passed to callback.
+ *
+ * If the device provided is a port,
+ * walk the subordinate bus, including any bridged devices
+ * on buses under this bus. Call the provided callback
+ * on each device found.
+ *
+ * If the device provided hs no subordinate bus, call the provided
+ * callback on the device itself.
+ *
+ */
+void pci_walk_below_dev(struct pci_dev *dev, int (*cb)(struct
pci_dev
*, void *),
+ void *userdata)
+{
+ struct pci_bus *bus;
+
+ if (dev->subordinate) {
+ bus = dev->subordinate;
+ pci_walk_bus(bus, cb, userdata);
+ } else {
+ cb(dev, userdata);
+ }
+}
+EXPORT_SYMBOL_GPL(pci_walk_below_dev);
+
struct pci_bus *pci_bus_get(struct pci_bus *bus)
{
if (bus)
diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c
index 14bb8f54723e..fa08b1cc3d96 100644
--- a/drivers/pci/pcie/err.c
+++ b/drivers/pci/pcie/err.c
@@ -151,33 +151,39 @@ pci_ers_result_t pcie_do_recovery(struct
pci_dev
*dev,
pci_ers_result_t (*reset_link)(struct pci_dev *pdev))
{
pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;
- struct pci_bus *bus;
/*
* Error recovery runs on all subordinates of the first downstream
port.
* If the downstream port detected the error, it is cleared at the
end.
+ * For RCiEPs we should reset just the RCiEP itself.
*/
if (!(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
- pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END))
dev = dev->bus->self;
- bus = dev->subordinate;
pci_dbg(dev, "broadcast error_detected message\n");
if (state == pci_channel_io_frozen) {
- pci_walk_bus(bus, report_frozen_detected, &status);
+ pci_walk_below_dev(dev, report_frozen_detected, &status);
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) {
+ pci_warn(dev, "link reset not possible for RCiEP\n");
+ status = PCI_ERS_RESULT_NONE;
+ goto failed;
+ }
+
status = reset_link(dev);
if (status != PCI_ERS_RESULT_RECOVERED) {
pci_warn(dev, "link reset failed\n");
goto failed;
}
} else {
- pci_walk_bus(bus, report_normal_detected, &status);
+ pci_walk_below_dev(dev, report_normal_detected, &status);
}
if (status == PCI_ERS_RESULT_CAN_RECOVER) {
status = PCI_ERS_RESULT_RECOVERED;
pci_dbg(dev, "broadcast mmio_enabled message\n");
- pci_walk_bus(bus, report_mmio_enabled, &status);
+ pci_walk_below_dev(dev, report_mmio_enabled, &status);
}
if (status == PCI_ERS_RESULT_NEED_RESET) {
@@ -188,17 +194,20 @@ pci_ers_result_t pcie_do_recovery(struct
pci_dev
*dev,
*/
status = PCI_ERS_RESULT_RECOVERED;
pci_dbg(dev, "broadcast slot_reset message\n");
- pci_walk_bus(bus, report_slot_reset, &status);
+ pci_walk_below_dev(dev, report_slot_reset, &status);
}
if (status != PCI_ERS_RESULT_RECOVERED)
goto failed;
pci_dbg(dev, "broadcast resume message\n");
- pci_walk_bus(bus, report_resume, &status);
+ pci_walk_below_dev(dev, report_resume, &status);
- pci_aer_clear_device_status(dev);
- pci_aer_clear_nonfatal_status(dev);
+ if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)) {
+ pci_aer_clear_device_status(dev);
+ pci_aer_clear_nonfatal_status(dev);
+ }
pci_info(dev, "device recovery successful\n");
return status;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index c79d83304e52..538bf0a76d33 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1411,6 +1411,8 @@ int pci_scan_bridge(struct pci_bus *bus,
struct
pci_dev *dev, int max,
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *,
void *),
void *userdata);
+void pci_walk_below_dev(struct pci_dev *dev, int (*cb)(struct
pci_dev
*, void *),
+ void *userdata);
int pci_cfg_space_size(struct pci_dev *dev);
unsigned char pci_bus_max_busnr(struct pci_bus *bus);
void pci_setup_bridge(struct pci_bus *bus);
--
2.19.1