From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:49:04
Hi,
This is v6 of the change to work around a PCIe link training phenomenon
where a pair of devices both capable of operating at a link speed above
2.5GT/s seems unable to negotiate the link speed and continues training
indefinitely with the Link Training bit switching on and off repeatedly
and the data link layer never reaching the active state.
Following Bjorn's suggestion from the previous iteration:
<https://lore.kernel.org/lkml/20221109050418.GA529724@bhelgaas/> I have
moved the workaround into the PCI core. I have kept the part specific to
ASMedia (to lift the speed restriction after a successful retrain) within,
although I find it a good candidate for a standalone quirk. It seems to
me we'd have to add additional classes of fixups however to move this part
to drivers/pci/quirks.c, which I think would be an overkill. So I've only
made it explicitly guarded by CONFIG_PCI_QUIRKS; I can see there's prior
art with this approach.
In the course of the update I have realised that commit 6b2f1351af56
("PCI: Wait for device to become ready after secondary bus reset") makes
no sense and was about to figure out what to do here about it, but then
found Lukas's recent patch series addressing this issue (thanks, Lukas,
you made my life easier!), so I have rebased my patch set on top of
Lukas's:
<https://lore.kernel.org/all/da77c92796b99ec568bd070cbe4725074a117038.1673769517.git.lukas@wunner.de/>.
This has resulted in mild ugliness in that `pcie_downstream_link_retrain'
may be called from `pci_bridge_wait_for_secondary_bus' twice, first time
via `pcie_wait_for_link_delay' and second time via `pci_dev_wait'. This
second call to `pcie_downstream_link_retrain' will do nothing, because for
`link_active_reporting' devices `pcie_wait_for_link_delay' will have
ensured the link has gone up or the second call won't have been reached.
I have also decided to move the initialisation of `link_active_reporting'
earlier on, so as to have a single way to check for the feature. This has
brought an extra patch and its 3 clean-up dependencies into the series.
This was originally observed in a configuration featuring a downstream
port of the ASMedia ASM2824 Gen 3 switch wired to the upstream port of the
Pericom PI7C9X2G304 Gen 2 switch. However in the course of review I have
come to the conclusion that similarly to the earlier similar change to
U-Boot it is indeed expected to be safe to apply this workaround to any
downstream port that has failed link negotiation provided that:
1. the port is capable of reporting the data link layer link active
status (because unlike U-Boot we cannot busy-loop continuously polling
the link training bit),
and:
2. we don't attempt to lift the 2.5GT/s speed restriction, imposed as the
basis of the workaround, for devices not explicitly known to continue
working in that case.
It is expected to be safe because the workaround is applied to a failed
link, that is one that does not (at the time this code is executed) work
anyway, so trying to bring it up cannot make the situation worse.
This has been verified with a SiFive HiFive unmatched board, with and
without CONFIG_PCI_QUIRKS enabled, booting with or without the workaround
activated in U-Boot, which covered both the link retraining part of the
quirk and the lifting of speed restriction already imposed by U-Boot.
I have also issued resets via sysfs to see how this change behaves. For
the problematic link this required a hack to remove a `dev->subordinate'
check from `pci_parent_bus_reset', which in turn triggered the workaround
as expected and brought the link up (but otherwise clobbered downstream
devices as one would inevitably expect).
I have no way to verify these patches with power management or hot-plug
events, but owing to Lukas's effort they get into the same infrastructure,
so I expect the workaround to do its job as expected. I note that there
is an extra call to `pcie_wait_for_link' from `pciehp_check_link_status',
but I expect it to work too. For `link_active_reporting' devices it will
call `pcie_downstream_link_retrain' and for`!link_active_reporting' ones
we have no means to do anything anyway.
The 3 extra clean-ups were only compile-tested (with PowerPC and x86-64
configurations, as appropriate), because I have no suitable hardware
available.
Please see individual change descriptions for further details.
Let me know if this is going in the right direction.
Maciej
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:49:07
Rename LINK_RETRAIN_TIMEOUT to PCIE_LINK_RETRAIN_TIMEOUT and make it
available via "pci.h" for PCI drivers to use.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
No change from v5.
New change in v5.
---
drivers/pci/pci.h | 2 ++
drivers/pci/pcie/aspm.c | 4 +---
2 files changed, 3 insertions(+), 3 deletions(-)
linux-pcie-link-retrain-timeout.diff
Index: linux-macro/drivers/pci/pci.h
===================================================================
@@ -213,7 +211,7 @@ static bool pcie_retrain_link(struct pci}/* Wait for link training end. Break out after waiting for timeout */-end_jiffies=jiffies+LINK_RETRAIN_TIMEOUT;+end_jiffies=jiffies+PCIE_LINK_RETRAIN_TIMEOUT;do{pcie_capability_read_word(parent,PCI_EXP_LNKSTA,®16);if(!(reg16&PCI_EXP_LNKSTA_LT))
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:49:13
Make `quirk_enable_clear_retrain_link' `pci_fixup_early' so that any later
fixups can rely on `clear_retrain_link' to have been already initialised.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
No change from v5.
New change in v5.
---
drivers/pci/quirks.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
linux-pcie-clear-retrain-link-early.diff
Index: linux-macro/drivers/pci/quirks.c
===================================================================
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:49:23
Determine whether Data Link Layer Link Active Reporting is available
ahead of calling any fixups so that the cached value can be used there
and later on.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
New change in v6.
---
drivers/pci/probe.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
linux-pcie-link-active-reporting-early.diff
Index: linux-macro/drivers/pci/probe.c
===================================================================
@@ -1828,6 +1827,7 @@ int pci_setup_device(struct pci_dev *devintpos=0;structpci_bus_regionregion;structresource*res;+u32linkcap;hdr_type=pci_hdr_type(dev);
@@ -1873,6 +1873,10 @@ int pci_setup_device(struct pci_dev *dev/* "Unknown power state" */dev->current_state=PCI_UNKNOWN;+/* Set it early to make it available to fixups, etc. */+pcie_capability_read_dword(dev,PCI_EXP_LNKCAP,&linkcap);+dev->link_active_reporting=!!(linkcap&PCI_EXP_LNKCAP_DLLLARC);+/* Early fixups, before probing the BARs */pci_fixup_device(pci_fixup_early,dev);
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:49:43
Use `link_active_reporting' to determine whether Data Link Layer Link
Active Reporting is available rather than re-retrieving the capability.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
NB this has been compile-tested only with a PPC64LE configuration.
New change in v6.
---
arch/powerpc/kernel/eeh_pe.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
linux-pcie-link-active-reporting-eeh.diff
Index: linux-macro/arch/powerpc/kernel/eeh_pe.c
===================================================================
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:49:55
Use `link_active_reporting' to determine whether Data Link Layer Link
Active Reporting is available rather than re-retrieving the capability.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
NB this has been compile-tested only with PPC64LE and x86-64
configurations.
New change in v6.
---
drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
linux-pcie-link-active-reporting-mlx5.diff
Index: linux-macro/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
===================================================================
@@ -294,7 +294,6 @@ static int mlx5_pci_link_toggle(struct munsignedlongtimeout;structpci_dev*sdev;intcap,err;-u32reg32;/* Check that all functions under the pci bridge are PFs of*thisdeviceotherwisefailthisfunction.
@@ -333,11 +332,8 @@ static int mlx5_pci_link_toggle(struct mreturnerr;/* Check link */-err=pci_read_config_dword(bridge,cap+PCI_EXP_LNKCAP,®32);-if(err)-returnerr;-if(!(reg32&PCI_EXP_LNKCAP_DLLLARC)){-mlx5_core_warn(dev,"No PCI link reporting capability (0x%08x)\n",reg32);+if(!bridge->link_active_reporting){+mlx5_core_warn(dev,"No PCI link reporting capability\n");msleep(1000);gotorestore;}
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:50:01
Use `link_active_reporting' to determine whether Data Link Layer Link
Active Reporting is available rather than re-retrieving the capability.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
NB this has been compile-tested only with PPC64LE and x86-64
configurations.
New change in v6.
---
drivers/pci/hotplug/pciehp_hpc.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
linux-pcie-link-active-reporting-hpc.diff
Index: linux-macro/drivers/pci/hotplug/pciehp_hpc.c
===================================================================
@@ -984,7 +984,7 @@ static inline int pcie_hotplug_depth(strstructcontroller*pcie_init(structpcie_device*dev){structcontroller*ctrl;-u32slot_cap,slot_cap2,link_cap;+u32slot_cap,slot_cap2;u8poweron;structpci_dev*pdev=dev->port;structpci_bus*subordinate=pdev->subordinate;
@@ -1030,9 +1030,6 @@ struct controller *pcie_init(struct pcieif(dmi_first_match(inband_presence_disabled_dmi_table))ctrl->inband_presence_disabled=1;-/* Check if Data Link Layer Link Active Reporting is implemented */-pcie_capability_read_dword(pdev,PCI_EXP_LNKCAP,&link_cap);-/* Clear all remaining event bits in Slot Status register. */pcie_capability_write_word(pdev,PCI_EXP_SLTSTA,PCI_EXP_SLTSTA_ABP|PCI_EXP_SLTSTA_PFD|
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-05 15:54:54
Attempt to handle cases such as with a downstream port of the ASMedia
ASM2824 PCIe switch where link training never completes and the link
continues switching between speeds indefinitely with the data link layer
never reaching the active state.
It has been observed with a downstream port of the ASMedia ASM2824 Gen 3
switch wired to the upstream port of the Pericom PI7C9X2G304 Gen 2
switch, using a Delock Riser Card PCI Express x1 > 2 x PCIe x1 device,
P/N 41433, wired to a SiFive HiFive Unmatched board. In this setup the
switches are supposed to negotiate the link speed of preferably 5.0GT/s,
falling back to 2.5GT/s.
Instead the link continues oscillating between the two speeds, at the
rate of 34-35 times per second, with link training reported repeatedly
active ~84% of the time. Forcibly limiting the target link speed to
2.5GT/s with the upstream ASM2824 device however makes the two switches
communicate correctly. Removing the speed restriction afterwards makes
the two devices switch to 5.0GT/s then.
Make use of these observations then and detect the inability to train
the link, by checking for the Data Link Layer Link Active status bit
being off while the Link Bandwidth Management Status indicating that
hardware has changed the link speed or width in an attempt to correct
unreliable link operation.
Restrict the speed to 2.5GT/s then with the Target Link Speed field,
request a retrain and wait 200ms for the data link to go up. If this
turns out successful, then lift the restriction, letting the devices
negotiate a higher speed.
Also check for a 2.5GT/s speed restriction the firmware may have already
arranged and lift it too with ports of devices known to continue working
afterwards, currently the ASM2824 only, that already report their data
link being up.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Link: https://lore.kernel.org/lkml/alpine.DEB.2.21.2203022037020.56670@angie.orcam.me.uk/
Link: https://source.denx.de/u-boot/u-boot/-/commit/a398a51ccc68
---
Changes from v5:
- Move from a quirk into PCI core and call at device probing, hot-plug,
reset and resume. Keep the ASMedia part under CONFIG_PCI_QUIRKS.
- Rely on `dev->link_active_reporting' rather than re-retrieving the
capability.
Changes from v4:
- Remove <linux/bug.h> inclusion no longer needed.
- Make the quirk generic based on probing device features rather than
specific to the ASM2824 part only; take the Retrain Link bit erratum
into account.
- Still lift the 2.5GT/s speed restriction with the ASM2824 only.
- Increase retrain timeout from 200ms to 1s (PCIE_LINK_RETRAIN_TIMEOUT).
- Remove retrain success notification.
- Use PCIe helpers rather than generic PCI functions throughout.
- Trim down and update the wording of the change description for the
switch from an ASM2824-specific to a generic fixup.
Changes from v3:
- Remove the <linux/pci_ids.h> entry for the ASM2824.
Changes from v2:
- Regenerate for 5.17-rc2 for a merge conflict.
- Replace BUG_ON for a missing PCI Express capability with WARN_ON and an
early return.
Changes from v1:
- Regenerate for a merge conflict.
---
drivers/pci/pci.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++--
drivers/pci/pci.h | 1
drivers/pci/probe.c | 2
3 files changed, 152 insertions(+), 5 deletions(-)
linux-pcie-asm2824-manual-retrain.diff
Index: linux-macro/drivers/pci/pci.c
===================================================================
@@ -859,6 +859,132 @@ int pci_wait_for_pending(struct pci_devreturn0;}+/*+*RetrainthelinkofadownstreamPCIeportbyhandifnecessary.+*+*ThisisneededatleastwhereadownstreamportoftheASMediaASM2824+*Gen3switchiswiredtotheupstreamportofthePericomPI7C9X2G304+*Gen2switch,andobservedwiththeDelockRiserCardPCIExpressx1>+*2xPCIex1device,P/N41433,pluggedintotheSiFiveHiFiveUnmatched+*board.+*+*Insuchaconfigurationtheswitchesaresupposedtonegotiatethelink+*speedofpreferably5.0GT/s,fallingbackto2.5GT/s.Howeverthelink+*continuesswitchingbetweenthetwospeedsindefinitelyandthedata+*linklayerneverreachestheactivestate,withlinktrainingreported+*repeatedlyactive~84%ofthetime.Forcingthetargetlinkspeedto+*2.5GT/swiththeupstreamASM2824devicemakesthetwoswitchestalkto+*eachothercorrectlyhowever.Andmoreinterestinglyretrainingwitha+*highertargetlinkspeedafterwardsletsthetwosuccessfullynegotiate+*5.0GT/s.+*+*WiththeASM2824wecanrelyontheotherwiseoptionalDataLinkLayer+*LinkActivestatusbitandinthefailedlinktrainingscenarioitwill+*beoffalongwiththeLinkBandwidthManagementStatusindicatingthat+*hardwarehaschangedthelinkspeedorwidthinanattempttocorrect+*unreliablelinkoperation.Foraportthathasbeenleftunconnected+*bothbitswillbeclear.Sousethisinformationtodetecttheproblem+*ratherthanpollingtheLinkTrainingbitandwatchingoutforflipsor+*atleasttheactivestatus.+*+*Sincetheexactnatureoftheproblemisn'tknownandinprinciplethis+*couldtriggerwhereanASM2824deviceisdownstreamratherupstream,+*applythiserratumworkaroundtoanydownstreamportsaslongasthey+*supportLinkActivereportingandhavetheLinkControl2register.+*Restrictthespeedto2.5GT/sthenwiththeTargetLinkSpeedfield,+*requestaretrainandwait200msforthedatalinktogoup.+*+*IfthisturnsoutsuccessfulandweknowbytheVendor:DeviceIDitis+*safetodoso,thenlifttherestriction,lettingthedevicesnegotiate+*ahigherspeed.Alsocheckforasimilar2.5GT/sspeedrestrictionthe+*firmwaremayhavealreadyarrangedandliftitwithportsthatalready+*reporttheirdatalinkbeingup.+*+*Return0ifthelinkhasbeensuccessfullyretrained,otherwise-1.+*/+intpcie_downstream_link_retrain(structpci_dev*dev)+{+staticconststructpci_device_idids[]={+{PCI_VDEVICE(ASMEDIA,0x2824)},/* ASMedia ASM2824 */+{}+};+u16lnksta,lnkctl2;++if(!pci_is_pcie(dev)||!pcie_downstream_port(dev)||+!pcie_cap_has_lnkctl2(dev)||!dev->link_active_reporting)+return-1;++pcie_capability_read_word(dev,PCI_EXP_LNKCTL2,&lnkctl2);+pcie_capability_read_word(dev,PCI_EXP_LNKSTA,&lnksta);+if((lnksta&(PCI_EXP_LNKSTA_LBMS|PCI_EXP_LNKSTA_DLLLA))==+PCI_EXP_LNKSTA_LBMS){+unsignedlongtimeout;+u16lnkctl;++pci_info(dev,"broken device, retraining non-functional downstream link at 2.5GT/s\n");++pcie_capability_read_word(dev,PCI_EXP_LNKCTL,&lnkctl);+lnkctl|=PCI_EXP_LNKCTL_RL;+lnkctl2&=~PCI_EXP_LNKCTL2_TLS;+lnkctl2|=PCI_EXP_LNKCTL2_TLS_2_5GT;+pcie_capability_write_word(dev,PCI_EXP_LNKCTL2,lnkctl2);+pcie_capability_write_word(dev,PCI_EXP_LNKCTL,lnkctl);+/*+*DuetoanerratuminsomedevicestheRetrainLinkbit+*needstobeclearedagainmanuallytoallowthelink+*trainingtosucceed.+*/+lnkctl&=~PCI_EXP_LNKCTL_RL;+if(dev->clear_retrain_link)+pcie_capability_write_word(dev,PCI_EXP_LNKCTL,+lnkctl);++timeout=jiffies+PCIE_LINK_RETRAIN_TIMEOUT;+do{+pcie_capability_read_word(dev,PCI_EXP_LNKSTA,+&lnksta);+if(lnksta&PCI_EXP_LNKSTA_DLLLA)+break;+usleep_range(10000,20000);+}while(time_before(jiffies,timeout));++if(!(lnksta&PCI_EXP_LNKSTA_DLLLA)){+pci_info(dev,"retraining failed\n");+return-1;+}+}++if(IS_ENABLED(CONFIG_PCI_QUIRKS)&&(lnksta&PCI_EXP_LNKSTA_DLLLA)&&+(lnkctl2&PCI_EXP_LNKCTL2_TLS)==PCI_EXP_LNKCTL2_TLS_2_5GT&&+pci_match_id(ids,dev)){+u32lnkcap;+u16lnkctl;++pci_info(dev,"removing 2.5GT/s downstream link speed restriction\n");+pcie_capability_read_dword(dev,PCI_EXP_LNKCAP,&lnkcap);+pcie_capability_read_word(dev,PCI_EXP_LNKCTL,&lnkctl);+lnkctl|=PCI_EXP_LNKCTL_RL;+lnkctl2&=~PCI_EXP_LNKCTL2_TLS;+lnkctl2|=lnkcap&PCI_EXP_LNKCAP_SLS;+pcie_capability_write_word(dev,PCI_EXP_LNKCTL2,lnkctl2);+pcie_capability_write_word(dev,PCI_EXP_LNKCTL,lnkctl);+}++return0;+}++/* Same as above, but called for a downstream device. */+staticintpcie_upstream_link_retrain(structpci_dev*dev)+{+structpci_dev*bridge;++bridge=pci_upstream_bridge(dev);+if(bridge)+returnpcie_downstream_link_retrain(bridge);+else+return-1;+}+staticintpci_acs_enable;/**
On Sun, Feb 05, 2023 at 03:49:21PM +0000, Maciej W. Rozycki wrote:
Use `link_active_reporting' to determine whether Data Link Layer Link
Active Reporting is available rather than re-retrieving the capability.
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Reviewed-by: Lukas Wunner <lukas@wunner.de>
I believe this should work without the preceding patches in the series,
hence can be applied independently.
Thanks,
Lukas
@@ -984,7 +984,7 @@ static inline int pcie_hotplug_depth(strstructcontroller*pcie_init(structpcie_device*dev){structcontroller*ctrl;-u32slot_cap,slot_cap2,link_cap;+u32slot_cap,slot_cap2;u8poweron;structpci_dev*pdev=dev->port;structpci_bus*subordinate=pdev->subordinate;
@@ -1030,9 +1030,6 @@ struct controller *pcie_init(struct pcieif(dmi_first_match(inband_presence_disabled_dmi_table))ctrl->inband_presence_disabled=1;-/* Check if Data Link Layer Link Active Reporting is implemented */-pcie_capability_read_dword(pdev,PCI_EXP_LNKCAP,&link_cap);-/* Clear all remaining event bits in Slot Status register. */pcie_capability_write_word(pdev,PCI_EXP_SLTSTA,PCI_EXP_SLTSTA_ABP|PCI_EXP_SLTSTA_PFD|
From: "Maciej W. Rozycki" <macro@orcam.me.uk> Date: 2023-02-19 18:53:08
On Sun, 5 Feb 2023, Maciej W. Rozycki wrote:
This is v6 of the change to work around a PCIe link training phenomenon
where a pair of devices both capable of operating at a link speed above
2.5GT/s seems unable to negotiate the link speed and continues training
indefinitely with the Link Training bit switching on and off repeatedly
and the data link layer never reaching the active state.