diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2013-08-26 15:40:03 -0600 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-08-26 15:40:03 -0600 |
commit | 1193725f543c92a77c73769bc2fbe48c53275f53 (patch) | |
tree | 71ef007260a1584f4f48d7a7118a3157d01eb5f7 /drivers/pci | |
parent | 7d8c4a2c5ae6d76f1142fb052d698b3c40ce518c (diff) | |
parent | 39772038ea93e85ea4f1307ec9c1f48a063d89a0 (diff) |
Merge branch 'pci/yinghai-assign-unassigned-v6' into next
* pci/yinghai-assign-unassigned-v6:
PCI: Assign resources for hot-added host bridge more aggressively
PCI: Move resource reallocation code to non-__init
PCI: Delay enabling bridges until they're needed
PCI: Assign resources on a per-bus basis
PCI: Enable unassigned resource reallocation on per-bus basis
PCI: Turn on reallocation for unassigned resources with host bridge offset
PCI: Look for unassigned resources on per-bus basis
PCI: Drop temporary variable in pci_assign_unassigned_resources()
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/bus.c | 19 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 1 | ||||
-rw-r--r-- | drivers/pci/pci.c | 20 | ||||
-rw-r--r-- | drivers/pci/probe.c | 1 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 151 |
5 files changed, 97 insertions, 95 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index b1ff02ab4f1..fc1b7401374 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -216,24 +216,6 @@ void pci_bus_add_devices(const struct pci_bus *bus) } } -void pci_enable_bridges(struct pci_bus *bus) -{ - struct pci_dev *dev; - int retval; - - list_for_each_entry(dev, &bus->devices, bus_list) { - if (dev->subordinate) { - if (!pci_is_enabled(dev)) { - retval = pci_enable_device(dev); - if (retval) - dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n", retval); - pci_set_master(dev); - } - pci_enable_bridges(dev->subordinate); - } - } -} - /** pci_walk_bus - walk devices on/under bus, calling callback. * @top bus whose devices should be walked * @cb callback to be called for each device found @@ -301,4 +283,3 @@ EXPORT_SYMBOL(pci_bus_put); EXPORT_SYMBOL(pci_bus_alloc_resource); EXPORT_SYMBOL_GPL(pci_bus_add_device); EXPORT_SYMBOL(pci_bus_add_devices); -EXPORT_SYMBOL(pci_enable_bridges); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 59df8575a48..52dee9d31e1 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -723,7 +723,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) acpiphp_sanitize_bus(bus); acpiphp_set_hpp_values(bus); acpiphp_set_acpi_region(slot); - pci_enable_bridges(bus); list_for_each_entry(dev, &bus->devices, bus_list) { /* Assume that newly added devices are powered on already. */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 10e3c4e1517..d3fdce8f3d6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1146,6 +1146,24 @@ int pci_reenable_device(struct pci_dev *dev) return 0; } +static void pci_enable_bridge(struct pci_dev *dev) +{ + int retval; + + if (!dev) + return; + + pci_enable_bridge(dev->bus->self); + + if (pci_is_enabled(dev)) + return; + retval = pci_enable_device(dev); + if (retval) + dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n", + retval); + pci_set_master(dev); +} + static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) { int err; @@ -1166,6 +1184,8 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) if (atomic_inc_return(&dev->enable_cnt) > 1) return 0; /* already enabled */ + pci_enable_bridge(dev->bus->self); + /* only skip sriov related */ for (i = 0; i <= PCI_ROM_RESOURCE; i++) if (dev->resource[i].flags & flags) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cf57fe79450..4ab388a6cc2 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1979,7 +1979,6 @@ unsigned int __ref pci_rescan_bus(struct pci_bus *bus) max = pci_scan_child_bus(bus); pci_assign_unassigned_bus_resources(bus); - pci_enable_bridges(bus); pci_bus_add_devices(bus); return max; diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8333c92c202..9be359594b0 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1297,7 +1297,7 @@ static void pci_bus_dump_resources(struct pci_bus *bus) } } -static int __init pci_bus_get_depth(struct pci_bus *bus) +static int pci_bus_get_depth(struct pci_bus *bus) { int depth = 0; struct pci_bus *child_bus; @@ -1312,21 +1312,6 @@ static int __init pci_bus_get_depth(struct pci_bus *bus) return depth; } -static int __init pci_get_max_depth(void) -{ - int depth = 0; - struct pci_bus *bus; - - list_for_each_entry(bus, &pci_root_buses, node) { - int ret; - - ret = pci_bus_get_depth(bus); - if (ret > depth) - depth = ret; - } - - return depth; -} /* * -1: undefined, will auto detect later @@ -1343,7 +1328,7 @@ enum enable_type { auto_enabled, }; -static enum enable_type pci_realloc_enable __initdata = undefined; +static enum enable_type pci_realloc_enable = undefined; void __init pci_realloc_get_opt(char *str) { if (!strncmp(str, "off", 3)) @@ -1351,45 +1336,64 @@ void __init pci_realloc_get_opt(char *str) else if (!strncmp(str, "on", 2)) pci_realloc_enable = user_enabled; } -static bool __init pci_realloc_enabled(void) +static bool pci_realloc_enabled(enum enable_type enable) { - return pci_realloc_enable >= user_enabled; + return enable >= user_enabled; } -static void __init pci_realloc_detect(void) -{ #if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO) - struct pci_dev *dev = NULL; - - if (pci_realloc_enable != undefined) - return; - - for_each_pci_dev(dev) { - int i; +static int iov_resources_unassigned(struct pci_dev *dev, void *data) +{ + int i; + bool *unassigned = data; - for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) { - struct resource *r = &dev->resource[i]; + for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) { + struct resource *r = &dev->resource[i]; + struct pci_bus_region region; - /* Not assigned, or rejected by kernel ? */ - if (r->flags && !r->start) { - pci_realloc_enable = auto_enabled; + /* Not assigned or rejected by kernel? */ + if (!r->flags) + continue; - return; - } + pcibios_resource_to_bus(dev, ®ion, r); + if (!region.start) { + *unassigned = true; + return 1; /* return early from pci_walk_bus() */ } } -#endif + + return 0; +} + +static enum enable_type pci_realloc_detect(struct pci_bus *bus, + enum enable_type enable_local) +{ + bool unassigned = false; + + if (enable_local != undefined) + return enable_local; + + pci_walk_bus(bus, iov_resources_unassigned, &unassigned); + if (unassigned) + return auto_enabled; + + return enable_local; +} +#else +static enum enable_type pci_realloc_detect(struct pci_bus *bus, + enum enable_type enable_local) +{ + return enable_local; } +#endif /* * first try will not touch pci bridge res * second and later try will clear small leaf bridge res * will stop till to the max deepth if can not find good one */ -void __init -pci_assign_unassigned_resources(void) +void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) { - struct pci_bus *bus; LIST_HEAD(realloc_head); /* list of resources that want additional resources */ struct list_head *add_list = NULL; @@ -1400,15 +1404,17 @@ pci_assign_unassigned_resources(void) unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; int pci_try_num = 1; + enum enable_type enable_local; /* don't realloc if asked to do so */ - pci_realloc_detect(); - if (pci_realloc_enabled()) { - int max_depth = pci_get_max_depth(); + enable_local = pci_realloc_detect(bus, pci_realloc_enable); + if (pci_realloc_enabled(enable_local)) { + int max_depth = pci_bus_get_depth(bus); pci_try_num = max_depth + 1; - printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n", - max_depth, pci_try_num); + dev_printk(KERN_DEBUG, &bus->dev, + "max bus depth: %d pci_try_num: %d\n", + max_depth, pci_try_num); } again: @@ -1420,32 +1426,30 @@ again: add_list = &realloc_head; /* Depth first, calculate sizes and alignments of all subordinate buses. */ - list_for_each_entry(bus, &pci_root_buses, node) - __pci_bus_size_bridges(bus, add_list); + __pci_bus_size_bridges(bus, add_list); /* Depth last, allocate resources and update the hardware. */ - list_for_each_entry(bus, &pci_root_buses, node) - __pci_bus_assign_resources(bus, add_list, &fail_head); + __pci_bus_assign_resources(bus, add_list, &fail_head); if (add_list) BUG_ON(!list_empty(add_list)); tried_times++; /* any device complain? */ if (list_empty(&fail_head)) - goto enable_and_dump; + goto dump; if (tried_times >= pci_try_num) { - if (pci_realloc_enable == undefined) - printk(KERN_INFO "Some PCI device resources are unassigned, try booting with pci=realloc\n"); - else if (pci_realloc_enable == auto_enabled) - printk(KERN_INFO "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); + if (enable_local == undefined) + dev_info(&bus->dev, "Some PCI device resources are unassigned, try booting with pci=realloc\n"); + else if (enable_local == auto_enabled) + dev_info(&bus->dev, "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); free_list(&fail_head); - goto enable_and_dump; + goto dump; } - printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n", - tried_times + 1); + dev_printk(KERN_DEBUG, &bus->dev, + "No. %d try to assign unassigned res\n", tried_times + 1); /* third times and later will not check if it is leaf */ if ((tried_times + 1) > 2) @@ -1455,12 +1459,11 @@ again: * Try to release leaf bridge's resources that doesn't fit resource of * child device under that bridge */ - list_for_each_entry(fail_res, &fail_head, list) { - bus = fail_res->dev->bus; - pci_bus_release_bridge_resources(bus, + list_for_each_entry(fail_res, &fail_head, list) + pci_bus_release_bridge_resources(fail_res->dev->bus, fail_res->flags & type_mask, rel_type); - } + /* restore size and flags */ list_for_each_entry(fail_res, &fail_head, list) { struct resource *res = fail_res->res; @@ -1475,14 +1478,17 @@ again: goto again; -enable_and_dump: - /* Depth last, update the hardware. */ - list_for_each_entry(bus, &pci_root_buses, node) - pci_enable_bridges(bus); - +dump: /* dump the resource on buses */ - list_for_each_entry(bus, &pci_root_buses, node) - pci_bus_dump_resources(bus); + pci_bus_dump_resources(bus); +} + +void __init pci_assign_unassigned_resources(void) +{ + struct pci_bus *root_bus; + + list_for_each_entry(root_bus, &pci_root_buses, node) + pci_assign_unassigned_root_bus_resources(root_bus); } void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) @@ -1519,13 +1525,11 @@ again: * Try to release leaf bridge's resources that doesn't fit resource of * child device under that bridge */ - list_for_each_entry(fail_res, &fail_head, list) { - struct pci_bus *bus = fail_res->dev->bus; - unsigned long flags = fail_res->flags; - - pci_bus_release_bridge_resources(bus, flags & type_mask, + list_for_each_entry(fail_res, &fail_head, list) + pci_bus_release_bridge_resources(fail_res->dev->bus, + fail_res->flags & type_mask, whole_subtree); - } + /* restore size and flags */ list_for_each_entry(fail_res, &fail_head, list) { struct resource *res = fail_res->res; @@ -1545,7 +1549,6 @@ enable_all: if (retval) dev_err(&bridge->dev, "Error reenabling bridge (%d)\n", retval); pci_set_master(bridge); - pci_enable_bridges(parent); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); |