[PATCH v5 03/18] PCI: Propagate FLR return values to callers
Nicolin Chen
nicolinc at nvidia.com
Thu Jul 2 21:06:28 PDT 2026
A reset failure implies that the device might be unreliable. E.g. its ATC
might still retain stale entries. Thus, the IOMMU layer cannot trust this
device to resume its ATS function that can lead to memory corruption. So,
a subsequent change to pci_dev_reset_iommu_done() will keep such a failed
device blocked instead of recovering its IOMMU pathway.
Several quirk functions in the pci_dev_reset_methods array call pcie_flr()
but discard its return value and unconditionally report success, hiding a
failed reset from __pci_reset_function_locked(). Return the value instead.
Nothing is short-circuited on failure: each quirk still runs its restore or
settle steps as before, and reset_hinic_vf_dev() also reports its firmware
FLR-completion timeout, keeping an earlier pcie_flr() error if any.
Note that a kept pcie_flr() error does not misreport a normal reset: as per
the erratum in commit 411e2a43d210e ("PCI: Work around Huawei Intelligent
NIC VF FLR erratum"), the VF responds to config reads before its firmware
completes the reset processing, so pci_dev_wait() returns promptly instead
of timing out.
However, pcie_flr() reports a device that did not come back after the FLR
as -ENOTTY, and __pci_reset_function_locked() takes any -ENOTTY as "try the
next method". Since these quirks have always returned success, escalating
would be a new and harmful behavior: the next method is typically the plain
"flr" method, which would re-run the same FLR without the extra steps the
quirk wraps around its pcie_flr() call. Add quirk_flr_err() converting that
-ENOTTY to -ETIMEDOUT, so the cascade still stops at the quirk as before.
The early "not applicable" -ENOTTY returns are left intact, and they still
ask the caller to try the next method.
Convert it at the quirk level rather than in pcie_flr() or pci_dev_wait(),
because those also serve the native "flr"/"af_flr"/"pm" methods, where the
-ENOTTY on timeout is deliberate per commit 91295d79d658 ("PCI: Handle FLR
failure and allow other reset types"): a timed-out native FLR escalates to
a stronger reset (e.g. bus reset), which skips no workaround there and may
recover the device. Converting in the core would also change the value seen
by every direct pcie_flr() caller.
This is not a bug fix, since these functions have always returned success
and the propagated values are only consumed by incoming work.
Suggested-by: Kevin Tian <kevin.tian at intel.com>
Assisted-by: Claude:claude-fable-5
Signed-off-by: Nicolin Chen <nicolinc at nvidia.com>
---
drivers/pci/quirks.c | 43 ++++++++++++++++++++++++++++++-------------
1 file changed, 30 insertions(+), 13 deletions(-)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 8ecd1bc561d28..7f6d1574fe2bf 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3934,6 +3934,18 @@ DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
* reset a single function if other methods (e.g. FLR, PM D0->D3) are
* not available.
*/
+
+/*
+ * pcie_flr() reports a device that did not come back after the FLR as -ENOTTY.
+ * The caller __pci_reset_function_locked() takes that as "try the next method"
+ * and ignores the error, which is wrong for the quirk functions below. Return
+ * -ETIMEDOUT instead.
+ */
+static int quirk_flr_err(int err)
+{
+ return err == -ENOTTY ? -ETIMEDOUT : err;
+}
+
static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, bool probe)
{
/*
@@ -3945,7 +3957,7 @@ static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, bool probe)
* supported.
*/
if (!probe)
- pcie_flr(dev);
+ return quirk_flr_err(pcie_flr(dev));
return 0;
}
@@ -4003,6 +4015,7 @@ static int reset_chelsio_generic_dev(struct pci_dev *dev, bool probe)
{
u16 old_command;
u16 msix_flags;
+ int ret;
/*
* If this isn't a Chelsio T4-based device, return -ENOTTY indicating
@@ -4048,16 +4061,15 @@ static int reset_chelsio_generic_dev(struct pci_dev *dev, bool probe)
PCI_MSIX_FLAGS_ENABLE |
PCI_MSIX_FLAGS_MASKALL);
- pcie_flr(dev);
+ ret = quirk_flr_err(pcie_flr(dev));
/*
* Restore the configuration information (BAR values, etc.) including
- * the original PCI Configuration Space Command word, and return
- * success.
+ * the original PCI Configuration Space Command word.
*/
pci_restore_state(dev);
pci_write_config_word(dev, PCI_COMMAND, old_command);
- return 0;
+ return ret;
}
#define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed
@@ -4140,9 +4152,7 @@ static int nvme_disable_and_flr(struct pci_dev *dev, bool probe)
pci_iounmap(dev, bar);
- pcie_flr(dev);
-
- return 0;
+ return quirk_flr_err(pcie_flr(dev));
}
/*
@@ -4154,14 +4164,17 @@ static int nvme_disable_and_flr(struct pci_dev *dev, bool probe)
*/
static int delay_250ms_after_flr(struct pci_dev *dev, bool probe)
{
+ int ret;
+
if (probe)
return pcie_reset_flr(dev, PCI_RESET_PROBE);
- pcie_reset_flr(dev, PCI_RESET_DO_RESET);
+ ret = quirk_flr_err(pcie_reset_flr(dev, PCI_RESET_DO_RESET));
+ /* Settle the device even on a failed FLR */
msleep(250);
- return 0;
+ return ret;
}
#define PCI_DEVICE_ID_HINIC_VF 0x375E
@@ -4177,6 +4190,7 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
unsigned long timeout;
void __iomem *bar;
u32 val;
+ int ret;
if (probe)
return 0;
@@ -4197,12 +4211,13 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
val = val | HINIC_VF_FLR_PROC_BIT;
iowrite32be(val, bar + HINIC_VF_OP);
- pcie_flr(pdev);
+ ret = quirk_flr_err(pcie_flr(pdev));
/*
* The device must recapture its Bus and Device Numbers after FLR
* in order generate Completions. Issue a config write to let the
- * device capture this information.
+ * device capture this information. Note that pcie_flr() can fail
+ * after the reset is asserted. So, recapture it unconditionally.
*/
pci_write_config_word(pdev, PCI_VENDOR_ID, 0);
@@ -4220,11 +4235,13 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
goto reset_complete;
pci_warn(pdev, "Reset dev timeout, FLR ack reg: %#010x\n", val);
+ /* Preserve pcie_flr()'s error if it failed before the device ack stage */
+ ret = ret ? : -ETIMEDOUT;
reset_complete:
pci_iounmap(pdev, bar);
- return 0;
+ return ret;
}
static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
--
2.43.0
More information about the linux-arm-kernel
mailing list