Skip to content

Commit 6ade6e8

Browse files
ij-intelkwilczynski
authored andcommitted
PCI: Update Link Speed after retraining
PCIe Link Retraining can alter Link Speed. pcie_retrain_link() that performs the Link Training is called from bwctrl and ASPM driver. While bwctrl listens for Link Bandwidth Management Status (LBMS) to pick up changes in Link Speed, there is a race between pcie_reset_lbms() clearing LBMS after the Link Training and pcie_bwnotif_irq() reading the Link Status register. If LBMS is already cleared when the irq handler reads the register, the interrupt handler will return early with IRQ_NONE and won't update the Link Speed. When Link Speed update originates from bwctrl, pcie_bwctrl_change_speed() ensures Link Speed is updated after the retraining. ASPM driver, however, calls pcie_retrain_link() but does not update the Link Speed after retraining which can result in stale Link Speed. Also, it is possible to have ASPM support with CONFIG_PCIEPORTBUS=n in which case bwctrl will not be built in (and thus won't update the Link Speed at all). To ensure Link Speed is not left stale after Link Training, move the call to pcie_update_link_speed() from pcie_bwctrl_change_speed() into pcie_retrain_link(). Suggested-by: Lukas Wunner <[email protected]> Signed-off-by: Ilpo Järvinen <[email protected]> Signed-off-by: Krzysztof Wilczyński <[email protected]> Reviewed-by: Lukas Wunner <[email protected]> Link: https://lore.kernel.org/linux-pci/[email protected] Link: https://lore.kernel.org/r/[email protected]
1 parent 2389d8d commit 6ade6e8

File tree

2 files changed

+18
-12
lines changed

2 files changed

+18
-12
lines changed

drivers/pci/pci.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4718,6 +4718,11 @@ static int pcie_wait_for_link_status(struct pci_dev *pdev,
47184718
* @pdev: Device whose link to retrain.
47194719
* @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE, for status.
47204720
*
4721+
* Trigger retraining of the PCIe Link and wait for the completion of the
4722+
* retraining. As link retraining is known to asserts LBMS and may change
4723+
* the Link Speed, LBMS is cleared after the retraining and the Link Speed
4724+
* of the subordinate bus is updated.
4725+
*
47214726
* Retrain completion status is retrieved from the Link Status Register
47224727
* according to @use_lt. It is not verified whether the use of the DLLLA
47234728
* bit is valid.
@@ -4758,6 +4763,18 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt)
47584763
* in attempt to correct unreliable link operation.
47594764
*/
47604765
pcie_reset_lbms(pdev);
4766+
4767+
/*
4768+
* Ensure the Link Speed updates after retraining in case the Link
4769+
* Speed was changed because of the retraining. While the bwctrl's
4770+
* IRQ handler normally picks up the new Link Speed, clearing LBMS
4771+
* races with the IRQ handler reading the Link Status register and
4772+
* can result in the handler returning early without updating the
4773+
* Link Speed.
4774+
*/
4775+
if (pdev->subordinate)
4776+
pcie_update_link_speed(pdev->subordinate);
4777+
47614778
return rc;
47624779
}
47634780

drivers/pci/pcie/bwctrl.c

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,7 @@ static int pcie_bwctrl_change_speed(struct pci_dev *port, u16 target_speed, bool
117117
if (ret != PCIBIOS_SUCCESSFUL)
118118
return pcibios_err_to_errno(ret);
119119

120-
ret = pcie_retrain_link(port, use_lt);
121-
if (ret < 0)
122-
return ret;
123-
124-
/*
125-
* Ensure link speed updates also with platforms that have problems
126-
* with notifications.
127-
*/
128-
if (port->subordinate)
129-
pcie_update_link_speed(port->subordinate);
130-
131-
return 0;
120+
return pcie_retrain_link(port, use_lt);
132121
}
133122

134123
/**

0 commit comments

Comments
 (0)