]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/pci/setup-bus.c
Merge tag '5.6-rc-small-smb3-fix-for-stable' of git://git.samba.org/sfrench/cifs-2.6
[linux.git] / drivers / pci / setup-bus.c
index f279826204eb4f51a8b7988802149da81e32eca6..f2461bf9243df4a965c8f2662959aba8c649805a 100644 (file)
@@ -1803,12 +1803,18 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
        /* Restore size and flags */
        list_for_each_entry(fail_res, &fail_head, list) {
                struct resource *res = fail_res->res;
+               int idx;
 
                res->start = fail_res->start;
                res->end = fail_res->end;
                res->flags = fail_res->flags;
-               if (fail_res->dev->subordinate)
-                       res->flags = 0;
+
+               if (pci_is_bridge(fail_res->dev)) {
+                       idx = res - &fail_res->dev->resource[0];
+                       if (idx >= PCI_BRIDGE_RESOURCES &&
+                           idx <= PCI_BRIDGE_RESOURCE_END)
+                               res->flags = 0;
+               }
        }
        free_list(&fail_head);
 
@@ -1832,56 +1838,72 @@ void __init pci_assign_unassigned_resources(void)
        }
 }
 
-static void extend_bridge_window(struct pci_dev *bridge, struct resource *res,
+static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
                                 struct list_head *add_list,
-                                resource_size_t available)
+                                resource_size_t new_size)
 {
-       struct pci_dev_resource *dev_res;
+       resource_size_t add_size, size = resource_size(res);
 
        if (res->parent)
                return;
 
-       if (resource_size(res) >= available)
+       if (!new_size)
                return;
 
-       dev_res = res_to_dev_res(add_list, res);
-       if (!dev_res)
-               return;
-
-       /* Is there room to extend the window? */
-       if (available - resource_size(res) <= dev_res->add_size)
-               return;
+       if (new_size > size) {
+               add_size = new_size - size;
+               pci_dbg(bridge, "bridge window %pR extended by %pa\n", res,
+                       &add_size);
+       } else if (new_size < size) {
+               add_size = size - new_size;
+               pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res,
+                       &add_size);
+       }
 
-       dev_res->add_size = available - resource_size(res);
-       pci_dbg(bridge, "bridge window %pR extended by %pa\n", res,
-               &dev_res->add_size);
+       res->end = res->start + new_size - 1;
+       remove_from_list(add_list, res);
 }
 
 static void pci_bus_distribute_available_resources(struct pci_bus *bus,
                                            struct list_head *add_list,
-                                           resource_size_t available_io,
-                                           resource_size_t available_mmio,
-                                           resource_size_t available_mmio_pref)
+                                           struct resource io,
+                                           struct resource mmio,
+                                           struct resource mmio_pref)
 {
-       resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref;
        unsigned int normal_bridges = 0, hotplug_bridges = 0;
        struct resource *io_res, *mmio_res, *mmio_pref_res;
        struct pci_dev *dev, *bridge = bus->self;
+       resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp, align;
 
        io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
        mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
        mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
 
        /*
-        * Update additional resource list (add_list) to fill all the
-        * extra resource space available for this port except the space
-        * calculated in __pci_bus_size_bridges() which covers all the
-        * devices currently connected to the port and below.
+        * The alignment of this bridge is yet to be considered, hence it must
+        * be done now before extending its bridge window.
+        */
+       align = pci_resource_alignment(bridge, io_res);
+       if (!io_res->parent && align)
+               io.start = min(ALIGN(io.start, align), io.end + 1);
+
+       align = pci_resource_alignment(bridge, mmio_res);
+       if (!mmio_res->parent && align)
+               mmio.start = min(ALIGN(mmio.start, align), mmio.end + 1);
+
+       align = pci_resource_alignment(bridge, mmio_pref_res);
+       if (!mmio_pref_res->parent && align)
+               mmio_pref.start = min(ALIGN(mmio_pref.start, align),
+                       mmio_pref.end + 1);
+
+       /*
+        * Now that we have adjusted for alignment, update the bridge window
+        * resources to fill as much remaining resource space as possible.
         */
-       extend_bridge_window(bridge, io_res, add_list, available_io);
-       extend_bridge_window(bridge, mmio_res, add_list, available_mmio);
-       extend_bridge_window(bridge, mmio_pref_res, add_list,
-                            available_mmio_pref);
+       adjust_bridge_window(bridge, io_res, add_list, resource_size(&io));
+       adjust_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio));
+       adjust_bridge_window(bridge, mmio_pref_res, add_list,
+                            resource_size(&mmio_pref));
 
        /*
         * Calculate how many hotplug bridges and normal bridges there
@@ -1902,11 +1924,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
         */
        if (hotplug_bridges + normal_bridges == 1) {
                dev = list_first_entry(&bus->devices, struct pci_dev, bus_list);
-               if (dev->subordinate) {
+               if (dev->subordinate)
                        pci_bus_distribute_available_resources(dev->subordinate,
-                               add_list, available_io, available_mmio,
-                               available_mmio_pref);
-               }
+                               add_list, io, mmio, mmio_pref);
                return;
        }
 
@@ -1919,12 +1939,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
         * extra space reduced by the minimal required space for the
         * non-hotplug bridges.
         */
