Commit 4723d0f2 authored by Bjorn Helgaas's avatar Bjorn Helgaas Committed by Jesse Barnes

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=17011Reported-and-tested-by: default avatarAnisse Astier <anisse@astier.eu>
Reported-and-tested-by: default avatarPramod Dematagoda <pmd.lotr.gandalf@gmail.com>
Signed-off-by: default avatarBjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent ac3abf2c
...@@ -138,7 +138,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data) ...@@ -138,7 +138,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
struct acpi_resource_address64 addr; struct acpi_resource_address64 addr;
acpi_status status; acpi_status status;
unsigned long flags; unsigned long flags;
struct resource *root, *conflict;
u64 start, end; u64 start, end;
status = resource_to_addr(acpi_res, &addr); status = resource_to_addr(acpi_res, &addr);
...@@ -146,12 +145,10 @@ setup_resource(struct acpi_resource *acpi_res, void *data) ...@@ -146,12 +145,10 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
return AE_OK; return AE_OK;
if (addr.resource_type == ACPI_MEMORY_RANGE) { if (addr.resource_type == ACPI_MEMORY_RANGE) {
root = &iomem_resource;
flags = IORESOURCE_MEM; flags = IORESOURCE_MEM;
if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY) if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
flags |= IORESOURCE_PREFETCH; flags |= IORESOURCE_PREFETCH;
} else if (addr.resource_type == ACPI_IO_RANGE) { } else if (addr.resource_type == ACPI_IO_RANGE) {
root = &ioport_resource;
flags = IORESOURCE_IO; flags = IORESOURCE_IO;
} else } else
return AE_OK; return AE_OK;
...@@ -172,25 +169,90 @@ setup_resource(struct acpi_resource *acpi_res, void *data) ...@@ -172,25 +169,90 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
return AE_OK; return AE_OK;
} }
conflict = insert_resource_conflict(root, res); info->res_num++;
if (conflict) { if (addr.translation_offset)
dev_err(&info->bridge->dev, dev_info(&info->bridge->dev, "host bridge window %pR "
"address space collision: host bridge window %pR " "(PCI address [%#llx-%#llx])\n",
"conflicts with %s %pR\n", res, res->start - addr.translation_offset,
res, conflict->name, conflict); res->end - addr.translation_offset);
} else { else
pci_bus_add_resource(info->bus, res, 0); dev_info(&info->bridge->dev, "host bridge window %pR\n", res);
info->res_num++;
if (addr.translation_offset) return AE_OK;
dev_info(&info->bridge->dev, "host bridge window %pR " }
"(PCI address [%#llx-%#llx])\n",
res, res->start - addr.translation_offset, static bool resource_contains(struct resource *res, resource_size_t point)
res->end - addr.translation_offset); {
if (res->start <= point && point <= res->end)
return true;
return false;
}
static void coalesce_windows(struct pci_root_info *info, int type)
{
int i, j;
struct resource *res1, *res2;
for (i = 0; i < info->res_num; i++) {
res1 = &info->res[i];
if (!(res1->flags & type))
continue;
for (j = i + 1; j < info->res_num; j++) {
res2 = &info->res[j];
if (!(res2->flags & type))
continue;
/*
* I don't like throwing away windows because then
* our resources no longer match the ACPI _CRS, but
* the kernel resource tree doesn't allow overlaps.
*/
if (resource_contains(res1, res2->start) ||
resource_contains(res1, res2->end) ||
resource_contains(res2, res1->start) ||
resource_contains(res2, res1->end)) {
res1->start = min(res1->start, res2->start);
res1->end = max(res1->end, res2->end);
dev_info(&info->bridge->dev,
"host bridge window expanded to %pR; %pR ignored\n",
res1, res2);
res2->flags = 0;
}
}
}
}
static void add_resources(struct pci_root_info *info)
{
int i;
struct resource *res, *root, *conflict;
if (!pci_use_crs)
return;
coalesce_windows(info, IORESOURCE_MEM);
coalesce_windows(info, IORESOURCE_IO);
for (i = 0; i < info->res_num; i++) {
res = &info->res[i];
if (res->flags & IORESOURCE_MEM)
root = &iomem_resource;
else if (res->flags & IORESOURCE_IO)
root = &ioport_resource;
else else
dev_info(&info->bridge->dev, continue;
"host bridge window %pR\n", res);
conflict = insert_resource_conflict(root, res);
if (conflict)
dev_err(&info->bridge->dev,
"address space collision: host bridge window %pR "
"conflicts with %s %pR\n",
res, conflict->name, conflict);
else
pci_bus_add_resource(info->bus, res, 0);
} }
return AE_OK;
} }
static void static void
...@@ -224,6 +286,7 @@ get_current_resources(struct acpi_device *device, int busnum, ...@@ -224,6 +286,7 @@ get_current_resources(struct acpi_device *device, int busnum,
acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
&info); &info);
add_resources(&info);
return; return;
name_alloc_fail: name_alloc_fail:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment