From da7822e5ad71ec9b745b412639f1e5e0ba795a20 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 12 May 2011 17:11:37 -0700 Subject: PCI: update bridge resources to get more big ranges when allocating space (again) With Ram's fixes, this should be safe to do again. So let's give it another try. BIOS separates IO ranges between several IOHs, and on some slots, BIOS assigns resources to a bridge, but stops assigning resources to the device under that bridge, because the device needs a big resource. So: 1. allocate resources and record the failed device resources 2. clear the BIOS assigned resources of the parent bridge of failing device 3. go back and call pci assign unassigned 4. if it still fails, go up the tree, clear more bridges. and try again Now Ram's allocate requested resource already got into mainline. could put this one again. Reviewed-by: Ram Pai Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 125 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 8 deletions(-) (limited to 'drivers/pci/setup-bus.c') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ebf51ad1b714..7a65db400253 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -991,30 +991,139 @@ static void pci_bus_dump_resources(struct pci_bus *bus) } } +static int __init pci_bus_get_depth(struct pci_bus *bus) +{ + int depth = 0; + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + int ret; + struct pci_bus *b = dev->subordinate; + if (!b) + continue; + + ret = pci_bus_get_depth(b); + if (ret + 1 > depth) + depth = ret + 1; + } + + 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; +} + +/* + * 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) { struct pci_bus *bus; struct resource_list_x add_list; /* list of resources that want additional resources */ + int tried_times = 0; + enum release_type rel_type = leaf_only; + struct resource_list_x head, *list; + unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH; + unsigned long failed_type; + int max_depth = pci_get_max_depth(); + int pci_try_num; + + + head.next = NULL; add_list.next = NULL; + + pci_try_num = max_depth + 1; + printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n", + max_depth, pci_try_num); + +again: /* Depth first, calculate sizes and alignments of all subordinate buses. */ - list_for_each_entry(bus, &pci_root_buses, node) { + list_for_each_entry(bus, &pci_root_buses, node) __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, NULL); - pci_enable_bridges(bus); - } + list_for_each_entry(bus, &pci_root_buses, node) + __pci_bus_assign_resources(bus, &add_list, &head); BUG_ON(add_list.next); + tried_times++; + + /* any device complain? */ + if (!head.next) + goto enable_and_dump; + failed_type = 0; + for (list = head.next; list;) { + failed_type |= list->flags; + list = list->next; + } + /* + * io port are tight, don't try extra + * or if reach the limit, don't want to try more + */ + failed_type &= type_mask; + if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) { + free_list(resource_list_x, &head); + goto enable_and_dump; + } + + printk(KERN_DEBUG "PCI: 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) + rel_type = whole_subtree; + + /* + * Try to release leaf bridge's resources that doesn't fit resource of + * child device under that bridge + */ + for (list = head.next; list;) { + bus = list->dev->bus; + pci_bus_release_bridge_resources(bus, list->flags & type_mask, + rel_type); + list = list->next; + } + /* restore size and flags */ + for (list = head.next; list;) { + struct resource *res = list->res; + + res->start = list->start; + res->end = list->end; + res->flags = list->flags; + if (list->dev->subordinate) + res->flags = 0; + + list = list->next; + } + free_list(resource_list_x, &head); + + goto again; + +enable_and_dump: + /* Depth last, update the hardware. */ + list_for_each_entry(bus, &pci_root_buses, node) + pci_enable_bridges(bus); /* dump the resource on buses */ - list_for_each_entry(bus, &pci_root_buses, node) { + list_for_each_entry(bus, &pci_root_buses, node) pci_bus_dump_resources(bus); - } } void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) -- cgit v1.2.3