diff options
author | Yuji Shimada <shimada-yxb@necst.nec.co.jp> | 2009-03-16 04:13:39 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-03-20 13:48:15 -0400 |
commit | 32a9a682bef2f6fce7026bd94d1ce20028b0e52d (patch) | |
tree | a93225b3039585e8364ae3d411b22f63eebc8d70 /drivers/pci/pci.c | |
parent | 1c8d7b0a562da06d3ebe83f01b1ed553205d1ae4 (diff) |
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>
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8310dc2f943b..a35a8b2ba631 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -20,6 +20,8 @@ | |||
20 | #include <linux/pm_wakeup.h> | 20 | #include <linux/pm_wakeup.h> |
21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
22 | #include <asm/dma.h> /* isa_dma_bridge_buggy */ | 22 | #include <asm/dma.h> /* isa_dma_bridge_buggy */ |
23 | #include <linux/device.h> | ||
24 | #include <asm/setup.h> | ||
23 | #include "pci.h" | 25 | #include "pci.h" |
24 | 26 | ||
25 | unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; | 27 | unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; |
@@ -2370,6 +2372,121 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) | |||
2370 | return 0; | 2372 | return 0; |
2371 | } | 2373 | } |
2372 | 2374 | ||
2375 | #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE | ||
2376 | static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0}; | ||
2377 | spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED; | ||
2378 | |||
2379 | /** | ||
2380 | * pci_specified_resource_alignment - get resource alignment specified by user. | ||
2381 | * @dev: the PCI device to get | ||
2382 | * | ||
2383 | * RETURNS: Resource alignment if it is specified. | ||
2384 | * Zero if it is not specified. | ||
2385 | */ | ||
2386 | resource_size_t pci_specified_resource_alignment(struct pci_dev *dev) | ||
2387 | { | ||
2388 | int seg, bus, slot, func, align_order, count; | ||
2389 | resource_size_t align = 0; | ||
2390 | char *p; | ||
2391 | |||
2392 | spin_lock(&resource_alignment_lock); | ||
2393 | p = resource_alignment_param; | ||
2394 | while (*p) { | ||
2395 | count = 0; | ||
2396 | if (sscanf(p, "%d%n", &align_order, &count) == 1 && | ||
2397 | p[count] == '@') { | ||
2398 | p += count + 1; | ||
2399 | } else { | ||
2400 | align_order = -1; | ||
2401 | } | ||
2402 | if (sscanf(p, "%x:%x:%x.%x%n", | ||
2403 | &seg, &bus, &slot, &func, &count) != 4) { | ||
2404 | seg = 0; | ||
2405 | if (sscanf(p, "%x:%x.%x%n", | ||
2406 | &bus, &slot, &func, &count) != 3) { | ||
2407 | /* Invalid format */ | ||
2408 | printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n", | ||
2409 | p); | ||
2410 | break; | ||
2411 | } | ||
2412 | } | ||
2413 | p += count; | ||
2414 | if (seg == pci_domain_nr(dev->bus) && | ||
2415 | bus == dev->bus->number && | ||
2416 | slot == PCI_SLOT(dev->devfn) && | ||
2417 | func == PCI_FUNC(dev->devfn)) { | ||
2418 | if (align_order == -1) { | ||
2419 | align = PAGE_SIZE; | ||
2420 | } else { | ||
2421 | align = 1 << align_order; | ||
2422 | } | ||
2423 | /* Found */ | ||
2424 | break; | ||
2425 | } | ||
2426 | if (*p != ';' && *p != ',') { | ||
2427 | /* End of param or invalid format */ | ||
2428 | break; | ||
2429 | } | ||
2430 | p++; | ||
2431 | } | ||
2432 | spin_unlock(&resource_alignment_lock); | ||
2433 | return align; | ||
2434 | } | ||
2435 | |||
2436 | /** | ||
2437 | * pci_is_reassigndev - check if specified PCI is target device to reassign | ||
2438 | * @dev: the PCI device to check | ||
2439 | * | ||
2440 | * RETURNS: non-zero for PCI device is a target device to reassign, | ||
2441 | * or zero is not. | ||
2442 | */ | ||
2443 | int pci_is_reassigndev(struct pci_dev *dev) | ||
2444 | { | ||
2445 | return (pci_specified_resource_alignment(dev) != 0); | ||
2446 | } | ||
2447 | |||
2448 | ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) | ||
2449 | { | ||
2450 | if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) | ||
2451 | count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1; | ||
2452 | spin_lock(&resource_alignment_lock); | ||
2453 | strncpy(resource_alignment_param, buf, count); | ||
2454 | resource_alignment_param[count] = '\0'; | ||
2455 | spin_unlock(&resource_alignment_lock); | ||
2456 | return count; | ||
2457 | } | ||
2458 | |||
2459 | ssize_t pci_get_resource_alignment_param(char *buf, size_t size) | ||
2460 | { | ||
2461 | size_t count; | ||
2462 | spin_lock(&resource_alignment_lock); | ||
2463 | count = snprintf(buf, size, "%s", resource_alignment_param); | ||
2464 | spin_unlock(&resource_alignment_lock); | ||
2465 | return count; | ||
2466 | } | ||
2467 | |||
2468 | static ssize_t pci_resource_alignment_show(struct bus_type *bus, char *buf) | ||
2469 | { | ||
2470 | return pci_get_resource_alignment_param(buf, PAGE_SIZE); | ||
2471 | } | ||
2472 | |||
2473 | static ssize_t pci_resource_alignment_store(struct bus_type *bus, | ||
2474 | const char *buf, size_t count) | ||
2475 | { | ||
2476 | return pci_set_resource_alignment_param(buf, count); | ||
2477 | } | ||
2478 | |||
2479 | BUS_ATTR(resource_alignment, 0644, pci_resource_alignment_show, | ||
2480 | pci_resource_alignment_store); | ||
2481 | |||
2482 | static int __init pci_resource_alignment_sysfs_init(void) | ||
2483 | { | ||
2484 | return bus_create_file(&pci_bus_type, | ||
2485 | &bus_attr_resource_alignment); | ||
2486 | } | ||
2487 | |||
2488 | late_initcall(pci_resource_alignment_sysfs_init); | ||
2489 | |||
2373 | static void __devinit pci_no_domains(void) | 2490 | static void __devinit pci_no_domains(void) |
2374 | { | 2491 | { |
2375 | #ifdef CONFIG_PCI_DOMAINS | 2492 | #ifdef CONFIG_PCI_DOMAINS |
@@ -2418,6 +2535,9 @@ static int __init pci_setup(char *str) | |||
2418 | pci_cardbus_io_size = memparse(str + 9, &str); | 2535 | pci_cardbus_io_size = memparse(str + 9, &str); |
2419 | } else if (!strncmp(str, "cbmemsize=", 10)) { | 2536 | } else if (!strncmp(str, "cbmemsize=", 10)) { |
2420 | pci_cardbus_mem_size = memparse(str + 10, &str); | 2537 | pci_cardbus_mem_size = memparse(str + 10, &str); |
2538 | } else if (!strncmp(str, "resource_alignment=", 19)) { | ||
2539 | pci_set_resource_alignment_param(str + 19, | ||
2540 | strlen(str + 19)); | ||
2421 | } else { | 2541 | } else { |
2422 | printk(KERN_ERR "PCI: Unknown option `%s'\n", | 2542 | printk(KERN_ERR "PCI: Unknown option `%s'\n", |
2423 | str); | 2543 | str); |