From d09ee9687e027fc7d2c6b95daf05a8ef3ff06340 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 23 Apr 2009 20:49:25 -0700 Subject: PCI: improve resource allocation under transparent bridges We could run out of space under under 4g, but devices under transparent bridges can use 64bit resources, so keep trying on the parent bus until we hit a non-transparent bridge. Impact: better support for assigning unassigned resources Reviewed-by: Ivan Kokshaysky Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/setup-res.c | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) (limited to 'drivers/pci/setup-res.c') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3039fcb86afc..0b6908b48935 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -135,23 +135,16 @@ void pci_disable_bridge_window(struct pci_dev *dev) } #endif /* CONFIG_PCI_QUIRKS */ -int pci_assign_resource(struct pci_dev *dev, int resno) +static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, + int resno) { - struct pci_bus *bus = dev->bus; struct resource *res = dev->resource + resno; resource_size_t size, min, align; int ret; size = resource_size(res); min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; - align = resource_alignment(res); - if (!align) { - dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " - "alignment) %pR flags %#lx\n", - resno, res, res->flags); - return -EINVAL; - } /* First, try exact prefetching match.. */ ret = pci_bus_alloc_resource(bus, res, size, align, min, @@ -169,10 +162,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) pcibios_align_resource, dev); } - if (ret) { - dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", - resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); - } else { + if (!ret) { res->flags &= ~IORESOURCE_STARTALIGN; if (resno < PCI_BRIDGE_RESOURCES) pci_update_resource(dev, resno); @@ -181,6 +171,39 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return ret; } +int pci_assign_resource(struct pci_dev *dev, int resno) +{ + struct resource *res = dev->resource + resno; + resource_size_t align; + struct pci_bus *bus; + int ret; + + align = resource_alignment(res); + if (!align) { + dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " + "alignment) %pR flags %#lx\n", + resno, res, res->flags); + return -EINVAL; + } + + bus = dev->bus; + while ((ret = __pci_assign_resource(bus, dev, resno))) { + if (bus->parent && bus->self->transparent) + bus = bus->parent; + else + bus = NULL; + if (bus) + continue; + break; + } + + if (ret) + dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", + resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); + + return ret; +} + #if 0 int pci_assign_resource_fixed(struct pci_dev *dev, int resno) { -- cgit v1.2.3 From cebd78a8c5624b5cf04c39c3335a5fc8670a7b69 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 17 Jun 2009 16:33:33 -0400 Subject: Fix pci_claim_resource Instead of starting from the iomem or ioport roots, start from the parent bus' resources. This fixes a bug where child resources would appear above their parents resources if they had the same size. Signed-off-by: Matthew Wilcox Tested-by: Andrew Patterson Signed-off-by: Linus Torvalds --- drivers/pci/setup-res.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/setup-res.c') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3039fcb86afc..12403516776a 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -99,11 +99,11 @@ void pci_update_resource(struct pci_dev *dev, int resno) int pci_claim_resource(struct pci_dev *dev, int resource) { struct resource *res = &dev->resource[resource]; - struct resource *root = NULL; + struct resource *root; char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge"; int err; - root = pcibios_select_root(dev, res); + root = pci_find_parent_resource(dev, res); err = -EINVAL; if (root != NULL) -- cgit v1.2.3 From 79896cf42f6a96d7e14f2dc3473443d68d74031d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 2 Aug 2009 14:04:19 -0700 Subject: Make pci_claim_resource() use request_resource() rather than insert_resource() This function has traditionally used "insert_resource()", because before commit cebd78a8c5 ("Fix pci_claim_resource") it used to just insert the resource into whatever root resource tree that was indicated by "pcibios_select_root()". So there Matthew fixed it to actually look up the proper parent resource, which means that now it's actively wrong to then traverse the resource tree any more: we already know exactly where the new resource should go. And when we then did commit a76117dfd6 ("x86: Use pci_claim_resource"), which changed the x86 PCI code from the open-coded pr = pci_find_parent_resource(dev, r); if (!pr || request_resource(pr, r) < 0) { to using if (pci_claim_resource(dev, idx) < 0) { that "insert_resource()" now suddenly became a problem, and causes a regression covered by http://bugzilla.kernel.org/show_bug.cgi?id=13891 which this fixes. Reported-and-tested-by: Rafael J. Wysocki Cc: Matthew Wilcox Cc: Andrew Patterson Cc: Linux PCI Signed-off-by: Linus Torvalds --- drivers/pci/setup-res.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/setup-res.c') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index b711fb7181e2..1898c7b47907 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -100,16 +100,16 @@ int pci_claim_resource(struct pci_dev *dev, int resource) { struct resource *res = &dev->resource[resource]; struct resource *root; - char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge"; int err; root = pci_find_parent_resource(dev, res); err = -EINVAL; if (root != NULL) - err = insert_resource(root, res); + err = request_resource(root, res); if (err) { + const char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge"; dev_err(&dev->dev, "BAR %d: %s of %s %pR\n", resource, root ? "address space collision on" : -- cgit v1.2.3 From 6faf17f6f1ffc586d16efc2f9fa2083a7785ee74 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 28 Aug 2009 13:00:06 -0700 Subject: PCI SR-IOV: correct broken resource alignment calculations An SR-IOV capable device includes an SR-IOV PCIe capability which describes the Virtual Function (VF) BAR requirements. A typical SR-IOV device can support multiple VFs whose BARs must be in a contiguous region, effectively an array of VF BARs. The BAR reports the size requirement for a single VF. We calculate the full range needed by simply multiplying the VF BAR size with the number of possible VFs and create a resource spanning the full range. This all seems sane enough except it artificially inflates the alignment requirement for the VF BAR. The VF BAR need only be aligned to the size of a single BAR not the contiguous range of VF BARs. This can cause us to fail to allocate resources for the BAR despite the fact that we actually have enough space. This patch adds a thin PCI specific layer over the generic resource_alignment() function which is aware of the special nature of VF BARs and does sorting and allocation based on the smaller alignment requirement. I recognize that while resource_alignment is generic, it's basically a PCI helper. An alternative to this patch is to add PCI VF BAR specific information to struct resource. I opted for the extra layer rather than adding such PCI specific information to struct resource. This does have the slight downside that we don't cache the BAR size and re-read for each alignment query (happens a small handful of times during boot for each VF BAR). Signed-off-by: Chris Wright Cc: Ivan Kokshaysky Cc: Linus Torvalds Cc: Matthew Wilcox Cc: Yu Zhao Cc: stable@kernel.org Signed-off-by: Jesse Barnes --- drivers/pci/setup-res.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/pci/setup-res.c') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 1898c7b47907..88cdd1a937d6 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -144,7 +144,7 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, size = resource_size(res); min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; - align = resource_alignment(res); + align = pci_resource_alignment(dev, res); /* First, try exact prefetching match.. */ ret = pci_bus_alloc_resource(bus, res, size, align, min, @@ -178,7 +178,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) struct pci_bus *bus; int ret; - align = resource_alignment(res); + align = pci_resource_alignment(dev, res); if (!align) { dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " "alignment) %pR flags %#lx\n", @@ -259,7 +259,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) if (!(r->flags) || r->parent) continue; - r_align = resource_alignment(r); + r_align = pci_resource_alignment(dev, r); if (!r_align) { dev_warn(&dev->dev, "BAR %d: bogus alignment " "%pR flags %#lx\n", @@ -271,7 +271,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) struct resource_list *ln = list->next; if (ln) - align = resource_alignment(ln->res); + align = pci_resource_alignment(ln->dev, ln->res); if (r_align > align) { tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); -- cgit v1.2.3