aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBjorn Helgaas <bjorn.helgaas@hp.com>2010-10-26 17:41:39 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2010-10-26 18:33:37 -0400
commitb126b4703afa4010b161784a43650337676dd03b (patch)
tree63c03638d5724fc8a26bc40a12ab5621af94fb15 /drivers
parente7f8567db9a7f6b3151b0b275e245c1cef0d9c70 (diff)
PCI: allocate bus resources from the top down
Allocate space from the highest-address PCI bus resource first, then work downward. Previously, we looked for space in PCI host bridge windows in the order we discovered the windows. For example, given the following windows (discovered via an ACPI _CRS method): pci_root PNP0A03:00: host bridge window [mem 0x000a0000-0x000bffff] pci_root PNP0A03:00: host bridge window [mem 0x000c0000-0x000effff] pci_root PNP0A03:00: host bridge window [mem 0x000f0000-0x000fffff] pci_root PNP0A03:00: host bridge window [mem 0xbff00000-0xf7ffffff] pci_root PNP0A03:00: host bridge window [mem 0xff980000-0xff980fff] pci_root PNP0A03:00: host bridge window [mem 0xff97c000-0xff97ffff] pci_root PNP0A03:00: host bridge window [mem 0xfed20000-0xfed9ffff] we attempted to allocate from [mem 0x000a0000-0x000bffff] first, then [mem 0x000c0000-0x000effff], and so on. With this patch, we allocate from [mem 0xff980000-0xff980fff] first, then [mem 0xff97c000-0xff97ffff], [mem 0xfed20000-0xfed9ffff], etc. Allocating top-down follows Windows practice, so we're less likely to trip over BIOS defects in the _CRS description. On the machine above (a Dell T3500), the [mem 0xbff00000-0xbfffffff] region doesn't actually work and is likely a BIOS defect. The symptom is that we move the AHCI controller to 0xbff00000, which leads to "Boot has failed, sleeping forever," a BUG in ahci_stop_engine(), or some other boot failure. Reference: https://bugzilla.kernel.org/show_bug.cgi?id=16228#c43 Reference: https://bugzilla.redhat.com/show_bug.cgi?id=620313 Reference: https://bugzilla.redhat.com/show_bug.cgi?id=629933 Reported-by: Brian Bloniarz <phunge0@hotmail.com> Reported-and-tested-by: Stefan Becker <chemobejk@gmail.com> Reported-by: Denys Vlasenko <dvlasenk@redhat.com> Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/bus.c53
1 files changed, 48 insertions, 5 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 7f0af0e9b826..172bf26e0680 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -64,6 +64,49 @@ void pci_bus_remove_resources(struct pci_bus *bus)
64 } 64 }
65} 65}
66 66
67/*
68 * Find the highest-address bus resource below the cursor "res". If the
69 * cursor is NULL, return the highest resource.
70 */
71static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus,
72 unsigned int type,
73 struct resource *res)
74{
75 struct resource *r, *prev = NULL;
76 int i;
77
78 pci_bus_for_each_resource(bus, r, i) {
79 if (!r)
80 continue;
81
82 if ((r->flags & IORESOURCE_TYPE_BITS) != type)
83 continue;
84
85 /* If this resource is at or past the cursor, skip it */
86 if (res) {
87 if (r == res)
88 continue;
89 if (r->end > res->end)
90 continue;
91 if (r->end == res->end && r->start > res->start)
92 continue;
93 }
94
95 if (!prev)
96 prev = r;
97
98 /*
99 * A small resource is higher than a large one that ends at
100 * the same address.
101 */
102 if (r->end > prev->end ||
103 (r->end == prev->end && r->start > prev->start))
104 prev = r;
105 }
106
107 return prev;
108}
109
67/** 110/**
68 * pci_bus_alloc_resource - allocate a resource from a parent bus 111 * pci_bus_alloc_resource - allocate a resource from a parent bus
69 * @bus: PCI bus 112 * @bus: PCI bus
@@ -89,9 +132,10 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
89 resource_size_t), 132 resource_size_t),
90 void *alignf_data) 133 void *alignf_data)
91{ 134{
92 int i, ret = -ENOMEM; 135 int ret = -ENOMEM;
93 struct resource *r; 136 struct resource *r;
94 resource_size_t max = -1; 137 resource_size_t max = -1;
138 unsigned int type = res->flags & IORESOURCE_TYPE_BITS;
95 139
96 type_mask |= IORESOURCE_IO | IORESOURCE_MEM; 140 type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
97 141
@@ -99,10 +143,9 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
99 if (!(res->flags & IORESOURCE_MEM_64)) 143 if (!(res->flags & IORESOURCE_MEM_64))
100 max = PCIBIOS_MAX_MEM_32; 144 max = PCIBIOS_MAX_MEM_32;
101 145
102 pci_bus_for_each_resource(bus, r, i) { 146 /* Look for space at highest addresses first */
103 if (!r) 147 r = pci_bus_find_resource_prev(bus, type, NULL);
104 continue; 148 for ( ; r; r = pci_bus_find_resource_prev(bus, type, r)) {
105
106 /* type_mask must match */ 149 /* type_mask must match */
107 if ((res->flags ^ r->flags) & type_mask) 150 if ((res->flags ^ r->flags) & type_mask)
108 continue; 151 continue;