diff options
author | Logan Gunthorpe <logang@deltatee.com> | 2018-07-30 12:18:37 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2018-08-09 17:23:06 -0400 |
commit | 07d8d7e57c28ca9a07dab4efd75dad3a654aeb85 (patch) | |
tree | 53592de54320ccabbfd94f043769cc3881ad0b41 | |
parent | bd2e9567db72e37f7f4b90faa5133bc7365b5f65 (diff) |
PCI: Make specifying PCI devices in kernel parameters reusable
Separate out the code to match a PCI device with a string (typically
originating from a kernel parameter) from the
pci_specified_resource_alignment() function into its own helper function.
While we are at it, this change fixes the kernel style of the function
(fixing a number of long lines and extra parentheses).
Additionally, make the analogous change to the kernel parameter
documentation: Separate the description of how to specify a PCI device
into its own section at the head of the "pci=" parameter.
This patch should have no functional alterations.
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>
-rw-r--r-- | Documentation/admin-guide/kernel-parameters.txt | 28 | ||||
-rw-r--r-- | drivers/pci/pci.c | 157 |
2 files changed, 126 insertions, 59 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index efc7aa7a0670..ab36fb34ed01 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt | |||
@@ -2994,7 +2994,26 @@ | |||
2994 | See header of drivers/block/paride/pcd.c. | 2994 | See header of drivers/block/paride/pcd.c. |
2995 | See also Documentation/blockdev/paride.txt. | 2995 | See also Documentation/blockdev/paride.txt. |
2996 | 2996 | ||
2997 | pci=option[,option...] [PCI] various PCI subsystem options: | 2997 | pci=option[,option...] [PCI] various PCI subsystem options. |
2998 | |||
2999 | Some options herein operate on a specific device | ||
3000 | or a set of devices (<pci_dev>). These are | ||
3001 | specified in one of the following formats: | ||
3002 | |||
3003 | [<domain>:]<bus>:<device>.<func> | ||
3004 | pci:<vendor>:<device>[:<subvendor>:<subdevice>] | ||
3005 | |||
3006 | Note: the first format specifies a PCI | ||
3007 | bus/device/function address which may change | ||
3008 | if new hardware is inserted, if motherboard | ||
3009 | firmware changes, or due to changes caused | ||
3010 | by other kernel parameters. If the | ||
3011 | domain is left unspecified, it is | ||
3012 | taken to be zero. The second format | ||
3013 | selects devices using IDs from the | ||
3014 | configuration space which may match multiple | ||
3015 | devices in the system. | ||
3016 | |||
2998 | earlydump [X86] dump PCI config space before the kernel | 3017 | earlydump [X86] dump PCI config space before the kernel |
2999 | changes anything | 3018 | changes anything |
3000 | off [X86] don't probe for the PCI bus | 3019 | off [X86] don't probe for the PCI bus |
@@ -3123,11 +3142,10 @@ | |||
3123 | window. The default value is 64 megabytes. | 3142 | window. The default value is 64 megabytes. |
3124 | resource_alignment= | 3143 | resource_alignment= |
3125 | Format: | 3144 | Format: |
3126 | [<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...] | 3145 | [<order of align>@]<pci_dev>[; ...] |
3127 | [<order of align>@]pci:<vendor>:<device>\ | ||
3128 | [:<subvendor>:<subdevice>][; ...] | ||
3129 | Specifies alignment and device to reassign | 3146 | Specifies alignment and device to reassign |
3130 | aligned memory resources. | 3147 | aligned memory resources. How to |
3148 | specify the device is described above. | ||
3131 | If <order of align> is not specified, | 3149 | If <order of align> is not specified, |
3132 | PAGE_SIZE is used as alignment. | 3150 | PAGE_SIZE is used as alignment. |
3133 | PCI-PCI bridge can be specified, if resource | 3151 | PCI-PCI bridge can be specified, if resource |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 97acba712e4e..1574b2da25e7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -191,6 +191,92 @@ void __iomem *pci_ioremap_wc_bar(struct pci_dev *pdev, int bar) | |||
191 | EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar); | 191 | EXPORT_SYMBOL_GPL(pci_ioremap_wc_bar); |
192 | #endif | 192 | #endif |
193 | 193 | ||
194 | /** | ||
195 | * pci_dev_str_match - test if a 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) matches a specified | ||
201 | * PCI device. The string may be of one of the following formats: | ||
202 | * | ||
203 | * [<domain>:]<bus>:<device>.<func> | ||
204 | * pci:<vendor>:<device>[:<subvendor>:<subdevice>] | ||
205 | * | ||
206 | * The first format specifies a PCI bus/device/function address which | ||
207 | * may change if new hardware is inserted, if motherboard firmware changes, | ||
208 | * or due to changes caused in kernel parameters. If the domain is | ||
209 | * left unspecified, it is taken to be 0. | ||
210 | * | ||
211 | * The second format matches devices using IDs in the configuration | ||
212 | * space which may match multiple devices in the system. A value of 0 | ||
213 | * for any field will match all devices. (Note: this differs from | ||
214 | * in-kernel code that uses PCI_ANY_ID which is ~0; this is for | ||
215 | * legacy reasons and convenience so users don't have to specify | ||
216 | * FFFFFFFFs on the command line.) | ||
217 | * | ||
218 | * Returns 1 if the string matches the device, 0 if it does not and | ||
219 | * a negative error code if the string cannot be parsed. | ||
220 | */ | ||
221 | static int pci_dev_str_match(struct pci_dev *dev, const char *p, | ||
222 | const char **endptr) | ||
223 | { | ||
224 | int ret; | ||
225 | int seg, bus, slot, func, count; | ||
226 | unsigned short vendor, device, subsystem_vendor, subsystem_device; | ||
227 | |||
228 | if (strncmp(p, "pci:", 4) == 0) { | ||
229 | /* PCI vendor/device (subvendor/subdevice) IDs are specified */ | ||
230 | p += 4; | ||
231 | ret = sscanf(p, "%hx:%hx:%hx:%hx%n", &vendor, &device, | ||
232 | &subsystem_vendor, &subsystem_device, &count); | ||
233 | if (ret != 4) { | ||
234 | ret = sscanf(p, "%hx:%hx%n", &vendor, &device, &count); | ||
235 | if (ret != 2) | ||
236 | return -EINVAL; | ||
237 | |||
238 | subsystem_vendor = 0; | ||
239 | subsystem_device = 0; | ||
240 | } | ||
241 | |||
242 | p += count; | ||
243 | |||
244 | if ((!vendor || vendor == dev->vendor) && | ||
245 | (!device || device == dev->device) && | ||
246 | (!subsystem_vendor || | ||
247 | subsystem_vendor == dev->subsystem_vendor) && | ||
248 | (!subsystem_device || | ||
249 | subsystem_device == dev->subsystem_device)) | ||
250 | goto found; | ||
251 | |||
252 | } 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)) | ||
270 | goto found; | ||
271 | } | ||
272 | |||
273 | *endptr = p; | ||
274 | return 0; | ||
275 | |||
276 | found: | ||
277 | *endptr = p; | ||
278 | return 1; | ||
279 | } | ||
194 | 280 | ||
195 | static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, | 281 | static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, |
196 | u8 pos, int cap, int *ttl) | 282 | u8 pos, int cap, int *ttl) |
@@ -5454,10 +5540,10 @@ static DEFINE_SPINLOCK(resource_alignment_lock); | |||
5454 | static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev, | 5540 | static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev, |
5455 | bool *resize) | 5541 | bool *resize) |
5456 | { | 5542 | { |
5457 | int seg, bus, slot, func, align_order, count; | 5543 | int align_order, count; |
5458 | unsigned short vendor, device, subsystem_vendor, subsystem_device; | ||
5459 | resource_size_t align = pcibios_default_alignment(); | 5544 | resource_size_t align = pcibios_default_alignment(); |
5460 | char *p; | 5545 | const char *p; |
5546 | int ret; | ||
5461 | 5547 | ||
5462 | spin_lock(&resource_alignment_lock); | 5548 | spin_lock(&resource_alignment_lock); |
5463 | p = resource_alignment_param; | 5549 | p = resource_alignment_param; |
@@ -5477,58 +5563,21 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev, | |||
5477 | } else { | 5563 | } else { |
5478 | align_order = -1; | 5564 | align_order = -1; |
5479 | } | 5565 | } |
5480 | if (strncmp(p, "pci:", 4) == 0) { | 5566 | |
5481 | /* PCI vendor/device (subvendor/subdevice) ids are specified */ | 5567 | ret = pci_dev_str_match(dev, p, &p); |
5482 | p += 4; | 5568 | if (ret == 1) { |
5483 | if (sscanf(p, "%hx:%hx:%hx:%hx%n", | 5569 | *resize = true; |
5484 | &vendor, &device, &subsystem_vendor, &subsystem_device, &count) != 4) { | 5570 | if (align_order == -1) |
5485 | if (sscanf(p, "%hx:%hx%n", &vendor, &device, &count) != 2) { | 5571 | align = PAGE_SIZE; |
5486 | printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: pci:%s\n", | 5572 | else |
5487 | p); | 5573 | align = 1 << align_order; |
5488 | break; | 5574 | break; |
5489 | } | 5575 | } else if (ret < 0) { |
5490 | subsystem_vendor = subsystem_device = 0; | 5576 | pr_err("PCI: Can't parse resource_alignment parameter: %s\n", |
5491 | } | 5577 | p); |
5492 | p += count; | 5578 | break; |
5493 | if ((!vendor || (vendor == dev->vendor)) && | ||
5494 | (!device || (device == dev->device)) && | ||
5495 | (!subsystem_vendor || (subsystem_vendor == dev->subsystem_vendor)) && | ||
5496 | (!subsystem_device || (subsystem_device == dev->subsystem_device))) { | ||
5497 | *resize = true; | ||
5498 | if (align_order == -1) | ||
5499 | align = PAGE_SIZE; | ||
5500 | else | ||
5501 | align = 1 << align_order; | ||
5502 | /* Found */ | ||
5503 | break; | ||
5504 | } | ||
5505 | } | ||
5506 | else { | ||
5507 | if (sscanf(p, "%x:%x:%x.%x%n", | ||
5508 | &seg, &bus, &slot, &func, &count) != 4) { | ||
5509 | seg = 0; | ||
5510 | if (sscanf(p, "%x:%x.%x%n", | ||
5511 | &bus, &slot, &func, &count) != 3) { | ||
5512 | /* Invalid format */ | ||
5513 | printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n", | ||
5514 | p); | ||
5515 | break; | ||
5516 | } | ||
5517 | } | ||
5518 | p += count; | ||
5519 | if (seg == pci_domain_nr(dev->bus) && | ||
5520 | bus == dev->bus->number && | ||
5521 | slot == PCI_SLOT(dev->devfn) && | ||
5522 | func == PCI_FUNC(dev->devfn)) { | ||
5523 | *resize = true; | ||
5524 | if (align_order == -1) | ||
5525 | align = PAGE_SIZE; | ||
5526 | else | ||
5527 | align = 1 << align_order; | ||
5528 | /* Found */ | ||
5529 | break; | ||
5530 | } | ||
5531 | } | 5579 | } |
5580 | |||
5532 | if (*p != ';' && *p != ',') { | 5581 | if (*p != ';' && *p != ',') { |
5533 | /* End of param or invalid format */ | 5582 | /* End of param or invalid format */ |
5534 | break; | 5583 | break; |