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 @@ -244,6 +244,41 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, return 0; } +/* + * 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 @@ -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;