Skip to content

Commit 45db337

Browse files
lsgunthbjorn-helgaas
authored andcommitted
PCI: Allow specifying devices using a base bus and path of devfns
When specifying PCI devices on the kernel command line using a bus/device/function address, bus numbers can change when adding or replacing a device, changing motherboard firmware, or applying kernel parameters like "pci=assign-buses". When bus numbers change, it's likely the command line tweak will be applied to the wrong device. Therefore, it is useful to be able to specify devices with a base bus number and the path of devfns needed to get to it, similar to the "device scope" structure in the Intel VT-d spec, Section 8.3.1. Thus, we add an option to specify devices in the following format: [<domain>:]<bus>:<device>.<func>[/<device>.<func>]* The path can be any segment within the PCI hierarchy of any length and determined through the use of 'lspci -t'. When specified this way, it is less likely that a renumbered bus will result in a valid device specification and the tweak won't be applied to the wrong device. Signed-off-by: Logan Gunthorpe <logang@deltatee.com> [bhelgaas: use "device" instead of "slot" in documentation since that's the usual language in the PCI specs] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Stephen Bates <sbates@raithlin.com> Reviewed-by: Alex Williamson <alex.williamson@redhat.com> Acked-by: Christian König <christian.koenig@amd.com>
1 parent 07d8d7e commit 45db337

File tree

2 files changed

+103
-23
lines changed

2 files changed

+103
-23
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3000,7 +3000,7 @@
30003000
or a set of devices (<pci_dev>). These are
30013001
specified in one of the following formats:
30023002

3003-
[<domain>:]<bus>:<device>.<func>
3003+
[<domain>:]<bus>:<dev>.<func>[/<dev>.<func>]*
30043004
pci:<vendor>:<device>[:<subvendor>:<subdevice>]
30053005

30063006
Note: the first format specifies a PCI
@@ -3009,7 +3009,11 @@
30093009
firmware changes, or due to changes caused
30103010
by other kernel parameters. If the
30113011
domain is left unspecified, it is
3012-
taken to be zero. The second format
3012+
taken to be zero. Optionally, a path
3013+
to a device through multiple device/function
3014+
addresses can be specified after the base
3015+
address (this is more robust against
3016+
renumbering issues). The second format
30133017
selects devices using IDs from the
30143018
configuration space which may match multiple
30153019
devices in the system.

drivers/pci/pci.c

Lines changed: 97 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,89 @@ void __iomem *pci_ioremap_wc_bar(struct pci_dev *pdev, int bar)
191191
EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar);
192192
#endif
193193

