summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/bios32.c3
-rw-r--r--arch/m68k/kernel/pcibios.c4
-rw-r--r--arch/mips/pci/pci-generic.c3
-rw-r--r--arch/mips/pci/pci-legacy.c2
-rw-r--r--arch/parisc/kernel/pci.c3
-rw-r--r--arch/powerpc/kernel/pci-common.c2
-rw-r--r--arch/sh/drivers/pci/pci.c2
-rw-r--r--arch/x86/pci/i386.c2
-rw-r--r--arch/xtensa/kernel/pci.c2
-rw-r--r--drivers/pci/setup-res.c39
-rw-r--r--include/linux/pci.h5
-rw-r--r--kernel/resource.c2
12 files changed, 67 insertions, 2 deletions
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index cedb83a85dd9..ac0e890510da 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -577,6 +577,9 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
return host_bridge->align_resource(dev, res,
start, size, align);
+ if (res->flags & IORESOURCE_MEM)
+ return pci_align_resource(dev, res, empty_res, size, align);
+
return start;
}
diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c
index 7e286ee1976b..7a9e60df79c5 100644
--- a/arch/m68k/kernel/pcibios.c
+++ b/arch/m68k/kernel/pcibios.c
@@ -31,11 +31,15 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size,
resource_size_t align)
{
+ struct pci_dev *dev = data;
resource_size_t start = res->start;
if ((res->flags & IORESOURCE_IO) && (start & 0x300))
start = (start + 0x3ff) & ~0x3ff;
+ if (res->flags & IORESOURCE_MEM)
+ return pci_align_resource(dev, res, empty_res, size, align);
+
return start;
}
diff --git a/arch/mips/pci/pci-generic.c b/arch/mips/pci/pci-generic.c
index aaa1d6de8bef..c2e23d0c1d77 100644
--- a/arch/mips/pci/pci-generic.c
+++ b/arch/mips/pci/pci-generic.c
@@ -38,6 +38,9 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
return host_bridge->align_resource(dev, res,
start, size, align);
+ if (res->flags & IORESOURCE_MEM)
+ return pci_align_resource(dev, res, empty_res, size, align);
+
return start;
}
diff --git a/arch/mips/pci/pci-legacy.c b/arch/mips/pci/pci-legacy.c
index 817e97402afe..dae6dafdd6e0 100644
--- a/arch/mips/pci/pci-legacy.c
+++ b/arch/mips/pci/pci-legacy.c
@@ -70,6 +70,8 @@ pcibios_align_resource(void *data, const struct resource *res,
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
} else if (res->flags & IORESOURCE_MEM) {
+ start = pci_align_resource(dev, res, empty_res, size, align);
+
/* Make sure we start at our min on all hoses */
if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start)
start = PCIBIOS_MIN_MEM + hose->mem_resource->start;
diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c
index f50be1a63c4c..b8007c7400d4 100644
--- a/arch/parisc/kernel/pci.c
+++ b/arch/parisc/kernel/pci.c
@@ -201,6 +201,7 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size,
resource_size_t alignment)
{
+ struct pci_dev *dev = data;
resource_size_t align, start = res->start;
DBG_RES("pcibios_align_resource(%s, (%p) [%lx,%lx]/%x, 0x%lx, 0x%lx)\n",
@@ -212,6 +213,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
align = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
if (align > alignment)
start = ALIGN(start, align);
+ else
+ start = pci_align_resource(dev, res, empty_res, size, alignment);
return start;
}
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index e7bfa15da043..8efe95a0c4ff 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -1144,6 +1144,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
return start;
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
+ } else if (res->flags & IORESOURCE_MEM) {
+ start = pci_align_resource(dev, res, empty_res, size, align);
}
return start;
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
index 7a0522316ee3..878a27a1acfb 100644
--- a/arch/sh/drivers/pci/pci.c
+++ b/arch/sh/drivers/pci/pci.c
@@ -185,6 +185,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
*/
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
+ } else if (res->flags & IORESOURCE_MEM) {
+ start = pci_align_resource(dev, res, empty_res, size, align);
}
return start;
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c
index 6fbd4b34c3f7..e2de26b82940 100644
--- a/arch/x86/pci/i386.c
+++ b/arch/x86/pci/i386.c
@@ -165,6 +165,8 @@ pcibios_align_resource(void *data, const struct resource *res,
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
} else if (res->flags & IORESOURCE_MEM) {
+ start = pci_align_resource(dev, res, empty_res, size, align);
+
/* The low 1MB range is reserved for ISA cards */
if (start < BIOS_END)
start = BIOS_END;
diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c
index 64ccb7e0d92f..305031551136 100644
--- a/arch/xtensa/kernel/pci.c
+++ b/arch/xtensa/kernel/pci.c
@@ -54,6 +54,8 @@ pcibios_align_resource(void *data, const struct resource *res,
if (start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
+ } else if (res->flags & IORESOURCE_MEM) {
+ start = pci_align_resource(dev, res, empty_res, size, align);
}
return start;
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index c375e255c509..fbc05cda96ee 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -245,6 +245,41 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
}
/*
+ * For mem bridge windows, try to relocate tail remainder space to space
+ * before res->start if there's enough free space there. This enables
+ * tighter packing for resources.
+ */
+resource_size_t pci_align_resource(struct pci_dev *dev,
+ const struct resource *res,
+ const struct resource *empty_res,
+ resource_size_t size,
+ resource_size_t align)
+{
+ resource_size_t remainder, start_addr;
+
+ if (!(res->flags & IORESOURCE_MEM))
+ return res->start;
+
+ if (IS_ALIGNED(size, align))
+ return res->start;
+
+ remainder = size - ALIGN_DOWN(size, align);
+ /* Don't mess with size that doesn't align with window size granularity */
+ if (!IS_ALIGNED(remainder, pci_min_window_alignment(dev->bus, res->flags)))
+ return res->start;
+ /* Try to place remainder that doesn't fill align before */
+ if (res->start < remainder)
+ return res->start;
+ start_addr = res->start - remainder;
+ if (empty_res->start > start_addr)
+ return res->start;
+
+ pci_dbg(dev, "%pR: moving candidate start address below align to %llx\n",
+ res, (unsigned long long)start_addr);
+ return start_addr;
+}
+
+/*
* We don't have to worry about legacy ISA devices, so nothing to do here.
* This is marked as __weak because multiple architectures define it; it should
* eventually go away.
@@ -255,7 +290,9 @@ resource_size_t __weak pcibios_align_resource(void *data,
resource_size_t size,
resource_size_t align)
{
- return res->start;
+ struct pci_dev *dev = data;
+
+ return pci_align_resource(dev, res, empty_res, size, align);
}
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
diff --git a/include/linux/pci.h b/include/linux/pci.h
index ac332ff9da9f..cedf948dc614 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1210,6 +1210,11 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
const struct resource *empty_res,
resource_size_t size,
resource_size_t align);
+resource_size_t pci_align_resource(struct pci_dev *dev,
+ const struct resource *res,
+ const struct resource *empty_res,
+ resource_size_t size,
+ resource_size_t align);
/* Generic PCI functions used internally */
diff --git a/kernel/resource.c b/kernel/resource.c
index 8c5fcb30fc33..d02a53fb95d8 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -766,7 +766,7 @@ static int __find_resource_space(struct resource *root, struct resource *old,
}
alloc.end = alloc.start + size - 1;
if (alloc.start <= alloc.end &&
- __resource_contains_unbound(&avail, &alloc)) {
+ __resource_contains_unbound(&full_avail, &alloc)) {
new->start = alloc.start;
new->end = alloc.end;
return 0;