According to PCIe r7.0, sec. 6.2.11, "Implementation Note: Determination of DPC Control", it is recommended that the Operating System link the enablement of Downstream Port Containment (DPC) to the enablement of Advanced Error Reporting (AER).
However, AER is advertised only on Root Port (RP) or Root Complex Event Collector (RCEC) devices. On the other hand, DPC may be advertised on any PCIe device in the hierarchy. In fact, since the main usecase of DPC is for the switch upstream of an endpoint device to trigger a signal to the host-bridge, it is imperative that it be supported on non-RP, non-RCEC devices.
Previously portdrv has interpreted the spec to mean that the AER service must be available on the same device in order for DPC to be available. This is not what the implementation note meant to imply. If the firmware hands Linux control of AER via _OSC on the host bridge upstream of the device, then Linux should be allowed to assume control of DPC on the device.
The comment above this check alludes to this, by saying:
With dpc-native, allow Linux to use DPC even if it doesn't have permission to use AER.
However, permission to use AER is negotiated at the host bridge, not per-device. So we should not link DPC to enabling AER at the device. Instead, DPC should be enabled if the OS has control of AER for the host bridge that is upstream of the device in question, or if dpc-native was set on the command line.
Cc: stable@vger.kernel.org Signed-off-by: Darshit Shah darnshah@amazon.de Reviewed-by: Lukas Wunner lukas@wunner.de Reviewed-by: Kuppuswamy Sathyanarayanan sathyanarayanan.kuppuswamy@linux.intel.com --- Changes from v2: * + stable@vger.kernel.org since moving to v6.2+ breaks DPC on our systems * Minor stylistic changes to commit message * NO functional changes
drivers/pci/pcie/portdrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 38a41ccf79b9..8db2fa140ae2 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -264,7 +264,7 @@ static int get_port_device_capability(struct pci_dev *dev) */ if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) && pci_aer_available() && - (pcie_ports_dpc_native || (services & PCIE_PORT_SERVICE_AER))) + (host->native_aer || pcie_ports_dpc_native)) services |= PCIE_PORT_SERVICE_DPC;
/* Enable bandwidth control if more than one speed is supported. */