PCI: augment bus resource table with a list
authorBjorn Helgaas <bjorn.helgaas@hp.com>
Tue, 23 Feb 2010 17:24:36 +0000 (10:24 -0700)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 23 Feb 2010 17:43:37 +0000 (09:43 -0800)
Previously we used a table of size PCI_BUS_NUM_RESOURCES (16) for resources
forwarded to a bus by its upstream bridge.  We've increased this size
several times when the table overflowed.

But there's no good limit on the number of resources because host bridges
and subtractive decode bridges can forward any number of ranges to their
secondary buses.

This patch reduces the table to only PCI_BRIDGE_RESOURCE_NUM (4) entries,
which corresponds to the number of windows a PCI-to-PCI (3) or CardBus (4)
bridge can positively decode.  Any additional resources, e.g., PCI host
bridge windows or subtractively-decoded regions, are kept in a list.

I'd prefer a single list rather than this split table/list approach, but
that requires simultaneous changes to every architecture.  This approach
only requires immediate changes where we set up (a) host bridges with more
than four windows and (b) subtractive-decode P2P bridges, and we can
incrementally change other architectures to use the list.

Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
arch/ia64/pci/pci.c
arch/x86/pci/acpi.c
arch/x86/pci/bus_numa.c
arch/x86/pci/bus_numa.h
drivers/pci/bus.c
drivers/pci/probe.c
include/linux/pci.h

