Skip to content

Commit

Permalink
x86/PCI: coalesce overlapping host bridge windows
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Bjorn Helgaas authored and Jesse Barnes committed Nov 11, 2010
1 parent ac3abf2 commit 4723d0f
Showing 1 changed file with 83 additions and 20 deletions.
103 changes: 83 additions & 20 deletions arch/x86/pci/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,17 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
struct acpi_resource_address64 addr;
acpi_status status;
unsigned long flags;
struct resource *root, *conflict;
u64 start, end;

status = resource_to_addr(acpi_res, &addr);
if (!ACPI_SUCCESS(status))
return AE_OK;

if (addr.resource_type == ACPI_MEMORY_RANGE) {
root = &iomem_resource;
flags = IORESOURCE_MEM;
if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
flags |= IORESOURCE_PREFETCH;
} else if (addr.resource_type == ACPI_IO_RANGE) {
root = &ioport_resource;
flags = IORESOURCE_IO;
} else
return AE_OK;
Expand All @@ -172,25 +169,90 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
return AE_OK;
}

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);
info->res_num++;
if (addr.translation_offset)
dev_info(&info->bridge->dev, "host bridge window %pR "
"(PCI address [%#llx-%#llx])\n",
res, res->start - addr.translation_offset,
res->end - addr.translation_offset);
info->res_num++;
if (addr.translation_offset)
dev_info(&info->bridge->dev, "host bridge window %pR "
"(PCI address [%#llx-%#llx])\n",
res, res->start - addr.translation_offset,
res->end - addr.translation_offset);
else
dev_info(&info->bridge->dev, "host bridge window %pR\n", res);

return AE_OK;
}

static bool resource_contains(struct resource *res, resource_size_t point)
{
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
dev_info(&info->bridge->dev,
"host bridge window %pR\n", res);
continue;

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
Expand Down Expand Up @@ -224,6 +286,7 @@ get_current_resources(struct acpi_device *device, int busnum,
acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
&info);

add_resources(&info);
return;

name_alloc_fail:
Expand Down

0 comments on commit 4723d0f

Please sign in to comment.