]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PCI: Align head space better
authorIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Tue, 24 Mar 2026 16:56:32 +0000 (18:56 +0200)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 27 Mar 2026 15:19:08 +0000 (10:19 -0500)
When a bridge window contains big and small resource(s), the small
resource(s) may not amount to the half of the size of the big resource
which would allow calculate_head_align() to shrink the head alignment.
This results in always placing the small resource(s) after the big
resource.

In general, it would be good to be able to place the small resource(s)
before the big resource to achieve better utilization of the address space.
In the cases where the large resource can only fit at the end of the
window, it is even required.

However, carrying the information over from pbus_size_mem() and
calculate_head_align() to __pci_assign_resource() and
pcibios_align_resource() is not easy with the current data structures.

A somewhat hacky way to move the non-aligning tail part to the head is
possible within pcibios_align_resource(). The free space between the start
of the free space span and the aligned start address can be compared with
the non-aligning remainder of the size. If the free space is larger than
the remainder, placing the remainder before the start address is possible.
This relocation should generally work, because PCI resources consist only
power-of-2 atoms.

Various arch requirements may still need to override the relocation, so the
relocation is only applied selectively in such cases.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221205
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Xifer <xiferdev@gmail.com>
Link: https://patch.msgid.link/20260324165633.4583-10-ilpo.jarvinen@linux.intel.com
12 files changed:
arch/arm/kernel/bios32.c
arch/m68k/kernel/pcibios.c
arch/mips/pci/pci-generic.c
arch/mips/pci/pci-legacy.c
arch/parisc/kernel/pci.c
arch/powerpc/kernel/pci-common.c
arch/sh/drivers/pci/pci.c
arch/x86/pci/i386.c
arch/xtensa/kernel/pci.c
drivers/pci/setup-res.c
include/linux/pci.h
kernel/resource.c

index cedb83a85dd982f0bf7fa5067be7a2f23bcb8961..ac0e890510daf63525e7460ec67230059066ca70 100644 (file)
@@ -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;
 }
 
index 7e286ee1976b0056505bfe4461dc410cf455ad3f..7a9e60df79c573b0e5369c88638331d98d14a538 100644 (file)
@@ -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;
 }
 
index aaa1d6de8bef698c27f0750820df8f28c1c21885..c2e23d0c1d77289a57fd15e6dbb04c393794ede5 100644 (file)
@@ -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;
 }
 
index 817e97402afe9c2094688bf4fa6afa462baf471f..dae6dafdd6e066ed984aa437c9aab6cd672c102c 100644 (file)
@@ -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;
index f50be1a63c4ce0967ca7b57c8856762effd45f90..b8007c7400d4784054bdeef0b76af5d0bb835fbd 100644 (file)
@@ -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;
 }
index e7bfa15da04354d4613f6415aabf2023e8502beb..8efe95a0c4ffda7aa436b6989de390fb844cac44 100644 (file)
@@ -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;
index 7a0522316ee3b08325f4a317c95f0a31e3d927c9..878a27a1acfb7bb9c6f1802269a02abd8c9267bc 100644 (file)
@@ -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;
index 6fbd4b34c3f7d3b2544d4541f5c5402b86e276c9..e2de26b829408bbe78bfbe9540bf83485ec8d9be 100644 (file)
@@ -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;
index 64ccb7e0d92fbd0ed3c171b4ab6d2296ed44148e..305031551136068b4974cf4df1e51512a32519f6 100644 (file)
@@ -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;
index c375e255c509b14c6ada3db6bf1bf461f64476f9..fbc05cda96ee015d968f9fca984d94541e6a3794 100644 (file)
@@ -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,
index ac332ff9da9f5df7c1a176f437ba320a181c1a32..cedf948dc6143ee6c090324b7f1ca4d758bf00c2 100644 (file)
@@ -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 */
 
index 8c5fcb30fc33f12f9fa722360a1965403fec3de9..d02a53fb95d85d754de55b898863b3ac27be7bf5 100644 (file)
@@ -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;