index 89f957ca3eb2ece2b31d466740e05f7589f5afeb..64aff520b899d59ef84a4c94032eb9cc13f7f3fa 100644 (file)
@@ -320,9 +320,9 @@ static __devinit acpi_status add_window(struct acpi_resource *res, void *data)
 static void __devinit
 pcibios_setup_root_windows(struct pci_bus *bus, struct pci_controller *ctrl)
 {
-       int i, j;
+       int i;
 
-       j = 0;
+       pci_bus_remove_resources(bus);
        for (i = 0; i < ctrl->windows; i++) {
                struct resource *res = &ctrl->window[i].resource;
                /* HP's firmware has a hack to work around a Windows bug.
@@ -330,13 +330,7 @@ pcibios_setup_root_windows(struct pci_bus *bus, struct pci_controller *ctrl)
                if ((res->flags & IORESOURCE_MEM) &&
                    (res->end - res->start < 16))
                        continue;
-               if (j >= PCI_BUS_NUM_RESOURCES) {
-                       dev_warn(&bus->dev,
-                                "ignoring host bridge window %pR (no space)\n",
-                                res);
-                       continue;
-               }
-               bus->resource[j++] = res;
+               pci_bus_add_resource(bus, res, 0);
        }
 }
 
index 959e548a7039e81a85cdd0e7eb114a86f91a05c5..a2f8cdb8c1d5d10cceb424e70e6053716f4d3906 100644 (file)
@@ -45,20 +45,6 @@ count_resource(struct acpi_resource *acpi_res, void *data)
        return AE_OK;
 }
 
-static int
-bus_has_transparent_bridge(struct pci_bus *bus)
-{
-       struct pci_dev *dev;
-
-       list_for_each_entry(dev, &bus->devices, bus_list) {
-               u16 class = dev->class >> 8;
-
-               if (class == PCI_CLASS_BRIDGE_PCI && dev->transparent)
-                       return true;
-       }
-       return false;
-}
-
 static void
 align_resource(struct acpi_device *bridge, struct resource *res)
 {
@@ -92,12 +78,8 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
        acpi_status status;
        unsigned long flags;
        struct resource *root;
-       int max_root_bus_resources = PCI_BUS_NUM_RESOURCES;
        u64 start, end;
 
-       if (bus_has_transparent_bridge(info->bus))
-               max_root_bus_resources -= 3;
-
        status = resource_to_addr(acpi_res, &addr);
        if (!ACPI_SUCCESS(status))
                return AE_OK;
@@ -115,15 +97,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
 
        start = addr.minimum + addr.translation_offset;
        end = start + addr.address_length - 1;
-       if (info->res_num >= max_root_bus_resources) {
-               if (pci_probe & PCI_USE__CRS)
-                       printk(KERN_WARNING "PCI: Failed to allocate "
-                              "0x%lx-0x%lx from %s for %s due to _CRS "
-                              "returning more than %d resource descriptors\n",
-                              (unsigned long) start, (unsigned long) end,
-                              root->name, info->name, max_root_bus_resources);
-               return AE_OK;
-       }
 
        res = &info->res[info->res_num];
        res->name = info->name;
@@ -143,7 +116,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
                dev_err(&info->bridge->dev,
                        "can't allocate host bridge window %pR\n", res);
        } else {
-               info->bus->resource[info->res_num] = res;
+               pci_bus_add_resource(info->bus, res, 0);
                info->res_num++;
                if (addr.translation_offset)
                        dev_info(&info->bridge->dev, "host bridge window %pR "
@@ -164,7 +137,9 @@ get_current_resources(struct acpi_device *device, int busnum,
        struct pci_root_info info;
        size_t size;
 
-       if (!(pci_probe & PCI_USE__CRS))
+       if (pci_probe & PCI_USE__CRS)
+               pci_bus_remove_resources(bus);
+       else
                dev_info(&device->dev,
                         "ignoring host bridge windows from ACPI; "
                         "boot with \"pci=use_crs\" to use them\n");
index f939d603adfab4257dca888766584b378c0be5e9..12d54ff3654d2b2623ecc0cdd67cc8c417858e77 100644 (file)
@@ -36,13 +36,14 @@ void x86_pci_root_bus_res_quirks(struct pci_bus *b)
        printk(KERN_DEBUG "PCI: peer root bus %02x res updated from pci conf\n",
                        b->number);
 
+       pci_bus_remove_resources(b);
        info = &pci_root_info[i];
        for (j = 0; j < info->res_num; j++) {
                struct resource *res;
                struct resource *root;
 
                res = &info->res[j];
-               b->resource[j] = res;
+               pci_bus_add_resource(b, res, 0);
                if (res->flags & IORESOURCE_IO)
                        root = &ioport_resource;
                else
index adbc23fe82acc4fbcde63a5821838eab4567f0f7..731b64ee8d8411946e6e2011fa74b48df489c1b7 100644 (file)
@@ -2,8 +2,7 @@
 
 /*
  * sub bus (transparent) will use entres from 3 to store extra from
- * root, so need to make sure we have enough slot there, Should we
- * increase PCI_BUS_NUM_RESOURCES?
+ * root, so need to make sure we have enough slot there.
  */
 #define RES_NUM 16
 struct pci_root_info {
index e75d219fd107a4c727ce1219d28559fc300e53c0..712250f5874a88f0acd238429cd6237139a986f9 100644 (file)
 
 #include "pci.h"
 
+void pci_bus_add_resource(struct pci_bus *bus, struct resource *res,
+                         unsigned int flags)
+{
+       struct pci_bus_resource *bus_res;
+
+       bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL);
+       if (!bus_res) {
+               dev_err(&bus->dev, "can't add %pR resource\n", res);
+               return;
+       }
+
+       bus_res->res = res;
+       bus_res->flags = flags;
+       list_add_tail(&bus_res->list, &bus->resources);
+}
+
+struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n)
+{
+       struct pci_bus_resource *bus_res;
+
+       if (n < PCI_BRIDGE_RESOURCE_NUM)
+               return bus->resource[n];
+
+       n -= PCI_BRIDGE_RESOURCE_NUM;
+       list_for_each_entry(bus_res, &bus->resources, list) {
+               if (n-- == 0)
+                       return bus_res->res;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(pci_bus_resource_n);
+
+void pci_bus_remove_resources(struct pci_bus *bus)
+{
+       struct pci_bus_resource *bus_res, *tmp;
+       int i;
+
+       for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
+               bus->resource[i] = 0;
+
+       list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
+               list_del(&bus_res->list);
+               kfree(bus_res);
+       }
+}
+
 /**
  * pci_bus_alloc_resource - allocate a resource from a parent bus
  * @bus: PCI bus
index 70c4ed2e67cc7828b38699fbe5ef7638e4e468d6..270d069819f7070a9a27f9bdf36e8961e2f67e7e 100644 (file)
@@ -89,6 +89,7 @@ static void release_pcibus_dev(struct device *dev)
 
        if (pci_bus->bridge)
                put_device(pci_bus->bridge);
+       pci_bus_remove_resources(pci_bus);
        kfree(pci_bus);
 }
 
@@ -394,6 +395,7 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child)
 void __devinit pci_read_bridge_bases(struct pci_bus *child)
 {
        struct pci_dev *dev = child->self;
+       struct resource *res;
        int i;
 
        if (pci_is_root_bus(child))     /* It's a host bus, nothing to read */
@@ -403,17 +405,23 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
                 child->secondary, child->subordinate,
                 dev->transparent ? " (subtractive decode)" : "");
 
+       pci_bus_remove_resources(child);
+       for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
+               child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
+
        pci_read_bridge_io(child);
        pci_read_bridge_mmio(child);
        pci_read_bridge_mmio_pref(child);
 
        if (dev->transparent) {
-               for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) {
-                       child->resource[i] = child->parent->resource[i - 3];
-                       if (child->resource[i])
+               pci_bus_for_each_resource(child->parent, res, i) {
+                       if (res) {
+                               pci_bus_add_resource(child, res,
+                                                    PCI_SUBTRACTIVE_DECODE);
                                dev_printk(KERN_DEBUG, &dev->dev,
                                           "  bridge window %pR (subtractive decode)\n",
-                                          child->resource[i]);
+                                          res);
+                       }
                }
        }
 }
