2fe2abf896
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>
102 lines
2 KiB
C
102 lines
2 KiB
C
#include <linux/init.h>
|
|
#include <linux/pci.h>
|
|
|
|
#include "bus_numa.h"
|
|
|
|
int pci_root_num;
|
|
struct pci_root_info pci_root_info[PCI_ROOT_NR];
|
|
int found_all_numa_early;
|
|
|
|
void x86_pci_root_bus_res_quirks(struct pci_bus *b)
|
|
{
|
|
int i;
|
|
int j;
|
|
struct pci_root_info *info;
|
|
|
|
/* don't go for it if _CRS is used already */
|
|
if (b->resource[0] != &ioport_resource ||
|
|
b->resource[1] != &iomem_resource)
|
|
return;
|
|
|
|
if (!pci_root_num)
|
|
return;
|
|
|
|
/* for amd, if only one root bus, don't need to do anything */
|
|
if (pci_root_num < 2 && found_all_numa_early)
|
|
return;
|
|
|
|
for (i = 0; i < pci_root_num; i++) {
|
|
if (pci_root_info[i].bus_min == b->number)
|
|
break;
|
|
}
|
|
|
|
if (i == pci_root_num)
|
|
return;
|
|
|
|
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];
|
|
pci_bus_add_resource(b, res, 0);
|
|
if (res->flags & IORESOURCE_IO)
|
|
root = &ioport_resource;
|
|
else
|
|
root = &iomem_resource;
|
|
insert_resource(root, res);
|
|
}
|
|
}
|
|
|
|
void __devinit update_res(struct pci_root_info *info, size_t start,
|
|
size_t end, unsigned long flags, int merge)
|
|
{
|
|
int i;
|
|
struct resource *res;
|
|
|
|
if (start > end)
|
|
return;
|
|
|
|
if (!merge)
|
|
goto addit;
|
|
|
|
/* try to merge it with old one */
|
|
for (i = 0; i < info->res_num; i++) {
|
|
size_t final_start, final_end;
|
|
size_t common_start, common_end;
|
|
|
|
res = &info->res[i];
|
|
if (res->flags != flags)
|
|
continue;
|
|
|
|
common_start = max((size_t)res->start, start);
|
|
common_end = min((size_t)res->end, end);
|
|
if (common_start > common_end + 1)
|
|
continue;
|
|
|
|
final_start = min((size_t)res->start, start);
|
|
final_end = max((size_t)res->end, end);
|
|
|
|
res->start = final_start;
|
|
res->end = final_end;
|
|
return;
|
|
}
|
|
|
|
addit:
|
|
|
|
/* need to add that */
|
|
if (info->res_num >= RES_NUM)
|
|
return;
|
|
|
|
res = &info->res[info->res_num];
|
|
res->name = info->name;
|
|
res->flags = flags;
|
|
res->start = start;
|
|
res->end = end;
|
|
res->child = NULL;
|
|
info->res_num++;
|
|
}
|