[PATCH] PCI: Add ID@path device format for pci= parameters
From: Ben Cressey
Date: Sat Sep 19 2026 - 16:56:38 EST
The <pci_dev> grammar shared by pci=resource_alignment=,
pci=disable_acs_redir= and pci=config_acs= names a device either by
address, with an optional path, or by ID. When one command line serves
more than one platform, neither form limits an entry to the intended
device: an address applies to whatever occupies it on each platform, and
an ID applies to every instance, such as all Downstream Ports of a
switch rather than the one that needs different ACS settings.
Accept pci:<vendor>:<device>[:<subvendor>:<subdevice>]@<path>, where
<path> is the address format, and match only when both halves match. An
entry written for one platform is then inert on another where that
address holds a different device. For example,
pci=config_acs=xx111x1@pci:10b5:8749@0000:80:02.0/00.0/08.0
configures ACS on the Downstream Port at that path only if it is a
PEX 8749 port, and matches nothing on a system where 0000:80:02.0 leads
to some other device.
The path goes last since pci_dev_str_match_path() consumes up to the
next ';'. The <order>@ and <flags>@ prefixes of resource_alignment and
config_acs are stripped before pci_dev_str_match() is called, so the
callers are unchanged.
Reviewed-by: Anthony Vardaro (Anthropic) <me@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Ben Cressey <ben@xxxxxxxxxxx>
Assisted-by: LLM
---
This touches pci_dev_str_match(), as does 3/6 of Wei Wang's "PCI: Add
support for ACS Enhanced Capability" series, currently at v9:
https://lore.kernel.org/r/SI2PR01MB4393F337A792CB3258C2E073DCB62@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
On current -rc this patch applies as is. If it is applied after that
3/6, the early return for a path that does not match needs to become
"ret = 0; goto out;" so the shared exit still checks and skips the
delimiter. I can respin on that base if preferred.
A pci:<IDs> entry with three or five fields is accepted by the existing
sscanf() and so bypasses the @<path> check; that predates this patch and
is rejected once that same 3/6, "PCI: Consolidate delimiter handling
into pci_dev_str_match()", is applied.
A backport of this change to a 6.18.y based tree was tested on two
server models from different vendors, both with PCIe switches whose
downstream ports share one vendor and device ID, and one boot of each
machine was examined with one command line that held config_acs entries
in the new form for both models. On each machine only the ports named
by its own entries were configured, and the bits those entries set read
back as requested, while the ports with the same vendor and device ID
at a path that only the other model's entries name were not configured.
---
Documentation/admin-guide/kernel-parameters.txt | 10 +++++++++-
drivers/pci/pci.c | 18 ++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 33cd30996e47e..3e4c618427a27 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -5042,6 +5042,7 @@ Kernel parameters
[<domain>:]<bus>:<dev>.<func>[/<dev>.<func>]*
pci:<vendor>:<device>[:<subvendor>:<subdevice>]
+ pci:<vendor>:<device>[:<subvendor>:<subdevice>]@<path>
Note: the first format specifies a PCI
bus/device/function address which may change
@@ -5056,7 +5057,14 @@ Kernel parameters
renumbering issues). The second format
selects devices using IDs from the
configuration space which may match multiple
- devices in the system.
+ devices in the system. The third format
+ combines the first two: the device must
+ match the IDs and also be at the address
+ given by <path>, which takes the first
+ format. This allows an entry written for a
+ device on one system to have no effect on
+ another system where the same address is
+ occupied by a different device.
earlydump dump PCI config space before the kernel
changes anything
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b2879a6be5f80..ebce05e604b88 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -343,6 +343,7 @@ static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
*
* [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
* pci:<vendor>:<device>[:<subvendor>:<subdevice>]
+ * pci:<vendor>:<device>[:<subvendor>:<subdevice>]@<path>
*
* The first format specifies a PCI bus/device/function address which
* may change if new hardware is inserted, if motherboard firmware changes,
@@ -359,6 +360,12 @@ static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
* legacy reasons and convenience so users don't have to specify
* FFFFFFFFs on the command line.)
*
+ * The third format combines the first two: the device must match the IDs
+ * and also be at the address given by <path>, which takes the first
+ * format. This allows a parameter written for a device at a known
+ * address on one system to have no effect on another system where that
+ * address is occupied by a different device.
+ *
* Returns 1 if the string matches the device, 0 if it does not and
* a negative error code if the string cannot be parsed.
*/
@@ -385,6 +392,17 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p,
p += count;
+ /* Third format: the device must also be at <path> */
+ if (*p == '@') {
+ ret = pci_dev_str_match_path(dev, p + 1, &p);
+ if (ret < 0)
+ return ret;
+ if (!ret) {
+ *endptr = p;
+ return 0;
+ }
+ }
+
if ((!vendor || vendor == dev->vendor) &&
(!device || device == dev->device) &&
(!subsystem_vendor ||
---
base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
change-id: 20260919-pci-idpath-selector-2847a8afe92c