x86/PCI: coalesce overlapping host bridge windows

Some BIOSes provide PCI host bridge windows that overlap, e.g.,

pci_root PNP0A03:00: host bridge window [mem 0xb0000000-0xffffffff]
pci_root PNP0A03:00: host bridge window [mem 0xafffffff-0xdfffffff]
pci_root PNP0A03:00: host bridge window [mem 0xf0000000-0xffffffff]

If we simply insert these as children of iomem_resource, the second window
fails because it conflicts with the first, and the third is inserted as a
child of the first, i.e.,

b0000000-ffffffff PCI Bus 0000:00
f0000000-ffffffff PCI Bus 0000:00

When we claim PCI device resources, this can cause collisions like this
if we put them in the first window:

pci 0000:00:01.0: address space collision: [mem 0xff300000-0xff4fffff] conflicts with PCI Bus 0000:00 [mem 0xf0000000-0xffffffff]

Host bridge windows are top-level resources by definition, so it doesn't
make sense to make the third window a child of the first. This patch
coalesces any host bridge windows that overlap. For the example above,
the result is this single window:

pci_root PNP0A03:00: host bridge window [mem 0xafffffff-0xffffffff]

This fixes a 2.6.34 regression.

Reference: https://bugzilla.kernel.org/show_bug.cgi?id=17011
Reported-and-tested-by: Anisse Astier <anisse@astier.eu>
Reported-and-tested-by: Pramod Dematagoda <pmd.lotr.gandalf@gmail.com>
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

authored by Bjorn Helgaas and committed by Jesse Barnes 4723d0f2 ac3abf2c

+84 -21
+84 -21
arch/x86/pci/acpi.c
··· 138 138 struct acpi_resource_address64 addr; 139 139 acpi_status status; 140 140 unsigned long flags; 141 - struct resource *root, *conflict; 142 141 u64 start, end; 143 142 144 143 status = resource_to_addr(acpi_res, &addr); ··· 145 146 return AE_OK; 146 147 147 148 if (addr.resource_type == ACPI_MEMORY_RANGE) { 148 - root = &iomem_resource; 149 149 flags = IORESOURCE_MEM; 150 150 if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY) 151 151 flags |= IORESOURCE_PREFETCH; 152 152 } else if (addr.resource_type == ACPI_IO_RANGE) { 153 - root = &ioport_resource; 154 153 flags = IORESOURCE_IO; 155 154 } else 156 155 return AE_OK; ··· 169 172 return AE_OK; 170 173 } 171 174 172 - conflict = insert_resource_conflict(root, res); 173 - if (conflict) { 174 - dev_err(&info->bridge->dev, 175 - "address space collision: host bridge window %pR " 176 - "conflicts with %s %pR\n", 177 - res, conflict->name, conflict); 178 - } else { 179 - pci_bus_add_resource(info->bus, res, 0); 180 - info->res_num++; 181 - if (addr.translation_offset) 182 - dev_info(&info->bridge->dev, "host bridge window %pR " 183 - "(PCI address [%#llx-%#llx])\n", 184 - res, res->start - addr.translation_offset, 185 - res->end - addr.translation_offset); 186 - else 187 - dev_info(&info->bridge->dev, 188 - "host bridge window %pR\n", res); 189 - } 175 + info->res_num++; 176 + if (addr.translation_offset) 177 + dev_info(&info->bridge->dev, "host bridge window %pR " 178 + "(PCI address [%#llx-%#llx])\n", 179 + res, res->start - addr.translation_offset, 180 + res->end - addr.translation_offset); 181 + else 182 + dev_info(&info->bridge->dev, "host bridge window %pR\n", res); 183 + 190 184 return AE_OK; 185 + } 186 + 187 + static bool resource_contains(struct resource *res, resource_size_t point) 188 + { 189 + if (res->start <= point && point <= res->end) 190 + return true; 191 + return false; 192 + } 193 + 194 + static void coalesce_windows(struct pci_root_info *info, int type) 195 + { 196 + int i, j; 197 + struct resource *res1, *res2; 198 + 199 + for (i = 0; i < info->res_num; i++) { 200 + res1 = &info->res[i]; 201 + if (!(res1->flags & type)) 202 + continue; 203 + 204 + for (j = i + 1; j < info->res_num; j++) { 205 + res2 = &info->res[j]; 206 + if (!(res2->flags & type)) 207 + continue; 208 + 209 + /* 210 + * I don't like throwing away windows because then 211 + * our resources no longer match the ACPI _CRS, but 212 + * the kernel resource tree doesn't allow overlaps. 213 + */ 214 + if (resource_contains(res1, res2->start) || 215 + resource_contains(res1, res2->end) || 216 + resource_contains(res2, res1->start) || 217 + resource_contains(res2, res1->end)) { 218 + res1->start = min(res1->start, res2->start); 219 + res1->end = max(res1->end, res2->end); 220 + dev_info(&info->bridge->dev, 221 + "host bridge window expanded to %pR; %pR ignored\n", 222 + res1, res2); 223 + res2->flags = 0; 224 + } 225 + } 226 + } 227 + } 228 + 229 + static void add_resources(struct pci_root_info *info) 230 + { 231 + int i; 232 + struct resource *res, *root, *conflict; 233 + 234 + if (!pci_use_crs) 235 + return; 236 + 237 + coalesce_windows(info, IORESOURCE_MEM); 238 + coalesce_windows(info, IORESOURCE_IO); 239 + 240 + for (i = 0; i < info->res_num; i++) { 241 + res = &info->res[i]; 242 + 243 + if (res->flags & IORESOURCE_MEM) 244 + root = &iomem_resource; 245 + else if (res->flags & IORESOURCE_IO) 246 + root = &ioport_resource; 247 + else 248 + continue; 249 + 250 + conflict = insert_resource_conflict(root, res); 251 + if (conflict) 252 + dev_err(&info->bridge->dev, 253 + "address space collision: host bridge window %pR " 254 + "conflicts with %s %pR\n", 255 + res, conflict->name, conflict); 256 + else 257 + pci_bus_add_resource(info->bus, res, 0); 258 + } 191 259 } 192 260 193 261 static void ··· 286 224 acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, 287 225 &info); 288 226 227 + add_resources(&info); 289 228 return; 290 229 291 230 name_alloc_fail: