diff mbox

[RFC,1/3] PCI: do not compare CPU address with PCI address

Message ID 1384843018-9479-2-git-send-email-yan@linux.vnet.ibm.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show

Commit Message

Guo Chao Nov. 19, 2013, 6:36 a.m. UTC
In resource assignment code, limits are exerted to restrict allocated
resource range. However these limits are PCI address but compared to
CPU address in the end. Translated them before comparing.

We can't just use pcibios_bus_to_resource because the limits may not
included in the host bridge window. Introduce a help function to
do this translation, if address missed, return an approximate one.

Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
 drivers/pci/bus.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 2 deletions(-)
diff mbox

Patch

diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index fc1b740..532c0a4 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -99,6 +99,64 @@  void pci_bus_remove_resources(struct pci_bus *bus)
 }
 
 /**
+ * pci_bus_to_resource
+ *
+ * Much like pcibios_bus_to_resource() except it takes a single address
+ * and returns an approximate one if target address is not included
+ * in the bridge window. The approximate address is smaller than required
+ * one is 'bound' is 1, larger than required one if 'bound' is 0.
+ */
+static resource_size_t pci_bus_to_resource(struct pci_bus *bus, int flags,
+				    int mask, resource_size_t addr, int bound)
+{
+	struct pci_host_bridge *bridge;
+	struct pci_host_bridge_window *window, *match = NULL;
+	resource_size_t max = 0, min = -1;
+	resource_size_t offset = -1, start, end;
+
+	while (bus->parent)
+		bus = bus->parent;
+
+	bridge = to_pci_host_bridge(bus->bridge);
+
+	list_for_each_entry(window, &bridge->windows, list) {
+		if ((flags ^ window->res->flags) & mask)
+			continue;
+
+		start = window->res->start - window->offset;
+		end = window->res->end - window->offset;
+
+		if (addr >= start && addr <= end) {
+			offset = window->offset;
+			break;
+		}
+
+		if (bound && addr > end && end > max) {
+			max = end;
+			match = window;
+		} else if (!bound && addr < start && start < min) {
+			min = start;
+			match = window;
+		}
+	}
+
+	if (offset == -1) {
+		/*
+		 * Not even found the matched type. This may happen,
+		 * for example, if try to translate IO address in a HB
+		 * without IO window. Just return the original address,
+		 * it will fail later anyway.
+		 */
+		if (match == NULL)
+			return addr;
+
+		return (bound ? max : min) + match->offset;
+	}
+
+	return addr + offset;
+}
+
+/**
  * pci_bus_alloc_resource - allocate a resource from a parent bus
  * @bus: PCI bus
  * @res: resource to allocate
@@ -129,9 +187,12 @@  pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 
 	type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
 
-	/* don't allocate too high if the pref mem doesn't support 64bit*/
+	/* don't allocate too high if the pref mem doesn't support 64bit */
 	if (!(res->flags & IORESOURCE_MEM_64))
-		max = PCIBIOS_MAX_MEM_32;
+		max = pci_bus_to_resource(bus, res->flags, type_mask,
+							PCIBIOS_MAX_MEM_32, 1);
+
+	min = pci_bus_to_resource(bus, res->flags, type_mask, min, 0);
 
 	pci_bus_for_each_resource(bus, r, i) {
 		if (!r)