Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PCI: Align head space better

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

authored by

Ilpo Järvinen and committed by
Bjorn Helgaas
9036bd0e 8bbe8cec

+67 -2
+3
arch/arm/kernel/bios32.c
··· 577 577 return host_bridge->align_resource(dev, res, 578 578 start, size, align); 579 579 580 + if (res->flags & IORESOURCE_MEM) 581 + return pci_align_resource(dev, res, empty_res, size, align); 582 + 580 583 return start; 581 584 } 582 585
+4
arch/m68k/kernel/pcibios.c
··· 31 31 resource_size_t size, 32 32 resource_size_t align) 33 33 { 34 + struct pci_dev *dev = data; 34 35 resource_size_t start = res->start; 35 36 36 37 if ((res->flags & IORESOURCE_IO) && (start & 0x300)) 37 38 start = (start + 0x3ff) & ~0x3ff; 39 + 40 + if (res->flags & IORESOURCE_MEM) 41 + return pci_align_resource(dev, res, empty_res, size, align); 38 42 39 43 return start; 40 44 }
+3
arch/mips/pci/pci-generic.c
··· 38 38 return host_bridge->align_resource(dev, res, 39 39 start, size, align); 40 40 41 + if (res->flags & IORESOURCE_MEM) 42 + return pci_align_resource(dev, res, empty_res, size, align); 43 + 41 44 return start; 42 45 } 43 46
+2
arch/mips/pci/pci-legacy.c
··· 70 70 if (start & 0x300) 71 71 start = (start + 0x3ff) & ~0x3ff; 72 72 } else if (res->flags & IORESOURCE_MEM) { 73 + start = pci_align_resource(dev, res, empty_res, size, align); 74 + 73 75 /* Make sure we start at our min on all hoses */ 74 76 if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start) 75 77 start = PCIBIOS_MIN_MEM + hose->mem_resource->start;
+3
arch/parisc/kernel/pci.c
··· 201 201 resource_size_t size, 202 202 resource_size_t alignment) 203 203 { 204 + struct pci_dev *dev = data; 204 205 resource_size_t align, start = res->start; 205 206 206 207 DBG_RES("pcibios_align_resource(%s, (%p) [%lx,%lx]/%x, 0x%lx, 0x%lx)\n", ··· 213 212 align = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; 214 213 if (align > alignment) 215 214 start = ALIGN(start, align); 215 + else 216 + start = pci_align_resource(dev, res, empty_res, size, alignment); 216 217 217 218 return start; 218 219 }
+2
arch/powerpc/kernel/pci-common.c
··· 1144 1144 return start; 1145 1145 if (start & 0x300) 1146 1146 start = (start + 0x3ff) & ~0x3ff; 1147 + } else if (res->flags & IORESOURCE_MEM) { 1148 + start = pci_align_resource(dev, res, empty_res, size, align); 1147 1149 } 1148 1150 1149 1151 return start;
+2
arch/sh/drivers/pci/pci.c
··· 185 185 */ 186 186 if (start & 0x300) 187 187 start = (start + 0x3ff) & ~0x3ff; 188 + } else if (res->flags & IORESOURCE_MEM) { 189 + start = pci_align_resource(dev, res, empty_res, size, align); 188 190 } 189 191 190 192 return start;
+2
arch/x86/pci/i386.c
··· 165 165 if (start & 0x300) 166 166 start = (start + 0x3ff) & ~0x3ff; 167 167 } else if (res->flags & IORESOURCE_MEM) { 168 + start = pci_align_resource(dev, res, empty_res, size, align); 169 + 168 170 /* The low 1MB range is reserved for ISA cards */ 169 171 if (start < BIOS_END) 170 172 start = BIOS_END;
+2
arch/xtensa/kernel/pci.c
··· 54 54 55 55 if (start & 0x300) 56 56 start = (start + 0x3ff) & ~0x3ff; 57 + } else if (res->flags & IORESOURCE_MEM) { 58 + start = pci_align_resource(dev, res, empty_res, size, align); 57 59 } 58 60 59 61 return start;
+38 -1
drivers/pci/setup-res.c
··· 245 245 } 246 246 247 247 /* 248 + * For mem bridge windows, try to relocate tail remainder space to space 249 + * before res->start if there's enough free space there. This enables 250 + * tighter packing for resources. 251 + */ 252 + resource_size_t pci_align_resource(struct pci_dev *dev, 253 + const struct resource *res, 254 + const struct resource *empty_res, 255 + resource_size_t size, 256 + resource_size_t align) 257 + { 258 + resource_size_t remainder, start_addr; 259 + 260 + if (!(res->flags & IORESOURCE_MEM)) 261 + return res->start; 262 + 263 + if (IS_ALIGNED(size, align)) 264 + return res->start; 265 + 266 + remainder = size - ALIGN_DOWN(size, align); 267 + /* Don't mess with size that doesn't align with window size granularity */ 268 + if (!IS_ALIGNED(remainder, pci_min_window_alignment(dev->bus, res->flags))) 269 + return res->start; 270 + /* Try to place remainder that doesn't fill align before */ 271 + if (res->start < remainder) 272 + return res->start; 273 + start_addr = res->start - remainder; 274 + if (empty_res->start > start_addr) 275 + return res->start; 276 + 277 + pci_dbg(dev, "%pR: moving candidate start address below align to %llx\n", 278 + res, (unsigned long long)start_addr); 279 + return start_addr; 280 + } 281 + 282 + /* 248 283 * We don't have to worry about legacy ISA devices, so nothing to do here. 249 284 * This is marked as __weak because multiple architectures define it; it should 250 285 * eventually go away. ··· 290 255 resource_size_t size, 291 256 resource_size_t align) 292 257 { 293 - return res->start; 258 + struct pci_dev *dev = data; 259 + 260 + return pci_align_resource(dev, res, empty_res, size, align); 294 261 } 295 262 296 263 static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
+5
include/linux/pci.h
··· 1210 1210 const struct resource *empty_res, 1211 1211 resource_size_t size, 1212 1212 resource_size_t align); 1213 + resource_size_t pci_align_resource(struct pci_dev *dev, 1214 + const struct resource *res, 1215 + const struct resource *empty_res, 1216 + resource_size_t size, 1217 + resource_size_t align); 1213 1218 1214 1219 /* Generic PCI functions used internally */ 1215 1220
+1 -1
kernel/resource.c
··· 766 766 } 767 767 alloc.end = alloc.start + size - 1; 768 768 if (alloc.start <= alloc.end && 769 - __resource_contains_unbound(&avail, &alloc)) { 769 + __resource_contains_unbound(&full_avail, &alloc)) { 770 770 new->start = alloc.start; 771 771 new->end = alloc.end; 772 772 return 0;