Re: [PATCH v3 3/4] PCI: Disable ACS SV capability for the broken IDT switches

Bjorn Helgaas
2026-02-05T23:39:22Z

[+cc Alex, James (author of aa667c6408d2)]

On Fri, Jan 02, 2026 at 09:04:49PM +0530, Manivannan Sadhasivam wrote:
> Some IDT switches behave erratically when ACS Source Validation is enabled.
> For example, they incorrectly flag an ACS Source Validation error on
> completions for config read requests even though PCIe r4.0, sec 6.12.1.1,
> says that completions are never affected by ACS Source Validation.
>
> Even though IDT suggests working around this issue by issuing a config
> write before the first config read, so that the device caches the bus and
> device number. But it would still be fragile since the device could loose
> the IDs after the reset and any further access may trigger ACS SV
> violation.
>
> Hence, to properly fix the issue, the respective capability needs to be
> disabled. Since the ACS Capabilities are RO values, and are cached in the
> 'pci_dev::acs_capabilities' field, remove the cached broken capabilities by
> calling pci_disable_broken_acs_cap() from pci_acs_init(). This will allow
> pci_enable_acs() helper to disable the relevant ACS ctrls.
>
> With this, the previous workaround can now be safely removed.

James added the existing IDT workaround for the IDT 0x80b5 switch,
which was merged as aa667c6408d2 ("PCI: Workaround IDT switch ACS
Source Validation erratum").

I guess these last three patches:

PCI: Cache ACS capabilities
PCI: Disable ACS SV capability for the broken IDT switches
PCI: Extend the pci_disable_broken_acs_cap() quirk for one more IDT switch

are basically to fix the same issue on the IDT 0x8090 switch?

The obvious approach would be to extend the test in
pci_bus_read_dev_vendor_id() to check for 0x8090 in addition to
0x80b5, which might be worth doing first, before changing the whole
approach.

I guess your point here is that the existing workaround isn't enough
even for 0x80b5 because it doesn't handle the reset case? The patch
changelogs after v9 of the original patch (see below) do mention that
FLR and hot reset were tested and didn't seem to require the
workaround. But you've probably seen problems after reset?

I think it's worth a note about why the reset case can't be fixed
similarly to the enumeration case.

This patch gives up on ACS SV completely for this switch instead of
the current approach of:

- disabling ACS SV
- doing a config write
- reading dev/vendor ID
- re-enabling ACS SV

It'd be worth expanding on this and what the effect of avoiding ACS SV
is. Does this change which devices can be safely passed through to
virtual guests? Does it give up isolation that users expect?

I also couldn't remember why we can't just do a config write before
reading the vendor/dev ID of a device below the switch, which is
addressed in the discussion of v3 (below). Basically if the device
isn't yet ready to accept config accesses, it may not latch the bus
number, and we can't really tell when it *is* ready.

I collected up all the history I could find about the original change:

v3 2017-06-09 https://lore.kernel.org/all/20170609231617.20243-1-yinghai@kernel.org/
doing config write is hard because we don't know when the device
is Configuration-Ready and can capture the bus number:
https://lore.kernel.org/all/20170613221451.GC7012@bhelgaas-glaptop.roam.corp.google.com/
v4 2017-06-27 https://lore.kernel.org/all/5952D144.8060609@oracle.com/
only do workaround for IDT switches
v5 2017-07-06 https://lore.kernel.org/all/595E610A.5000900@oracle.com/
skip devices that don't advertise ACS SV support
v7 2017-09-18 https://lore.kernel.org/all/59C02719.5050103@oracle.com/
add https://bugzilla.kernel.org/show_bug.cgi?id=196979 with
details about how Windows deals with this
v8 2018-03-06 https://lore.kernel.org/all/ce8e9a3e-474d-961c-eb0a-c021077f2dca@oracle.com/
v9 ? (couldn't find this)
tested FLR and hot reset scenarios; didn't see issues where
workaround was required
v11 2018-04-11 https://lore.kernel.org/all/e6a55432-8c56-9c99-ce99-4ae80a0ed3ba@oracle.com/ (no version label)
2018-04-19 https://lore.kernel.org/all/b6439e86-2284-5b3c-3656-a0c3c2568317@oracle.com/ (no version label)
v12 2018-04-24 https://lore.kernel.org/all/7e9f5905-34c5-f24c-8b13-df36b8bf3621@oracle.com/
v13 2018-04-30 https://lore.kernel.org/all/4bd28931-8e8d-6bfe-a98c-49f8e2778c6e@oracle.com/ (labeled v2)
v14 2018-07-09 https://lore.kernel.org/all/4117c119-c754-bf4e-544c-19cb6e239f38@oracle.com/

> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---
> drivers/pci/pci.c | 1 +
> drivers/pci/pci.h | 3 ++-
> drivers/pci/probe.c | 12 -----------
> drivers/pci/quirks.c | 61 ++++++++++++----------------------------------------
> 4 files changed, 17 insertions(+), 60 deletions(-)
>
> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index d89b04451aea..e16229e7ff6f 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -3652,6 +3652,7 @@ void pci_acs_init(struct pci_dev *dev)
> return;
>
> pci_read_config_word(dev, pos + PCI_ACS_CAP, &dev->acs_capabilities);
> + pci_disable_broken_acs_cap(dev);
> }
>
> /**
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index 4592ede0ebcc..5fe5d6e84c95 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -432,7 +432,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
> int rrs_timeout);
> bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
> int rrs_timeout);
> -int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *pl, int rrs_timeout);
>
> int pci_setup_device(struct pci_dev *dev);
> void __pci_size_stdbars(struct pci_dev *dev, int count,
> @@ -944,6 +943,7 @@ void pci_enable_acs(struct pci_dev *dev);
> int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
> int pci_dev_specific_enable_acs(struct pci_dev *dev);
> int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
> +void pci_disable_broken_acs_cap(struct pci_dev *pdev);
> int pcie_failed_link_retrain(struct pci_dev *dev);
> #else
> static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
> @@ -959,6 +959,7 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
> {
> return -ENOTTY;
> }
> +static inline void pci_disable_broken_acs_cap(struct pci_dev *dev) { }
> static inline int pcie_failed_link_retrain(struct pci_dev *dev)
> {
> return -ENOTTY;
> diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
> index 41183aed8f5d..c7304ac5afc2 100644
> --- a/drivers/pci/probe.c
> +++ b/drivers/pci/probe.c
> @@ -2547,18 +2547,6 @@ bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
> bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
> int timeout)
> {
> -#ifdef CONFIG_PCI_QUIRKS
> - struct pci_dev *bridge = bus->self;
> -
> - /*
> - * Certain IDT switches have an issue where they improperly trigger
> - * ACS Source Validation errors on completions for config reads.
> - */
> - if (bridge && bridge->vendor == PCI_VENDOR_ID_IDT &&
> - bridge->device == 0x80b5)
> - return pci_idt_bus_quirk(bus, devfn, l, timeout);
> -#endif
> -
> return pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
> }
> EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index b9c252aa6fe0..1571a2ef331e 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -5778,58 +5778,25 @@ DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
> PCI_BASE_CLASS_DISPLAY, 16, quirk_nvidia_hda);
>
> /*
> - * Some IDT switches incorrectly flag an ACS Source Validation error on
> - * completions for config read requests even though PCIe r4.0, sec
> - * 6.12.1.1, says that completions are never affected by ACS Source
> - * Validation. Here's the text of IDT 89H32H8G3-YC, erratum #36:
> + * Some IDT switches behave erratically when ACS Source Validation is enabled.
> + * For example, they incorrectly flag an ACS Source Validation error on
> + * completions for config read requests even though PCIe r4.0, sec 6.12.1.1,
> + * says that completions are never affected by ACS Source Validation.
> *
> - * Item #36 - Downstream port applies ACS Source Validation to Completions
> - * Section 6.12.1.1 of the PCI Express Base Specification 3.1 states that
> - * completions are never affected by ACS Source Validation. However,
> - * completions received by a downstream port of the PCIe switch from a
> - * device that has not yet captured a PCIe bus number are incorrectly
> - * dropped by ACS Source Validation by the switch downstream port.
> + * Even though IDT suggests working around this issue by issuing a config write
> + * before the first config read, so that the switch caches the bus and device
> + * number, it would still be fragile since the device could loose the IDs after
> + * the reset.
> *
> - * The workaround suggested by IDT is to issue a config write to the
> - * downstream device before issuing the first config read. This allows the
> - * downstream device to capture its bus and device numbers (see PCIe r4.0,
> - * sec 2.2.9), thus avoiding the ACS error on the completion.
> - *
> - * However, we don't know when the device is ready to accept the config
> - * write, so we do config reads until we receive a non-Config Request Retry
> - * Status, then do the config write.
> - *
> - * To avoid hitting the erratum when doing the config reads, we disable ACS
> - * SV around this process.
> + * Hence, a reliable fix would be to assume that these switches don't support
> + * ACS SV.
> */
> -int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout)
> +void pci_disable_broken_acs_cap(struct pci_dev *pdev)
> {
> - int pos;
> - u16 ctrl = 0;
> - bool found;
> - struct pci_dev *bridge = bus->self;
> -
> - pos = bridge->acs_cap;
> -
> - /* Disable ACS SV before initial config reads */
> - if (pos) {
> - pci_read_config_word(bridge, pos + PCI_ACS_CTRL, &ctrl);
> - if (ctrl & PCI_ACS_SV)
> - pci_write_config_word(bridge, pos + PCI_ACS_CTRL,
> - ctrl & ~PCI_ACS_SV);
> + if (pdev->vendor == PCI_VENDOR_ID_IDT && pdev->device == 0x80b5) {
> + pci_info(pdev, "Disabling broken ACS SV\n");
> + pdev->acs_capabilities &= ~PCI_ACS_SV;
> }
> -
> - found = pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
> -
> - /* Write Vendor ID (read-only) so the endpoint latches its bus/dev */
> - if (found)
> - pci_bus_write_config_word(bus, devfn, PCI_VENDOR_ID, 0);
> -
> - /* Re-enable ACS_SV if it was previously enabled */
> - if (ctrl & PCI_ACS_SV)
> - pci_write_config_word(bridge, pos + PCI_ACS_CTRL, ctrl);
> -
> - return found;
> }
>
> /*
>
> --
> 2.48.1
>