-       remaining_io = available_io;
-       remaining_mmio = available_mmio;
-       remaining_mmio_pref = available_mmio_pref;
-
        for_each_pci_bridge(dev, bus) {
-               const struct resource *res;
+               resource_size_t used_size;
+               struct resource *res;
 
                if (dev->is_hotplug_bridge)
                        continue;
@@ -1934,24 +1951,39 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
                 * bridge and devices below it occupy.
                 */
                res = &dev->resource[PCI_BRIDGE_RESOURCES + 0];
-               if (!res->parent && available_io > resource_size(res))
-                       remaining_io -= resource_size(res);
+               align = pci_resource_alignment(dev, res);
+               align = align ? ALIGN(io.start, align) - io.start : 0;
+               used_size = align + resource_size(res);
+               if (!res->parent)
+                       io.start = min(io.start + used_size, io.end + 1);
 
                res = &dev->resource[PCI_BRIDGE_RESOURCES + 1];
-               if (!res->parent && available_mmio > resource_size(res))
-                       remaining_mmio -= resource_size(res);
+               align = pci_resource_alignment(dev, res);
+               align = align ? ALIGN(mmio.start, align) - mmio.start : 0;
+               used_size = align + resource_size(res);
+               if (!res->parent)
+                       mmio.start = min(mmio.start + used_size, mmio.end + 1);
 
                res = &dev->resource[PCI_BRIDGE_RESOURCES + 2];
-               if (!res->parent && available_mmio_pref > resource_size(res))
-                       remaining_mmio_pref -= resource_size(res);
+               align = pci_resource_alignment(dev, res);
+               align = align ? ALIGN(mmio_pref.start, align) -
+                       mmio_pref.start : 0;
+               used_size = align + resource_size(res);
+               if (!res->parent)
+                       mmio_pref.start = min(mmio_pref.start + used_size,
+                               mmio_pref.end + 1);
        }
 
+       io_per_hp = div64_ul(resource_size(&io), hotplug_bridges);
+       mmio_per_hp = div64_ul(resource_size(&mmio), hotplug_bridges);
+       mmio_pref_per_hp = div64_ul(resource_size(&mmio_pref),
+               hotplug_bridges);
+
        /*
         * Go over devices on this bus and distribute the remaining
         * resource space between hotplug bridges.
         */
        for_each_pci_bridge(dev, bus) {
-               resource_size_t align, io, mmio, mmio_pref;
                struct pci_bus *b;
 
                b = dev->subordinate;
@@ -1963,42 +1995,31 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
                 * hotplug-capable downstream ports taking alignment into
                 * account.
                 */
-               align = pci_resource_alignment(bridge, io_res);
-               io = div64_ul(available_io, hotplug_bridges);
-               io = min(ALIGN(io, align), remaining_io);
-               remaining_io -= io;
-
-               align = pci_resource_alignment(bridge, mmio_res);
-               mmio = div64_ul(available_mmio, hotplug_bridges);
-               mmio = min(ALIGN(mmio, align), remaining_mmio);
-               remaining_mmio -= mmio;
-
-               align = pci_resource_alignment(bridge, mmio_pref_res);
-               mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges);
-               mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref);
-               remaining_mmio_pref -= mmio_pref;
+               io.end = io.start + io_per_hp - 1;
+               mmio.end = mmio.start + mmio_per_hp - 1;
+               mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1;
 
                pci_bus_distribute_available_resources(b, add_list, io, mmio,
                                                       mmio_pref);
+
+               io.start += io_per_hp;
+               mmio.start += mmio_per_hp;
+               mmio_pref.start += mmio_pref_per_hp;
        }
 }
 
 static void pci_bridge_distribute_available_resources(struct pci_dev *bridge,
                                                     struct list_head *add_list)
 {
-       resource_size_t available_io, available_mmio, available_mmio_pref;
-       const struct resource *res;
+       struct resource available_io, available_mmio, available_mmio_pref;
 
        if (!bridge->is_hotplug_bridge)
                return;
 
        /* Take the initial extra resources from the hotplug port */
-       res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
-       available_io = resource_size(res);
-       res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
-       available_mmio = resource_size(res);
-       res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
-       available_mmio_pref = resource_size(res);
+       available_io = bridge->resource[PCI_BRIDGE_RESOURCES + 0];
+       available_mmio = bridge->resource[PCI_BRIDGE_RESOURCES + 1];
+       available_mmio_pref = bridge->resource[PCI_BRIDGE_RESOURCES + 2];
 
        pci_bus_distribute_available_resources(bridge->subordinate,
                                               add_list, available_io,
@@ -2055,12 +2076,18 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
        /* Restore size and flags */
        list_for_each_entry(fail_res, &fail_head, list) {
                struct resource *res = fail_res->res;
+               int idx;
 
                res->start = fail_res->start;
                res->end = fail_res->end;
                res->flags = fail_res->flags;
-               if (fail_res->dev->subordinate)
-                       res->flags = 0;
+
+               if (pci_is_bridge(fail_res->dev)) {
+                       idx = res - &fail_res->dev->resource[0];
+                       if (idx >= PCI_BRIDGE_RESOURCES &&
+                           idx <= PCI_BRIDGE_RESOURCE_END)
+                               res->flags = 0;
+               }
        }
        free_list(&fail_head);