PCI: Remove pci_find_parent_resource() use for allocation
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 26 Feb 2014 18:25:58 +0000 (11:25 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 27 Feb 2014 17:43:32 +0000 (10:43 -0700)
If the resource hasn't been allocated yet, pci_find_parent_resource() is
documented as returning the region "where it should be allocated from."
This is impossible in general because there may be several candidates: a
prefetchable BAR can be put in either a prefetchable or non-prefetchable
window, a transparent bridge may have overlapping positively- and
subtractively-decoded windows, and a root bus may have several windows of
the same type.

Allocation should be done by pci_bus_alloc_resource(), which iterates
through all bus resources and looks for the best match, e.g., one with the
desired prefetchability attributes, and falls back to less-desired
possibilities.

The only valid use of pci_find_parent_resource() is to find the parent of
an already-allocated resource so we can claim it via request_resource(),
and all we need for that is a bus region of the correct type that contains
the resource.

Note that like 8c8def26bfaa ("PCI: allow matching of prefetchable resources
to non-prefetchable windows"), this depends on pci_bus_for_each_resource()
iterating through positively-decoded regions before subtractively-decoded
ones.  We prefer not to return a subtractively-decoded region because
requesting from it will likely conflict with the overlapping positively-
decoded window (see Launchpad report below).

Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/424142
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: Linus Torvalds <torvalds@linux-foundation.org>
drivers/pci/pci.c

index 1febe90..99293fa 100644 (file)
@@ -401,33 +401,40 @@ EXPORT_SYMBOL_GPL(pci_find_ht_capability);
  * @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.
+ *  region of parent bus the given region is contained in.
  */
 struct resource *
 pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
 {
        const struct pci_bus *bus = dev->bus;
+       struct resource *r;
        int i;
-       struct resource *best = NULL, *r;
 
        pci_bus_for_each_resource(bus, r, i) {
                if (!r)
                        continue;
-               if (res->start && !(res->start >= r->start && res->end <= r->end))
-                       continue;       /* Not contained */
-               if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))
-                       continue;       /* Wrong type */
-               if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH))
-                       return r;       /* Exact match */
-               /* We can't insert a non-prefetch resource inside a prefetchable parent .. */
-               if (r->flags & IORESOURCE_PREFETCH)
-                       continue;
-               /* .. but we can put a prefetchable resource inside a non-prefetchable one */
-               if (!best)
-                       best = r;
+               if (res->start && resource_contains(r, res)) {
+
+                       /*
+                        * If the window is prefetchable but the BAR is
+                        * not, the allocator made a mistake.
+                        */
+                       if (r->flags & IORESOURCE_PREFETCH &&
+                           !(res->flags & IORESOURCE_PREFETCH))
+                               return NULL;
+
+                       /*
+                        * If we're below a transparent bridge, there may
+                        * be both a positively-decoded aperture and a
+                        * subtractively-decoded region that contain the BAR.
+                        * We want the positively-decoded one, so this depends
+                        * on pci_bus_for_each_resource() giving us those
+                        * first.
+                        */
+                       return r;
+               }
        }
-       return best;
+       return NULL;
 }
 
 /**