194+
/**
195+
* pci_dev_str_match_path - test if a path string matches a device
196+
* @dev: the PCI device to test
197+
* @p: string to match the device against
198+
* @endptr: pointer to the string after the match
199+
*
200+
* Test if a string (typically from a kernel parameter) formatted as a
201+
* path of device/function addresses matches a PCI device. The string must
202+
* be of the form:
203+
*
204+
* [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
205+
*
206+
* A path for a device can be obtained using 'lspci -t'. Using a path
207+
* is more robust against bus renumbering than using only a single bus,
208+
* device and function address.
209+
*
210+
* Returns 1 if the string matches the device, 0 if it does not and
211+
* a negative error code if it fails to parse the string.
212+
*/
213+
static int pci_dev_str_match_path(struct pci_dev *dev, const char *path,
214+
const char **endptr)
215+
{
216+
int ret;
217+
int seg, bus, slot, func;
218+
char *wpath, *p;
219+
char end;
220+
221+
*endptr = strchrnul(path, ';');
222+
223+
wpath = kmemdup_nul(path, *endptr - path, GFP_KERNEL);
224+
if (!wpath)
225+
return -ENOMEM;
226+
227+
while (1) {
228+
p = strrchr(wpath, '/');
229+
if (!p)
230+
break;
231+
ret = sscanf(p, "/%x.%x%c", &slot, &func, &end);
232+
if (ret != 2) {
233+
ret = -EINVAL;
234+
goto free_and_exit;
235+
}
236+
237+
if (dev->devfn != PCI_DEVFN(slot, func)) {
238+
ret = 0;
239+
goto free_and_exit;
240+
}
241+
242+
/*
243+
* Note: we don't need to get a reference to the upstream
244+
* bridge because we hold a reference to the top level
245+
* device which should hold a reference to the bridge,
246+
* and so on.
247+
*/
248+
dev = pci_upstream_bridge(dev);
249+
if (!dev) {
250+
ret = 0;
251+
goto free_and_exit;
252+
}
253+
254+
*p = 0;
255+
}
256+
257+
ret = sscanf(wpath, "%x:%x:%x.%x%c", &seg, &bus, &slot,
258+
&func, &end);
259+
if (ret != 4) {
260+
seg = 0;
261+
ret = sscanf(wpath, "%x:%x.%x%c", &bus, &slot, &func, &end);
262+
if (ret != 3) {
263+
ret = -EINVAL;
264+
goto free_and_exit;
265+
}
266+
}
267+
268+
ret = (seg == pci_domain_nr(dev->bus) &&
269+
bus == dev->bus->number &&
270+
dev->devfn == PCI_DEVFN(slot, func));
271+
272+
free_and_exit:
273+
kfree(wpath);
274+
return ret;
275+
}
276+
194277
/**
195278
* pci_dev_str_match - test if a string matches a device
196279
* @dev: the PCI device to test
@@ -200,13 +283,16 @@ EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar);
200283
* Test if a string (typically from a kernel parameter) matches a specified
201284
* PCI device. The string may be of one of the following formats:
202285
*
203-
* [<domain>:]<bus>:<device>.<func>
286+
* [<domain>:]<bus>:<device>.<func>[/<device>.<func>]*
204287
* pci:<vendor>:<device>[:<subvendor>:<subdevice>]
205288
*
206289
* The first format specifies a PCI bus/device/function address which
207290
* may change if new hardware is inserted, if motherboard firmware changes,
208291
* or due to changes caused in kernel parameters. If the domain is
209-
* left unspecified, it is taken to be 0.
292+
* left unspecified, it is taken to be 0. In order to be robust against
293+
* bus renumbering issues, a path of PCI device/function numbers may be used
294+
* to address the specific device. The path for a device can be determined
295+
* through the use of 'lspci -t'.
210296
*
211297
* The second format matches devices using IDs in the configuration
212298
* space which may match multiple devices in the system. A value of 0
@@ -222,7 +308,7 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p,
222308
const char **endptr)
223309
{
224310
int ret;
225-
int seg, bus, slot, func, count;
311+
int count;
226312
unsigned short vendor, device, subsystem_vendor, subsystem_device;
227313

228314
if (strncmp(p, "pci:", 4) == 0) {
@@ -248,25 +334,15 @@ static int pci_dev_str_match(struct pci_dev *dev, const char *p,
248334
(!subsystem_device ||
249335
subsystem_device == dev->subsystem_device))
250336
goto found;
251-
252337
} else {
253-
/* PCI Bus, Device, Function IDs are specified */
254-
ret = sscanf(p, "%x:%x:%x.%x%n", &seg, &bus, &slot,
255-
&func, &count);
256-
if (ret != 4) {
257-
seg = 0;
258-
ret = sscanf(p, "%x:%x.%x%n", &bus, &slot,
259-
&func, &count);
260-
if (ret != 3)
261-
return -EINVAL;
262-
}
263-
264-
p += count;
265-
266-
if (seg == pci_domain_nr(dev->bus) &&
267-
bus == dev->bus->number &&
268-
slot == PCI_SLOT(dev->devfn) &&
269-
func == PCI_FUNC(dev->devfn))
338+
/*
339+
* PCI Bus, Device, Function IDs are specified
340+
* (optionally, may include a path of devfns following it)
341+
*/
342+
ret = pci_dev_str_match_path(dev, p, &p);
343+
if (ret < 0)
344+
return ret;
345+
else if (ret)
270346
goto found;
271347
}
272348

0 commit comments

Comments
 (0)