[PATCH] PCI: update bridge resources to get bigger ranges

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

 



From: Yinghai Lu <yinghai@xxxxxxxxxx>

    BIOS separates IO ranges between several IOHs. On some slots, BIOS assigns
    resources to a bridge but stops assigning resources to the device under
    that bridge, when the device needs a bigger resource.

    The patch reassigns bridge resources to accomadate devices with larger
    resource needs.
    1. allocate resources and record the failed device resources
    2. clear the BIOS assigned resources of the parent bridge of failing device
    3. go back and reassign resources to all the resouces under the bridge.
    4. if allocation still fails go up the tree, clear more bridges and
           try again


    Background:
    Yinghai Lu originally wrote this patch, which was commited to Linus's
    git tree. The commit id is 977d17bb1749517b353874ccdc9b85abc7a58c2a.
    Unfortunately it got reverted for causing a regression, when
    in fact it had exposed a bug.  The bug is now fixed paving the path
    for reconsideration of this patch.

    Signed-off-by: Yinghai Lu <yinghai@xxxxxxxxxx>
    Signed-off-by: Jesse Barnes <jbarnes@xxxxxxxxxxxxxxxx>
    Signed-off-by: Ram Pai <linuxram@xxxxxxxxxx>
    Cc: Bjorn Helgaas <bjorn.helgaas@xxxxxx>
    Cc: Jesse Barnes <jbarnes@xxxxxxxxxxxxxxxx>
    Cc: clemens@xxxxxxxxxx
---
 drivers/pci/setup-bus.c |  115 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 113 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 89d0a6a..f3a0644 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -991,12 +991,66 @@ static void pci_bus_dump_resources(struct pci_bus *bus)
 	}
 }
 
+static int __init pci_bus_get_depth(struct pci_bus *bus)
+{
+	int depth = 0;
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		int ret;
+		struct pci_bus *b = dev->subordinate;
+		if (!b)
+			continue;
+
+		ret = pci_bus_get_depth(b);
+		if (ret + 1 > depth)
+			depth = ret + 1;
+	}
+
+	return depth;
+}
+static int __init pci_get_max_depth(void)
+{
+	int depth = 0;
+	struct pci_bus *bus;
+
+	list_for_each_entry(bus, &pci_root_buses, node) {
+		int ret;
+
+		ret = pci_bus_get_depth(bus);
+		if (ret > depth)
+			depth = ret;
+	}
+
+	return depth;
+}
+
+/*
+ * first try will not touch pci bridge res
+ * second  and later try will clear small leaf bridge res
+ * will stop till to the max  deepth if can not find good one
+ */
 void __init
 pci_assign_unassigned_resources(void)
 {
 	struct pci_bus *bus;
 	struct resource_list_x add_list; /* list of resources that
 					want additional resources */
+	int tried_times = 0;
+	enum release_type rel_type = leaf_only;
+	struct resource_list_x head, *list;
+	unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
+				  IORESOURCE_PREFETCH;
+	unsigned long failed_type;
+	int max_depth = pci_get_max_depth();
+	int pci_try_num;
+
+	head.next = NULL;
+	pci_try_num = max_depth + 1;
+	printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n",
+		 max_depth, pci_try_num);
+
+again:
 	add_list.next = NULL;
 	/* Depth first, calculate sizes and alignments of all
 	   subordinate buses. */
@@ -1006,10 +1060,67 @@ pci_assign_unassigned_resources(void)
 
 	/* Depth last, allocate resources and update the hardware. */
 	list_for_each_entry(bus, &pci_root_buses, node) {
-		__pci_bus_assign_resources(bus, &add_list, NULL);
-		pci_enable_bridges(bus);
+		__pci_bus_assign_resources(bus, &add_list, &head);
 	}
 	BUG_ON(add_list.next);
+	tried_times++;
+
+	/* any device complain? */
+	if (!head.next)
+		goto enable_and_dump;
+	failed_type = 0;
+	for (list = head.next; list;) {
+		failed_type |= list->flags;
+		list = list->next;
+	}
+	/*
+	 * io port are tight, don't try extra
+	 * or if reach the limit, don't want to try more
+	 */
+	failed_type &= type_mask;
+	if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) {
+		free_list(resource_list_x, &head);
+		goto enable_and_dump;
+	}
+
+	printk(KERN_DEBUG "PCI: No. %d try to assign unassigned res\n",
+			 tried_times + 1);
+
+	/* third times and later will not check if it is leaf */
+	if ((tried_times + 1) > 2)
+		rel_type = whole_subtree;
+
+	/*
+	 * Try to release leaf bridge's resources that doesn't fit resource of
+	 * child device under that bridge
+	 */
+	for (list = head.next; list;) {
+		bus = list->dev->bus;
+		pci_bus_release_bridge_resources(bus, list->flags & type_mask,
+						  rel_type);
+		list = list->next;
+	}
+	/* restore size and flags */
+	for (list = head.next; list;) {
+		struct resource *res = list->res;
+
+		res->start = list->start;
+		res->end = list->end;
+		res->flags = list->flags;
+		if (list->dev->subordinate)
+			res->flags = 0;
+
+		list = list->next;
+	}
+	free_list(resource_list_x, &head);
+
+	goto again;
+
+enable_and_dump:
+	/* Depth last, update the hardware. */
+	list_for_each_entry(bus, &pci_root_buses, node) {
+		pci_enable_bridges(bus);
+	}
 
 	/* dump the resource on buses */
 	list_for_each_entry(bus, &pci_root_buses, node) {
-- 
1.7.1

--
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