PCI: make re-allocation try harder by reassigning ranges higher in the heirarchy
authorYinghai Lu <yinghai@kernel.org>
Sat, 21 Jan 2012 10:08:24 +0000 (02:08 -0800)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 14 Feb 2012 16:44:54 +0000 (08:44 -0800)
On a system with devices that support SRIOV connected to a pcie switch
to pcie root port:

 +-[0000:80]-+-00.0-[81-8f]--
 |           +-01.0-[90-9f]--
 |           +-02.0-[a0-af]----00.0-[a1-a3]--+-02.0-[a2]--+-00.0 Oracle Corporation Device 207a
 |           |                               \-03.0-[a3]--+-00.0 Oracle Corporation Device 207a
 |           +-02.2-[b0-bf]----00.0-[b1-b3]--+-02.0-[b2]--+-00.0 Oracle Corporation Device 207a
 |           |                               \-03.0-[b3]--+-00.0 Oracle Corporation Device 207a

When the BIOS does not assign resources for SRIOV BARs, kernel pci
reallocation only goes up one bridge and then gives up, failing to to
get resources for all sSRIOV BARs, even though the range is large enough
in the peer root bus.

Specifically, only the bridge at the a1:02.0 level has its resources
cleared and reallocated.  The kernel does not go up to clear the bridge
at the 80:02.0 level.

To make it go to upper levels, during retry, we need to treat "good to have"
resources as "must have".

Only on the last try will we treat good to have resources as optional.
At that time, parent bridge resources will already have been released so
we'll have a chance to get everything assigned with must_have plus
good_to_have for all child devices.

Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/setup-bus.c

index c09c67ab5612cd500ebb078018ddbf07bea8abb7..c79ce4ee634b72b56881d98e2a7a68a9b4382570 100644 (file)
@@ -943,7 +943,8 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
                 * Follow thru
                 */
        default:
-               pbus_size_io(bus, 0, additional_io_size, realloc_head);
+               pbus_size_io(bus, realloc_head ? 0 : additional_io_size,
+                            additional_io_size, realloc_head);
                /* If the bridge supports prefetchable range, size it
                   separately. If it doesn't, or its prefetchable window
                   has already been allocated by arch code, try
@@ -951,11 +952,15 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
                   resources. */
                mask = IORESOURCE_MEM;
                prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
-               if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, realloc_head))
+               if (pbus_size_mem(bus, prefmask, prefmask,
+                                 realloc_head ? 0 : additional_mem_size,
+                                 additional_mem_size, realloc_head))
                        mask = prefmask; /* Success, size non-prefetch only. */
                else
                        additional_mem_size += additional_mem_size;
-               pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, realloc_head);
+               pbus_size_mem(bus, mask, IORESOURCE_MEM,
+                               realloc_head ? 0 : additional_mem_size,
+                               additional_mem_size, realloc_head);
                break;
        }
 }
@@ -1194,45 +1199,50 @@ pci_assign_unassigned_resources(void)
        struct pci_bus *bus;
        struct resource_list_x realloc_list; /* list of resources that
                                        want additional resources */
+       struct resource_list_x *add_list = NULL;
        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;
-
+       int pci_try_num = 1;
 
        head.next = NULL;
        realloc_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);
+       /* don't realloc if asked to do so */
+       if (pci_realloc_enabled()) {
+               int max_depth = pci_get_max_depth();
+
+               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:
+       /*
+        * last try will use add_list, otherwise will try good to have as
+        * must have, so can realloc parent bridge resource
+        */
+       if (tried_times + 1 == pci_try_num)
+               add_list = &realloc_list;
        /* Depth first, calculate sizes and alignments of all
           subordinate buses. */
        list_for_each_entry(bus, &pci_root_buses, node)
-               __pci_bus_size_bridges(bus, &realloc_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, &realloc_list, &head);
-       BUG_ON(realloc_list.next);
+               __pci_bus_assign_resources(bus, add_list, &head);
+       if (add_list)
+               BUG_ON(add_list->next);
        tried_times++;
 
        /* any device complain? */
        if (!head.next)
                goto enable_and_dump;
 
-       /* don't realloc if asked to do so */
-       if (!pci_realloc_enabled()) {
-               free_list(resource_list_x, &head);
-               goto enable_and_dump;
-       }
-
        failed_type = 0;
        for (list = head.next; list;) {
                failed_type |= list->flags;