Commit 08e3ed12 authored by Maciej W. Rozycki's avatar Maciej W. Rozycki Committed by Bjorn Helgaas
Browse files

PCI: Add failed link recovery for device reset events

Request failed link recovery with any upstream PCIe bridge where a device
has not come back after reset within PCI_RESET_WAIT time.  Reset the
polling interval if recovery succeeded, otherwise continue as usual.

[bhelgaas: inline pcie_parent_link_retrain()]
Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306111631050.64925@angie.orcam.me.uk


Signed-off-by: default avatarMaciej W. Rozycki <macro@orcam.me.uk>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent a89c8224
Loading
Loading
Loading
Loading
+24 −5
Original line number Diff line number Diff line
@@ -1156,7 +1156,14 @@ void pci_resume_bus(struct pci_bus *bus)
static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
{
	int delay = 1;
	u32 id;
	bool retrain = false;
	struct pci_dev *bridge;

	if (pci_is_pcie(dev)) {
		bridge = pci_upstream_bridge(dev);
		if (bridge)
			retrain = true;
	}

	/*
	 * After reset, the device should not silently discard config
@@ -1170,21 +1177,33 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
	 * Command register instead of Vendor ID so we don't have to
	 * contend with the CRS SV value.
	 */
	for (;;) {
		u32 id;

		pci_read_config_dword(dev, PCI_COMMAND, &id);
	while (PCI_POSSIBLE_ERROR(id)) {
		if (!PCI_POSSIBLE_ERROR(id))
			break;

		if (delay > timeout) {
			pci_warn(dev, "not ready %dms after %s; giving up\n",
				 delay - 1, reset_type);
			return -ENOTTY;
		}

		if (delay > PCI_RESET_WAIT)
		if (delay > PCI_RESET_WAIT) {
			if (retrain) {
				retrain = false;
				if (pcie_failed_link_retrain(bridge)) {
					delay = 1;
					continue;
				}
			}
			pci_info(dev, "not ready %dms after %s; waiting\n",
				 delay - 1, reset_type);
		}

		msleep(delay);
		delay *= 2;
		pci_read_config_dword(dev, PCI_COMMAND, &id);
	}

	if (delay > PCI_RESET_WAIT)