Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2fe2abf8 authored by Bjorn Helgaas's avatar Bjorn Helgaas Committed by Jesse Barnes
Browse files

PCI: augment bus resource table with a list



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: default avatarBjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent 89a74ecc
Loading
Loading
Loading
Loading
+3 −9
Original line number Diff line number Diff line
@@ -320,9 +320,9 @@ static __devinit acpi_status add_window(struct acpi_resource *res, void *data)
static void __devinit
pcibios_setup_root_windows(struct pci_bus *bus, struct pci_controller *ctrl)
{
	int i, j;
	int i;

	j = 0;
	pci_bus_remove_resources(bus);
	for (i = 0; i < ctrl->windows; i++) {
		struct resource *res = &ctrl->window[i].resource;
		/* HP's firmware has a hack to work around a Windows bug.
@@ -330,13 +330,7 @@ pcibios_setup_root_windows(struct pci_bus *bus, struct pci_controller *ctrl)
		if ((res->flags & IORESOURCE_MEM) &&
		    (res->end - res->start < 16))
			continue;
		if (j >= PCI_BUS_NUM_RESOURCES) {
			dev_warn(&bus->dev,
				 "ignoring host bridge window %pR (no space)\n",
				 res);
			continue;
		}
		bus->resource[j++] = res;
		pci_bus_add_resource(bus, res, 0);
	}
}

+4 −29
Original line number Diff line number Diff line
@@ -45,20 +45,6 @@ count_resource(struct acpi_resource *acpi_res, void *data)
	return AE_OK;
}

static int
bus_has_transparent_bridge(struct pci_bus *bus)
{
	struct pci_dev *dev;

	list_for_each_entry(dev, &bus->devices, bus_list) {
		u16 class = dev->class >> 8;

		if (class == PCI_CLASS_BRIDGE_PCI && dev->transparent)
			return true;
	}
	return false;
}

static void
align_resource(struct acpi_device *bridge, struct resource *res)
{
@@ -92,12 +78,8 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
	acpi_status status;
	unsigned long flags;
	struct resource *root;
	int max_root_bus_resources = PCI_BUS_NUM_RESOURCES;
	u64 start, end;

	if (bus_has_transparent_bridge(info->bus))
		max_root_bus_resources -= 3;

	status = resource_to_addr(acpi_res, &addr);
	if (!ACPI_SUCCESS(status))
		return AE_OK;
@@ -115,15 +97,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)

	start = addr.minimum + addr.translation_offset;
	end = start + addr.address_length - 1;
	if (info->res_num >= max_root_bus_resources) {
		if (pci_probe & PCI_USE__CRS)
			printk(KERN_WARNING "PCI: Failed to allocate "
			       "0x%lx-0x%lx from %s for %s due to _CRS "
			       "returning more than %d resource descriptors\n",
			       (unsigned long) start, (unsigned long) end,
			       root->name, info->name, max_root_bus_resources);
		return AE_OK;
	}

	res = &info->res[info->res_num];
	res->name = info->name;
@@ -143,7 +116,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
		dev_err(&info->bridge->dev,
			"can't allocate host bridge window %pR\n", res);
	} else {
		info->bus->resource[info->res_num] = res;
		pci_bus_add_resource(info->bus, res, 0);
		info->res_num++;
		if (addr.translation_offset)
			dev_info(&info->bridge->dev, "host bridge window %pR "
@@ -164,7 +137,9 @@ get_current_resources(struct acpi_device *device, int busnum,
	struct pci_root_info info;
	size_t size;

	if (!(pci_probe & PCI_USE__CRS))
	if (pci_probe & PCI_USE__CRS)
		pci_bus_remove_resources(bus);
	else
		dev_info(&device->dev,
			 "ignoring host bridge windows from ACPI; "
			 "boot with \"pci=use_crs\" to use them\n");
+2 −1
Original line number Diff line number Diff line
@@ -36,13 +36,14 @@ void x86_pci_root_bus_res_quirks(struct pci_bus *b)
	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];
		b->resource[j] = res;
		pci_bus_add_resource(b, res, 0);
		if (res->flags & IORESOURCE_IO)
			root = &ioport_resource;
		else
+1 −2
Original line number Diff line number Diff line
@@ -2,8 +2,7 @@

/*
 * sub bus (transparent) will use entres from 3 to store extra from
 * root, so need to make sure we have enough slot there, Should we
 * increase PCI_BUS_NUM_RESOURCES?
 * root, so need to make sure we have enough slot there.
 */
#define RES_NUM 16
struct pci_root_info {
+46 −0
Original line number Diff line number Diff line
@@ -17,6 +17,52 @@

#include "pci.h"

void pci_bus_add_resource(struct pci_bus *bus, struct resource *res,
			  unsigned int flags)
{
	struct pci_bus_resource *bus_res;

	bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL);
	if (!bus_res) {
		dev_err(&bus->dev, "can't add %pR resource\n", res);
		return;
	}

	bus_res->res = res;
	bus_res->flags = flags;
	list_add_tail(&bus_res->list, &bus->resources);
}

struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n)
{
	struct pci_bus_resource *bus_res;

	if (n < PCI_BRIDGE_RESOURCE_NUM)
		return bus->resource[n];

	n -= PCI_BRIDGE_RESOURCE_NUM;
	list_for_each_entry(bus_res, &bus->resources, list) {
		if (n-- == 0)
			return bus_res->res;
	}
	return NULL;
}
EXPORT_SYMBOL_GPL(pci_bus_resource_n);

void pci_bus_remove_resources(struct pci_bus *bus)
{
	struct pci_bus_resource *bus_res, *tmp;
	int i;

	for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
		bus->resource[i] = 0;

	list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
		list_del(&bus_res->list);
		kfree(bus_res);
	}
}

/**
 * pci_bus_alloc_resource - allocate a resource from a parent bus
 * @bus: PCI bus
Loading