@@ -428,6 +436,7 @@ static struct pci_bus * pci_alloc_bus(void)
                INIT_LIST_HEAD(&b->children);
                INIT_LIST_HEAD(&b->devices);
                INIT_LIST_HEAD(&b->slots);
+               INIT_LIST_HEAD(&b->resources);
                b->max_bus_speed = PCI_SPEED_UNKNOWN;
                b->cur_bus_speed = PCI_SPEED_UNKNOWN;
        }
index 2ff9d26a078f47cf068b72aae45affe1acc62c9a..e19a69613d8f01548a980425e96ac121e4f8421f 100644 (file)
@@ -364,9 +364,26 @@ static inline void pci_add_saved_cap(struct pci_dev *pci_dev,
        hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space);
 }
 
-#ifndef PCI_BUS_NUM_RESOURCES
-#define PCI_BUS_NUM_RESOURCES  16
-#endif
+/*
+ * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond
+ * to P2P or CardBus bridge windows) go in a table.  Additional ones (for
+ * buses below host bridges or subtractive decode bridges) go in the list.
+ * Use pci_bus_for_each_resource() to iterate through all the resources.
+ */
+
+/*
+ * PCI_SUBTRACTIVE_DECODE means the bridge forwards the window implicitly
+ * and there's no way to program the bridge with the details of the window.
+ * This does not apply to ACPI _CRS windows, even with the _DEC subtractive-
+ * decode bit set, because they are explicit and can be programmed with _SRS.
+ */
+#define PCI_SUBTRACTIVE_DECODE 0x1
+
+struct pci_bus_resource {
+       struct list_head list;
+       struct resource *res;
+       unsigned int flags;
+};
 
 #define PCI_REGION_FLAG_MASK   0x0fU   /* These bits of resource flags tell us the PCI region flags */
 
@@ -377,8 +394,8 @@ struct pci_bus {
        struct list_head devices;       /* list of devices on this bus */
        struct pci_dev  *self;          /* bridge device as seen by parent */
        struct list_head slots;         /* list of slots on this bus */
-       struct resource *resource[PCI_BUS_NUM_RESOURCES];
-                                       /* address space routed to this bus */
+       struct resource *resource[PCI_BRIDGE_RESOURCE_NUM];
+       struct list_head resources;     /* address space routed to this bus */
 
        struct pci_ops  *ops;           /* configuration access functions */
        void            *sysdata;       /* hook for sys-specific extension */
@@ -829,8 +846,14 @@ int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *);
 void pci_release_selected_regions(struct pci_dev *, int);
 
 /* drivers/pci/bus.c */
+void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, unsigned int flags);
+struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n);
+void pci_bus_remove_resources(struct pci_bus *bus);
+
 #define pci_bus_for_each_resource(bus, res, i)                         \
-       for (i = 0; res = bus->resource[i], i < PCI_BUS_NUM_RESOURCES; i++)
+       for (i = 0;                                                     \
+           (res = pci_bus_resource_n(bus, i)) || i < PCI_BRIDGE_RESOURCE_NUM; \
+            i++)
 
 int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
                        struct resource *res, resource_size_t size,