--- /dev/null
+From 2bbc6942273b5b3097bd265d82227bdd84b351b2 Mon Sep 17 00:00:00 2001
+From: Ram Pai <linuxram@us.ibm.com>
+Date: Mon, 25 Jul 2011 13:08:39 -0700
+Subject: PCI : ability to relocate assigned pci-resources
+
+From: Ram Pai <linuxram@us.ibm.com>
+
+commit 2bbc6942273b5b3097bd265d82227bdd84b351b2 upstream.
+
+Currently pci-bridges are allocated enough resources to satisfy their immediate
+requirements. Any additional resource-requests fail if additional free space,
+contiguous to the one already allocated, is not available. This behavior is not
+reasonable since sufficient contiguous resources, that can satisfy the request,
+are available at a different location.
+
+This patch provides the ability to expand and relocate a allocated resource.
+
+ v2: Changelog: Fixed size calculation in pci_reassign_resource()
+ v3: Changelog : Split this patch. The resource.c changes are already
+ upstream. All the pci driver changes are in here.
+
+Signed-off-by: Ram Pai <linuxram@us.ibm.com>
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+Cc: Andrew Worsley <amworsley@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/pci/setup-bus.c | 27 +++++---
+ drivers/pci/setup-res.c | 150 ++++++++++++++++++++++++++++++------------------
+ include/linux/pci.h | 1
+ 3 files changed, 112 insertions(+), 66 deletions(-)
+
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -34,6 +34,7 @@ struct resource_list_x {
+ resource_size_t start;
+ resource_size_t end;
+ resource_size_t add_size;
++ resource_size_t min_align;
+ unsigned long flags;
+ };
+
+@@ -65,7 +66,7 @@ void pci_realloc(void)
+ */
+ static void add_to_list(struct resource_list_x *head,
+ struct pci_dev *dev, struct resource *res,
+- resource_size_t add_size)
++ resource_size_t add_size, resource_size_t min_align)
+ {
+ struct resource_list_x *list = head;
+ struct resource_list_x *ln = list->next;
+@@ -84,13 +85,16 @@ static void add_to_list(struct resource_
+ tmp->end = res->end;
+ tmp->flags = res->flags;
+ tmp->add_size = add_size;
++ tmp->min_align = min_align;
+ list->next = tmp;
+ }
+
+ static void add_to_failed_list(struct resource_list_x *head,
+ struct pci_dev *dev, struct resource *res)
+ {
+- add_to_list(head, dev, res, 0);
++ add_to_list(head, dev, res,
++ 0 /* dont care */,
++ 0 /* dont care */);
+ }
+
+ static void __dev_sort_resources(struct pci_dev *dev,
+@@ -159,13 +163,16 @@ static void adjust_resources_sorted(stru
+
+ idx = res - &list->dev->resource[0];
+ add_size=list->add_size;
+- if (!resource_size(res) && add_size) {
+- res->end = res->start + add_size - 1;
+- if(pci_assign_resource(list->dev, idx))
++ if (!resource_size(res)) {
++ res->end = res->start + add_size - 1;
++ if(pci_assign_resource(list->dev, idx))
+ reset_resource(res);
+- } else if (add_size) {
+- adjust_resource(res, res->start,
+- resource_size(res) + add_size);
++ } else {
++ resource_size_t align = list->min_align;
++ res->flags |= list->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
++ if (pci_reassign_resource(list->dev, idx, add_size, align))
++ dev_printk(KERN_DEBUG, &list->dev->dev, "failed to add optional resources res=%pR\n",
++ res);
+ }
+ out:
+ tmp = list;
+@@ -622,7 +629,7 @@ static void pbus_size_io(struct pci_bus
+ b_res->end = b_res->start + size0 - 1;
+ b_res->flags |= IORESOURCE_STARTALIGN;
+ if (size1 > size0 && add_head)
+- add_to_list(add_head, bus->self, b_res, size1-size0);
++ add_to_list(add_head, bus->self, b_res, size1-size0, 4096);
+ }
+
+ /**
+@@ -725,7 +732,7 @@ static int pbus_size_mem(struct pci_bus
+ b_res->end = size0 + min_align - 1;
+ b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask;
+ if (size1 > size0 && add_head)
+- add_to_list(add_head, bus->self, b_res, size1-size0);
++ add_to_list(add_head, bus->self, b_res, size1-size0, min_align);
+ return 1;
+ }
+
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -129,16 +129,16 @@ void pci_disable_bridge_window(struct pc
+ }
+ #endif /* CONFIG_PCI_QUIRKS */
+
++
++
+ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
+- int resno)
++ int resno, resource_size_t size, resource_size_t align)
+ {
+ struct resource *res = dev->resource + resno;
+- resource_size_t size, min, align;
++ resource_size_t min;
+ int ret;
+
+- size = resource_size(res);
+ min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
+- align = pci_resource_alignment(dev, res);
+
+ /* First, try exact prefetching match.. */
+ ret = pci_bus_alloc_resource(bus, res, size, align, min,
+@@ -155,56 +155,101 @@ static int __pci_assign_resource(struct
+ ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
+ pcibios_align_resource, dev);
+ }
++ return ret;
++}
+
+- if (ret < 0 && dev->fw_addr[resno]) {
+- struct resource *root, *conflict;
+- resource_size_t start, end;
++static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
++ int resno, resource_size_t size)
++{
++ struct resource *root, *conflict;
++ resource_size_t start, end;
++ int ret = 0;
++
++ if (res->flags & IORESOURCE_IO)
++ root = &ioport_resource;
++ else
++ root = &iomem_resource;
++
++ start = res->start;
++ end = res->end;
++ res->start = dev->fw_addr[resno];
++ res->end = res->start + size - 1;
++ dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
++ resno, res);
++ conflict = request_resource_conflict(root, res);
++ if (conflict) {
++ dev_info(&dev->dev,
++ "BAR %d: %pR conflicts with %s %pR\n", resno,
++ res, conflict->name, conflict);
++ res->start = start;
++ res->end = end;
++ ret = 1;
++ }
++ return ret;
++}
+
+- /*
+- * If we failed to assign anything, let's try the address
+- * where firmware left it. That at least has a chance of
+- * working, which is better than just leaving it disabled.
+- */
++static int _pci_assign_resource(struct pci_dev *dev, int resno, int size, resource_size_t min_align)
++{
++ struct resource *res = dev->resource + resno;
++ struct pci_bus *bus;
++ int ret;
++ char *type;
+
+- if (res->flags & IORESOURCE_IO)
+- root = &ioport_resource;
++ bus = dev->bus;
++ while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) {
++ if (!bus->parent || !bus->self->transparent)
++ break;
++ bus = bus->parent;
++ }
++
++ if (ret) {
++ if (res->flags & IORESOURCE_MEM)
++ if (res->flags & IORESOURCE_PREFETCH)
++ type = "mem pref";
++ else
++ type = "mem";
++ else if (res->flags & IORESOURCE_IO)
++ type = "io";
+ else
+- root = &iomem_resource;
++ type = "unknown";
++ dev_info(&dev->dev,
++ "BAR %d: can't assign %s (size %#llx)\n",
++ resno, type, (unsigned long long) resource_size(res));
++ }
+
+- start = res->start;
+- end = res->end;
+- res->start = dev->fw_addr[resno];
+- res->end = res->start + size - 1;
+- dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
+- resno, res);
+- conflict = request_resource_conflict(root, res);
+- if (conflict) {
+- dev_info(&dev->dev,
+- "BAR %d: %pR conflicts with %s %pR\n", resno,
+- res, conflict->name, conflict);
+- res->start = start;
+- res->end = end;
+- } else
+- ret = 0;
++ return ret;
++}
++
++int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize,
++ resource_size_t min_align)
++{
++ struct resource *res = dev->resource + resno;
++ resource_size_t new_size;
++ int ret;
++
++ if (!res->parent) {
++ dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resouce %pR "
++ "\n", resno, res);
++ return -EINVAL;
+ }
+
++ new_size = resource_size(res) + addsize + min_align;
++ ret = _pci_assign_resource(dev, resno, new_size, min_align);
+ if (!ret) {
+ res->flags &= ~IORESOURCE_STARTALIGN;
+ dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
+ if (resno < PCI_BRIDGE_RESOURCES)
+ pci_update_resource(dev, resno);
+ }
+-
+ return ret;
+ }
+
+ int pci_assign_resource(struct pci_dev *dev, int resno)
+ {
+ struct resource *res = dev->resource + resno;
+- resource_size_t align;
++ resource_size_t align, size;
+ struct pci_bus *bus;
+ int ret;
+- char *type;
+
+ align = pci_resource_alignment(dev, res);
+ if (!align) {
+@@ -214,34 +259,27 @@ int pci_assign_resource(struct pci_dev *
+ }
+
+ 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;
+- }
++ size = resource_size(res);
++ ret = _pci_assign_resource(dev, resno, size, align);
+
+- if (ret) {
+- if (res->flags & IORESOURCE_MEM)
+- if (res->flags & IORESOURCE_PREFETCH)
+- type = "mem pref";
+- else
+- type = "mem";
+- else if (res->flags & IORESOURCE_IO)
+- type = "io";
+- else
+- type = "unknown";
+- dev_info(&dev->dev,
+- "BAR %d: can't assign %s (size %#llx)\n",
+- resno, type, (unsigned long long) resource_size(res));
+- }
++ /*
++ * If we failed to assign anything, let's try the address
++ * where firmware left it. That at least has a chance of
++ * working, which is better than just leaving it disabled.
++ */
++ if (ret < 0 && dev->fw_addr[resno])
++ ret = pci_revert_fw_address(res, dev, resno, size);
+
++ if (!ret) {
++ res->flags &= ~IORESOURCE_STARTALIGN;
++ dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
++ if (resno < PCI_BRIDGE_RESOURCES)
++ pci_update_resource(dev, resno);
++ }
+ return ret;
+ }
+
++
+ /* Sort resources by alignment */
+ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
+ {
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -800,6 +800,7 @@ int __pci_reset_function(struct pci_dev
+ int pci_reset_function(struct pci_dev *dev);
+ void pci_update_resource(struct pci_dev *dev, int resno);
+ int __must_check pci_assign_resource(struct pci_dev *dev, int i);
++int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
+ int pci_select_bars(struct pci_dev *dev, unsigned long flags);
+
+ /* ROM control related routines */
--- /dev/null
+From a4ac9fea016fc5c09227eb479bd35e34978323a4 Mon Sep 17 00:00:00 2001
+From: Yinghai Lu <yinghai@kernel.org>
+Date: Sat, 21 Jan 2012 02:08:17 -0800
+Subject: PCI : Calculate right add_size
+
+From: Yinghai Lu <yinghai@kernel.org>
+
+commit a4ac9fea016fc5c09227eb479bd35e34978323a4 upstream.
+
+During debug of one SRIOV enabled hotplug device, we found found that
+add_size is not passed properly.
+
+The device has devices under two level bridges:
+
+ +-[0000:80]-+-00.0-[81-8f]--
+ | +-01.0-[90-9f]--
+ | +-02.0-[a0-af]----00.0-[a1-a3]--+-02.0-[a2]--+-00.0 Oracle Corporation Device
+ | | \-03.0-[a3]--+-00.0 Oracle Corporation Device
+
+Which means later the parent bridge will not try to add a big enough range:
+
+[ 557.455077] pci 0000:a0:00.0: BAR 14: assigned [mem 0xf9000000-0xf93fffff]
+[ 557.461974] pci 0000:a0:00.0: BAR 15: assigned [mem 0xf6000000-0xf61fffff pref]
+[ 557.469340] pci 0000:a1:02.0: BAR 14: assigned [mem 0xf9000000-0xf91fffff]
+[ 557.476231] pci 0000:a1:02.0: BAR 15: assigned [mem 0xf6000000-0xf60fffff pref]
+[ 557.483582] pci 0000:a1:03.0: BAR 14: assigned [mem 0xf9200000-0xf93fffff]
+[ 557.490468] pci 0000:a1:03.0: BAR 15: assigned [mem 0xf6100000-0xf61fffff pref]
+[ 557.497833] pci 0000:a1:03.0: BAR 14: can't assign mem (size 0x200000)
+[ 557.504378] pci 0000:a1:03.0: failed to add optional resources res=[mem 0xf9200000-0xf93fffff]
+[ 557.513026] pci 0000:a1:02.0: BAR 14: can't assign mem (size 0x200000)
+[ 557.519578] pci 0000:a1:02.0: failed to add optional resources res=[mem 0xf9000000-0xf91fffff]
+
+It turns out we did not calculate size1 properly.
+
+static resource_size_t calculate_memsize(resource_size_t size,
+ resource_size_t min_size,
+ resource_size_t size1,
+ resource_size_t old_size,
+ resource_size_t align)
+{
+ if (size < min_size)
+ size = min_size;
+ if (old_size == 1 )
+ old_size = 0;
+ if (size < old_size)
+ size = old_size;
+ size = ALIGN(size + size1, align);
+ return size;
+}
+
+We should not pass add_size with min_size in calculate_memsize since
+that will make add_size not contribute final add_size.
+
+So just pass add_size with size1 to calculate_memsize().
+
+With this change, we should have chance to remove extra addon in
+pci_reassign_resource.
+
+Signed-off-by: Yinghai Lu <yinghai@kernel.org>
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+Cc: Andrew Worsley <amworsley@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/pci/setup-bus.c | 4 ++--
+ drivers/pci/setup-res.c | 5 +++--
+ 2 files changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -614,7 +614,7 @@ static void pbus_size_io(struct pci_bus
+ if (children_add_size > add_size)
+ add_size = children_add_size;
+ size1 = (!add_head || (add_head && !add_size)) ? size0 :
+- calculate_iosize(size, min_size+add_size, size1,
++ calculate_iosize(size, min_size, add_size + size1,
+ resource_size(b_res), 4096);
+ if (!size0 && !size1) {
+ if (b_res->start || b_res->end)
+@@ -718,7 +718,7 @@ static int pbus_size_mem(struct pci_bus
+ if (children_add_size > add_size)
+ add_size = children_add_size;
+ size1 = (!add_head || (add_head && !add_size)) ? size0 :
+- calculate_memsize(size, min_size+add_size, 0,
++ calculate_memsize(size, min_size, add_size,
+ resource_size(b_res), min_align);
+ if (!size0 && !size1) {
+ if (b_res->start || b_res->end)
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -233,11 +233,12 @@ int pci_reassign_resource(struct pci_dev
+ return -EINVAL;
+ }
+
+- new_size = resource_size(res) + addsize + min_align;
++ /* already aligned with min_align */
++ new_size = resource_size(res) + addsize;
+ ret = _pci_assign_resource(dev, resno, new_size, min_align);
+ if (!ret) {
+ res->flags &= ~IORESOURCE_STARTALIGN;
+- dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
++ dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res);
+ if (resno < PCI_BRIDGE_RESOURCES)
+ pci_update_resource(dev, resno);
+ }