Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: allow assignment of memory resources with a specified alignment

This patch allows memory resources to be assigned with a specified
alignment at boot-time or run-time. The patch is useful when we use PCI
pass-through, because page-aligned memory resources are required to
securely share PCI resources with guest drivers.

If you want to assign the resource at boot time, please set
"pci=resource_alignment=" boot parameter.

This is format of "pci=resource_alignment=" boot parameter:

[<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...]
Specifies alignment and device to reassign
aligned memory resources.
If <order of align> is not specified, PAGE_SIZE is
used as alignment.
PCI-PCI bridge can be specified, if resource
windows need to be expanded.

This is example:

pci=resource_alignment=20@07:00.0;18@0f:00.0;00:1d.7

If you want to assign the resource at run-time, please set
"/sys/bus/pci/resource_alignment" file, and hot-remove the device and
hot-add the device. For this purpose, fakephp or PCI hotplug interfaces
can be used.

The format of "/sys/bus/pci/resource_alignment" file is the same with
boot parameter. You can use "," instead of ";".

For example:

# cd /sys/bus/pci
# echo -n 20@12:00.0 > resource_alignment
# echo 1 > devices/0000:12:00.0/remove
# echo 1 > rescan

Reviewed-by: Alex Chiang <achiang@hp.com>
Reviewed-by: Yu Zhao <yu.zhao@intel.com>
Signed-off-by: Yuji Shimada <shimada-yxb@necst.nec.co.jp>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

authored by

Yuji Shimada and committed by
Jesse Barnes
32a9a682 1c8d7b0a

