[PATCH] PCI: fix pci_bus_alloc_resource() hang

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When a PCI bus has two resources with the same start/end, e.g.,

    pci_bus 0000:04: resource 2 [mem 0xd0000000-0xd7ffffff pref]
    pci_bus 0000:04: resource 7 [mem 0xd0000000-0xd7ffffff]

the previous pci_bus_find_resource_prev() implementation would alternate
between them forever:

    pci_bus_find_resource_prev(... [mem 0xd0000000-0xd7ffffff pref])
	returns [mem 0xd0000000-0xd7ffffff]
    pci_bus_find_resource_prev(... [mem 0xd0000000-0xd7ffffff])
	returns [mem 0xd0000000-0xd7ffffff pref]
    pci_bus_find_resource_prev(... [mem 0xd0000000-0xd7ffffff pref])
	returns [mem 0xd0000000-0xd7ffffff]
    ...

This happened because there was no ordering between two resources with the
same start and end.  A resource that had the same start and end as the
cursor, but was not itself the cursor, was considered to be before the
cursor.

One might question whether it even makes sense to have two resources
with the same start/end but different flags, but this patch makes
pci_bus_find_resource_prev() more robust by reworking the comparison
so there is always a fixed ordering between any two resources.

Reference: https://bugzilla.kernel.org/show_bug.cgi?id=22062
Reported-and-tested-by: Borislav Petkov <bp@xxxxxxxxx>
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@xxxxxx>
---

 drivers/pci/bus.c |   42 +++++++++++++++++++++++-------------------
 1 files changed, 23 insertions(+), 19 deletions(-)


diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 5624db8..794c890 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -64,6 +64,24 @@ void pci_bus_remove_resources(struct pci_bus *bus)
 	}
 }
 
+static int resource_compare(struct resource *res1, struct resource *res2)
+{
+	if (res1->end < res2->end)
+		return -1;
+
+	if (res1->end > res2->end)
+		return 1;
+
+	if (res1->start < res2->start)
+		return -1;
+
+	if (res1->start > res2->start)
+		return 1;
+
+	/* If start/end are identical, order them to avoid loops */
+	return res1 - res2;
+}
+
 /*
  * Find the highest-address bus resource below the cursor "res".  If the
  * cursor is NULL, return the highest resource.
@@ -82,26 +100,12 @@ static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus,
 		if ((r->flags & IORESOURCE_TYPE_BITS) != type)
 			continue;
 
-		/* If this resource is at or past the cursor, skip it */
-		if (res) {
-			if (r == res)
-				continue;
-			if (r->end > res->end)
-				continue;
-			if (r->end == res->end && r->start > res->start)
-				continue;
+		/* Only look at resources before the cursor */
+		if (!res || resource_compare(r, res) < 0) {
+			/* Keep the highest one we find */
+			if (!prev || resource_compare(r, prev) > 0)
+				prev = r;
 		}
-
-		if (!prev)
-			prev = r;
-
-		/*
-		 * A small resource is higher than a large one that ends at
-		 * the same address.
-		 */
-		if (r->end > prev->end ||
-		    (r->end == prev->end && r->start > prev->start))
-			prev = r;
 	}
 
 	return prev;

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux