mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
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
This commit is contained in:
committed by
Bjorn Helgaas
parent
8bbe8cec89
commit
9036bd0efc
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user