+210
+9
Documentation/kernel-parameters.txt
··· 1760 1760 cbmemsize=nn[KMG] The fixed amount of bus space which is 1761 1761 reserved for the CardBus bridge's memory 1762 1762 window. The default value is 64 megabytes. 1763 + resource_alignment= 1764 + Format: 1765 + [<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...] 1766 + Specifies alignment and device to reassign 1767 + aligned memory resources. 1768 + If <order of align> is not specified, 1769 + PAGE_SIZE is used as alignment. 1770 + PCI-PCI bridge can be specified, if resource 1771 + windows need to be expanded. 1763 1772 1764 1773 pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power 1765 1774 Management.
+120
drivers/pci/pci.c
··· 20 20 #include <linux/pm_wakeup.h> 21 21 #include <linux/interrupt.h> 22 22 #include <asm/dma.h> /* isa_dma_bridge_buggy */ 23 + #include <linux/device.h> 24 + #include <asm/setup.h> 23 25 #include "pci.h" 24 26 25 27 unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; ··· 2372 2370 return 0; 2373 2371 } 2374 2372 2373 + #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE 2374 + static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0}; 2375 + spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED; 2376 + 2377 + /** 2378 + * pci_specified_resource_alignment - get resource alignment specified by user. 2379 + * @dev: the PCI device to get 2380 + * 2381 + * RETURNS: Resource alignment if it is specified. 2382 + * Zero if it is not specified. 2383 + */ 2384 + resource_size_t pci_specified_resource_alignment(struct pci_dev *dev) 2385 + { 2386 + int seg, bus, slot, func, align_order, count; 2387 + resource_size_t align = 0; 2388 + char *p; 2389 + 2390 + spin_lock(&resource_alignment_lock); 2391 + p = resource_alignment_param; 2392 + while (*p) { 2393 + count = 0; 2394 + if (sscanf(p, "%d%n", &align_order, &count) == 1 && 2395 + p[count] == '@') { 2396 + p += count + 1; 2397 + } else { 2398 + align_order = -1; 2399 + } 2400 + if (sscanf(p, "%x:%x:%x.%x%n", 2401 + &seg, &bus, &slot, &func, &count) != 4) { 2402 + seg = 0; 2403 + if (sscanf(p, "%x:%x.%x%n", 2404 + &bus, &slot, &func, &count) != 3) { 2405 + /* Invalid format */ 2406 + printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n", 2407 + p); 2408 + break; 2409 + } 2410 + } 2411 + p += count; 2412 + if (seg == pci_domain_nr(dev->bus) && 2413 + bus == dev->bus->number && 2414 + slot == PCI_SLOT(dev->devfn) && 2415 + func == PCI_FUNC(dev->devfn)) { 2416 + if (align_order == -1) { 2417 + align = PAGE_SIZE; 2418 + } else { 2419 + align = 1 << align_order; 2420 + } 2421 + /* Found */ 2422 + break; 2423 + } 2424 + if (*p != ';' && *p != ',') { 2425 + /* End of param or invalid format */ 2426 + break; 2427 + } 2428 + p++; 2429 + } 2430 + spin_unlock(&resource_alignment_lock); 2431 + return align; 2432 + } 2433 + 2434 + /** 2435 + * pci_is_reassigndev - check if specified PCI is target device to reassign 2436 + * @dev: the PCI device to check 2437 + * 2438 + * RETURNS: non-zero for PCI device is a target device to reassign, 2439 + * or zero is not. 2440 + */ 2441 + int pci_is_reassigndev(struct pci_dev *dev) 2442 + { 2443 + return (pci_specified_resource_alignment(dev) != 0); 2444 + } 2445 + 2446 + ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) 2447 + { 2448 + if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) 2449 + count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1; 2450 + spin_lock(&resource_alignment_lock); 2451 + strncpy(resource_alignment_param, buf, count); 2452 + resource_alignment_param[count] = '\0'; 2453 + spin_unlock(&resource_alignment_lock); 2454 + return count; 2455 + } 2456 + 2457 + ssize_t pci_get_resource_alignment_param(char *buf, size_t size) 2458 + { 2459 + size_t count; 2460 + spin_lock(&resource_alignment_lock); 2461 + count = snprintf(buf, size, "%s", resource_alignment_param); 2462 + spin_unlock(&resource_alignment_lock); 2463 + return count; 2464 + } 2465 + 2466 + static ssize_t pci_resource_alignment_show(struct bus_type *bus, char *buf) 2467 + { 2468 + return pci_get_resource_alignment_param(buf, PAGE_SIZE); 2469 + } 2470 + 2471 + static ssize_t pci_resource_alignment_store(struct bus_type *bus, 2472 + const char *buf, size_t count) 2473 + { 2474 + return pci_set_resource_alignment_param(buf, count); 2475 + } 2476 + 2477 + BUS_ATTR(resource_alignment, 0644, pci_resource_alignment_show, 2478 + pci_resource_alignment_store); 2479 + 2480 + static int __init pci_resource_alignment_sysfs_init(void) 2481 + { 2482 + return bus_create_file(&pci_bus_type, 2483 + &bus_attr_resource_alignment); 2484 + } 2485 + 2486 + late_initcall(pci_resource_alignment_sysfs_init); 2487 + 2375 2488 static void __devinit pci_no_domains(void) 2376 2489 { 2377 2490 #ifdef CONFIG_PCI_DOMAINS ··· 2535 2418 pci_cardbus_io_size = memparse(str + 9, &str); 2536 2419 } else if (!strncmp(str, "cbmemsize=", 10)) { 2537 2420 pci_cardbus_mem_size = memparse(str + 10, &str); 2421 + } else if (!strncmp(str, "resource_alignment=", 19)) { 2422 + pci_set_resource_alignment_param(str + 19, 2423 + strlen(str + 19)); 2538 2424 } else { 2539 2425 printk(KERN_ERR "PCI: Unknown option `%s'\n", 2540 2426 str);
+6
drivers/pci/pci.h
··· 195 195 return bus->self && bus->self->ari_enabled; 196 196 } 197 197 198 + #ifdef CONFIG_PCI_QUIRKS 199 + extern int pci_is_reassigndev(struct pci_dev *dev); 200 + resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); 201 + extern void pci_disable_bridge_window(struct pci_dev *dev); 202 + #endif 203 + 198 204 #endif /* DRIVERS_PCI_H */
+60
drivers/pci/quirks.c
··· 24 24 #include <linux/kallsyms.h> 25 25 #include <linux/dmi.h> 26 26 #include <linux/pci-aspm.h> 27 + #include <linux/ioport.h> 27 28 #include "pci.h" 28 29 29 30 int isa_dma_bridge_buggy; ··· 35 34 EXPORT_SYMBOL(pcie_mch_quirk); 36 35 37 36 #ifdef CONFIG_PCI_QUIRKS 37 + /* 38 + * This quirk function disables the device and releases resources 39 + * which is specified by kernel's boot parameter 'pci=resource_alignment='. 40 + * It also rounds up size to specified alignment. 41 + * Later on, the kernel will assign page-aligned memory resource back 42 + * to that device. 43 + */ 44 + static void __devinit quirk_resource_alignment(struct pci_dev *dev) 45 + { 46 + int i; 47 + struct resource *r; 48 + resource_size_t align, size; 49 + 50 + if (!pci_is_reassigndev(dev)) 51 + return; 52 + 53 + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && 54 + (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { 55 + dev_warn(&dev->dev, 56 + "Can't reassign resources to host bridge.\n"); 57 + return; 58 + } 59 + 60 + dev_info(&dev->dev, "Disabling device and release resources.\n"); 61 + pci_disable_device(dev); 62 + 63 + align = pci_specified_resource_alignment(dev); 64 + for (i=0; i < PCI_BRIDGE_RESOURCES; i++) { 65 + r = &dev->resource[i]; 66 + if (!(r->flags & IORESOURCE_MEM)) 67 + continue; 68 + size = resource_size(r); 69 + if (size < align) { 70 + size = align; 71 + dev_info(&dev->dev, 72 + "Rounding up size of resource #%d to %#llx.\n", 73 + i, (unsigned long long)size); 74 + } 75 + r->end = size - 1; 76 + r->start = 0; 77 + } 78 + /* Need to disable bridge's resource window, 79 + * to enable the kernel to reassign new resource 80 + * window later on. 81 + */ 82 + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && 83 + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { 84 + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { 85 + r = &dev->resource[i]; 86 + if (!(r->flags & IORESOURCE_MEM)) 87 + continue; 88 + r->end = resource_size(r) - 1; 89 + r->start = 0; 90 + } 91 + pci_disable_bridge_window(dev); 92 + } 93 + } 94 + DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); 95 + 38 96 /* The Mellanox Tavor device gives false positive parity errors 39 97 * Mark this device with a broken_parity_status, to allow 40 98 * PCI scanning code to "skip" this now blacklisted device.
+15
drivers/pci/setup-res.c
··· 120 120 return err; 121 121 } 122 122 123 + #ifdef CONFIG_PCI_QUIRKS 124 + void pci_disable_bridge_window(struct pci_dev *dev) 125 + { 126 + dev_dbg(&dev->dev, "Disabling bridge window.\n"); 127 + 128 + /* MMIO Base/Limit */ 129 + pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0); 130 + 131 + /* Prefetchable MMIO Base/Limit */ 132 + pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0); 133 + pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); 134 + pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); 135 + } 136 + #endif /* CONFIG_PCI_QUIRKS */ 137 + 123 138 int pci_assign_resource(struct pci_dev *dev, int resno) 124 139 { 125 140 struct pci_bus *bus = dev->bus;