From 971735bd32d7a8cb7cd1a8d4316fc2a2e192f8e2 Mon Sep 17 00:00:00 2001 From: Ian Kumlien Date: Sun, 26 Jul 2020 16:01:15 +0200 Subject: [PATCH] Use maximum latency when determining L1/L0s ASPM Currently we check the maximum latency of upstream and downstream per link, not the maximum for the path This would work if all links have the same latency, but: endpoint -> c -> b -> a -> root (in the order we walk the path) If c or b has the higest latency, it will not register Fix this by maintaining the maximum latency value for the path Also, L0s seems to be a cumulative maximum over the path, fix this as well This change fixes a regression introduced (but not caused) by: 66ff14e59e8a (PCI/ASPM: Allow ASPM on links to PCIe-to-PCI/PCI-X Bridges) Signed-off-by: Ian Kumlien --- drivers/pci/pcie/aspm.c | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index b17e5ffd31b1..0d93ae065f73 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -434,7 +434,8 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev, static void pcie_aspm_check_latency(struct pci_dev *endpoint) { - u32 latency, l1_switch_latency = 0; + u32 latency, l1_max_latency = 0, l1_switch_latency = 0, + l0s_max_latency = 0; struct aspm_latency *acceptable; struct pcie_link_state *link; @@ -447,15 +448,24 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; while (link) { - /* Check upstream direction L0s latency */ - if ((link->aspm_capable & ASPM_STATE_L0S_UP) && - (link->latency_up.l0s > acceptable->l0s)) - link->aspm_capable &= ~ASPM_STATE_L0S_UP; - - /* Check downstream direction L0s latency */ - if ((link->aspm_capable & ASPM_STATE_L0S_DW) && - (link->latency_dw.l0s > acceptable->l0s)) - link->aspm_capable &= ~ASPM_STATE_L0S_DW; + if (link->aspm_capable & ASPM_STATE_L0S) { + u32 l0s_up = 0, l0s_dw = 0; + + /* Check upstream direction L0s latency */ + if (link->aspm_capable & ASPM_STATE_L0S_UP) + l0s_up = link->latency_up.l0s; + + /* Check downstream direction L0s latency */ + if (link->aspm_capable & ASPM_STATE_L0S_DW) + l0s_dw = link->latency_dw.l0s; + + l0s_max_latency += max_t(u32, l0s_up, l0s_dw); + + /* If the latency exceeds, disable both */ + if (l0s_max_latency > acceptable->l0s) + link->aspm_capable &= ~ASPM_STATE_L0S; + } + /* * Check L1 latency. * Every switch on the path to root complex need 1 @@ -469,11 +479,13 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) * L1 exit latencies advertised by a device include L1 * substate latencies (and hence do not do any check). */ - latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1); - if ((link->aspm_capable & ASPM_STATE_L1) && - (latency + l1_switch_latency > acceptable->l1)) - link->aspm_capable &= ~ASPM_STATE_L1; - l1_switch_latency += 1000; + if (link->aspm_capable & ASPM_STATE_L1) { + latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1); + l1_max_latency = max_t(u32, latency, l1_max_latency); + if (l1_max_latency + l1_switch_latency > acceptable->l1) + link->aspm_capable &= ~ASPM_STATE_L1; + l1_switch_latency += 1000; + } link = link->parent; } -- 2.28.0