From akpm@osdl.org Wed Mar 30 13:29:03 2005 Subject: PCI-PCI transparent bridge handling improvements (pci core) To: greg@kroah.com Cc: akpm@osdl.org, linux@dominikbrodowski.net From: akpm@osdl.org Date: Wed, 30 Mar 2005 13:23:32 -0800 From: Dominik Brodowski "Transparent" PCI-PCI bridges are currently "ignored" by the resource management code in the PCI core. This means devices behind the bridge are handled as if there was no bridge. However, it seems more suitable -- and it seems to allow for proper "prefetch"-type memory handling, too -- to handle a transparent PCI-PCI bridge like any other PCI-PCI bridge, and to only break out of the limits set by the bridge windows if the resource allocation code determines it needs to do s. The tricky part is in pci_find_parent_resource(). There are two types of functions calling it: some functions already know the exact resource for which they want to find the parent in order to properly insert it into the resource database. This can be handled easily -- if the resource is inside the bridge window, this is returned; if it isn't, the bridge's parent resource is returned. However, two callers (yenta_socket and i2o) intend something different: they call pci_find_parent_resource() with an empty resource and want to find out the biggest valid resource of the proper type in order to analyze it and adapt its own hunger for resources to it. To keep this behaviour backwards-compatible, we always need to not limit it to the bridge window resources, but get back to the parent bus. This patch is a modified and (hopefully) improved derivation of Linus' "pcmcia-bridge-resource-management-fix.patch" included in 2.6.11-rc4-mm1. Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman Index: gregkh-2.6/drivers/pci/bus.c =================================================================== --- gregkh-2.6.orig/drivers/pci/bus.c 2005-04-12 22:36:19.000000000 -0700 +++ gregkh-2.6/drivers/pci/bus.c 2005-04-12 22:38:42.000000000 -0700 @@ -18,22 +18,12 @@ #include "pci.h" /** - * pci_bus_alloc_resource - allocate a resource from a parent bus - * @bus: PCI bus - * @res: resource to allocate - * @size: size of resource to allocate - * @align: alignment of resource to allocate - * @min: minimum /proc/iomem address to allocate - * @type_mask: IORESOURCE_* type flags - * @alignf: resource alignment function - * @alignf_data: data argument for resource alignment function + * pci_one_bus_alloc_resource - allocate a resource from one specific bus * - * Given the PCI bus a device resides on, the size, minimum address, - * alignment and type, try to find an acceptable resource allocation - * for a specific device resource. + * Always use pci_bus_alloc_resource() described below. */ -int -pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, +static int +pci_one_bus_alloc_resource(struct pci_bus *bus, struct resource *res, unsigned long size, unsigned long align, unsigned long min, unsigned int type_mask, void (*alignf)(void *, struct resource *, @@ -69,6 +59,48 @@ } /** + * pci_bus_alloc_resource - allocate a resource from a parent bus + * @bus: PCI bus + * @res: resource to allocate + * @size: size of resource to allocate + * @align: alignment of resource to allocate + * @min: minimum /proc/iomem address to allocate + * @type_mask: IORESOURCE_* type flags + * @alignf: resource alignment function + * @alignf_data: data argument for resource alignment function + * + * Given the PCI bus a device resides on, the size, minimum address, + * alignment and type, try to find an acceptable resource allocation + * for a specific device resource. + */ +int +pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, + unsigned long size, unsigned long align, unsigned long min, + unsigned int type_mask, + void (*alignf)(void *, struct resource *, + unsigned long, unsigned long), + void *alignf_data) +{ + int ret = pci_one_bus_alloc_resource(bus, res, size, align, min, + type_mask, alignf, alignf_data); + + /* + * If allocation from the resources available to this bus failed, + * and there is a transparent parent PCI-PCI bridge, we can check + * the resources of the parent bus as well + */ + while (ret && bus->self && bus->self->transparent) { + bus = bus->self->bus; + if (!bus) + return ret; + ret = pci_one_bus_alloc_resource(bus, res, size, align, min, + type_mask, alignf, alignf_data); + } + return ret; +} + + +/** * add a single device * @dev: device to add * Index: gregkh-2.6/drivers/pci/pci.c =================================================================== --- gregkh-2.6.orig/drivers/pci/pci.c 2005-04-12 22:38:38.000000000 -0700 +++ gregkh-2.6/drivers/pci/pci.c 2005-04-12 22:38:42.000000000 -0700 @@ -190,18 +190,13 @@ } /** - * pci_find_parent_resource - return resource region of parent bus of given region - * @dev: PCI device structure contains resources to be searched - * @res: child resource record for which parent is sought + * pci_bus_find_parent_resource * - * For given resource region of given device, return the resource - * region of parent bus the given region is contained in or where - * it should be allocated from. + * Always use pci_find_parent_resource below. */ -struct resource * -pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) +static struct resource * +pci_bus_find_parent_resource(const struct pci_bus *bus, struct resource *res) { - const struct pci_bus *bus = dev->bus; int i; struct resource *best = NULL; @@ -222,6 +217,54 @@ } /** + * pci_find_parent_resource - return resource region of parent bus of given region + * @dev: PCI device structure contains resources to be searched + * @res: child resource record for which parent is sought. + * + * For given resource region of given device, return the resource + * region of parent bus the given region is contained in or where + * it should be allocated from. + */ +struct resource * +pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) +{ + struct pci_bus *bus = dev->bus; + struct resource *best, *r; + + best = pci_bus_find_parent_resource(bus, res); + + /* + * If there's a transparent parent PCI-PCI bridge, we check the parent + * bus as well in the following occasions: + * - we didn't find any appropriate resource + * - res->start and res->end were 0. This is used in code which + * then wants to request large amounts of IO, so better not limit + * it to the sub bus. [yenta_socket.c, i2o/iop.c] + * - we did find a sub-optimal resource (no PREFETCH for PREFETCH) + */ + while (bus->self && bus->self->transparent && + (!best || (!res->start && !res->end) + || ((res->flags & IORESOURCE_PREFETCH) && + !(best->flags & IORESOURCE_PREFETCH)))) { + bus = bus->self->bus; + if (!bus) + return best; + r = pci_bus_find_parent_resource(bus, res); + + /* + * If we didn't find an appropriate resource before, use what + * we found in the parent bus; if it's just a matter of + * PREFETCH, only use it if it is optimal. + */ + if (!best || (!res->start && !res->end) + || ((r->flags & IORESOURCE_PREFETCH) && + !(best->flags & IORESOURCE_PREFETCH))) + best = r; + } + return best; +} + +/** * pci_set_power_state - Set the power state of a PCI device * @dev: PCI device to be suspended * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering Index: gregkh-2.6/drivers/pci/probe.c =================================================================== --- gregkh-2.6.orig/drivers/pci/probe.c 2005-04-12 22:38:38.000000000 -0700 +++ gregkh-2.6/drivers/pci/probe.c 2005-04-12 22:38:42.000000000 -0700 @@ -237,16 +237,10 @@ if (!dev) /* It's a host bus, nothing to read */ return; - if (dev->transparent) { - printk(KERN_INFO "PCI: Transparent bridge - %s\n", pci_name(dev)); - for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++) - child->resource[i] = child->parent->resource[i]; - return; - } - - for(i=0; i<3; i++) + for (i=0; i<3; i++) child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + /* Resource 0 - IO ports */ res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); @@ -267,6 +261,7 @@ res->end = limit + 0xfff; } + /* Resource 1 - nonprefetchable memory resource */ res = child->resource[1]; pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); @@ -278,6 +273,7 @@ res->end = limit + 0xfffff; } + /* Resource 2 - prefetchable memory resource */ res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);