Merge branch 'pci/resource'

- Prevent assigning space to unimplemented bridge windows; previously we
  mistakenly assumed prefetchable window existed and assigned space and put
  a BAR there (Ahmed Naseef)

- Avoid shrinking bridge windows to fit in the initial Root Port window;
  this fixes one problem with devices with large BARs connected via
  switches, e.g., Thunderbolt (Ilpo Järvinen)

- Retain information about optional resources to make assignment during
  rescan more likely to succeed (Ilpo Järvinen)

- Add __resource_contains_unbound() for use in finding space for resources
  with no address assigned (Ilpo Järvinen)

- Pass full extent of empty space, not just the aligned space, to
  resource_alignf callback so free space before the requested alignment can
  be used (Ilpo Järvinen)

- Remove unnecessary second alignment from ARM, m68k, MIPS (Ilpo Järvinen)

- Place small resources before larger ones for better utilization of
  address space (Ilpo Järvinen)

- Fix alignment calculation for resource size larger than align, e.g.,
  bridge windows larger than the 1MB required alignment (Ilpo Järvinen)

* pci/resource:
  PCI: Fix alignment calculation for resource size larger than align
  PCI: Align head space better
  PCI: Rename window_alignment() to pci_min_window_alignment()
  parisc/PCI: Clean up align handling
  MIPS: PCI: Remove unnecessary second application of align
  m68k/PCI: Remove unnecessary second application of align
  ARM/PCI: Remove unnecessary second application of align
  resource: Rename 'tmp' variable to 'full_avail'
  resource: Pass full extent of empty space to resource_alignf callback
  resource: Add __resource_contains_unbound() for internal contains checks
  PCI: Fix premature removal from realloc_head list during resource assignment
  PCI: Prevent shrinking bridge window from its required size
  PCI: Prevent assignment to unsupported bridge windows
This commit is contained in:
Bjorn Helgaas 2026-04-13 12:50:05 -05:00
commit 6cf4941ba9
19 changed files with 199 additions and 52 deletions

View File

@ -125,6 +125,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_final);
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)
{
struct pci_dev *dev = data;

View File

@ -560,7 +560,9 @@ char * __init pcibios_setup(char *str)
* which might be mirrored at 0x0100-0x03ff..
*/
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
const struct resource *empty_res,
resource_size_t size,
resource_size_t align)
{
struct pci_dev *dev = data;
resource_size_t start = res->start;
@ -569,14 +571,15 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
if (res->flags & IORESOURCE_IO && start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
start = (start + align - 1) & ~(align - 1);
host_bridge = pci_find_host_bridge(dev->bus);
if (host_bridge->align_resource)
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;
}

View File

@ -27,14 +27,18 @@
* which might be mirrored at 0x0100-0x03ff..
*/
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
const struct resource *empty_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;
start = (start + align - 1) & ~(align - 1);
if (res->flags & IORESOURCE_MEM)
return pci_align_resource(dev, res, empty_res, size, align);
return start;
}

View File

@ -22,7 +22,8 @@
* which might have be mirrored at 0x0100-0x03ff..
*/
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
const struct resource *empty_res,
resource_size_t size, resource_size_t align)
{
struct pci_dev *dev = data;
resource_size_t start = res->start;
@ -31,14 +32,15 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
if (res->flags & IORESOURCE_IO && start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
start = (start + align - 1) & ~(align - 1);
host_bridge = pci_find_host_bridge(dev->bus);
if (host_bridge->align_resource)
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;
}

View File

@ -52,6 +52,7 @@ unsigned long pci_address_to_pio(phys_addr_t address)
*/
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)
{
struct pci_dev *dev = data;
@ -69,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;

View File

@ -8,6 +8,7 @@
* Copyright (C) 1999-2001 Hewlett-Packard Company
* Copyright (C) 1999-2001 Grant Grundler
*/
#include <linux/align.h>
#include <linux/eisa.h>
#include <linux/init.h>
#include <linux/module.h>
@ -196,9 +197,12 @@ void __ref pcibios_init_bridge(struct pci_dev *dev)
* than res->start.
*/
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t alignment)
const struct resource *empty_res,
resource_size_t size,
resource_size_t alignment)
{
resource_size_t mask, align, start = res->start;
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",
pci_name(((struct pci_dev *) data)),
@ -207,11 +211,10 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
/* If it's not IO, then it's gotta be MEM */
align = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
/* Align to largest of MIN or input size */
mask = max(alignment, align) - 1;
start += mask;
start &= ~mask;
if (align > alignment)
start = ALIGN(start, align);
else
start = pci_align_resource(dev, res, empty_res, size, alignment);
return start;
}

View File

@ -1132,7 +1132,9 @@ static int skip_isa_ioresource_align(struct pci_dev *dev)
* which might have be mirrored at 0x0100-0x03ff..
*/
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
const struct resource *empty_res,
resource_size_t size,
resource_size_t align)
{
struct pci_dev *dev = data;
resource_size_t start = res->start;
@ -1142,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;

View File

@ -266,6 +266,7 @@ static int zpci_cfg_store(struct zpci_dev *zdev, int offset, u32 val, u8 len)
}
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)
{

View File

@ -168,7 +168,9 @@ subsys_initcall(pcibios_init);
* modulo 0x400.
*/
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
const struct resource *empty_res,
resource_size_t size,
resource_size_t align)
{
struct pci_dev *dev = data;
struct pci_channel *hose = dev->sysdata;
@ -183,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;

View File

@ -153,7 +153,8 @@ skip_isa_ioresource_align(struct pci_dev *dev) {
*/
resource_size_t
pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
const struct resource *empty_res,
resource_size_t size, resource_size_t align)
{
struct pci_dev *dev = data;
resource_size_t start = res->start;
@ -164,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;

View File

@ -39,6 +39,7 @@
*/
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)
{
struct pci_dev *dev = data;
@ -53,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;

View File

@ -1053,6 +1053,9 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
return resource_alignment(res);
}
resource_size_t pci_min_window_alignment(struct pci_bus *bus,
unsigned long type);
void pci_acs_init(struct pci_dev *dev);
void pci_enable_acs(struct pci_dev *dev);
#ifdef CONFIG_PCI_QUIRKS

View File

@ -378,6 +378,9 @@ static void pci_read_bridge_io(struct pci_dev *dev, struct resource *res,
unsigned long io_mask, io_granularity, base, limit;
struct pci_bus_region region;
if (!dev->io_window)
return;
io_mask = PCI_IO_RANGE_MASK;
io_granularity = 0x1000;
if (dev->io_window_1k) {
@ -448,6 +451,9 @@ static void pci_read_bridge_mmio_pref(struct pci_dev *dev, struct resource *res,
pci_bus_addr_t base, limit;
struct pci_bus_region region;
if (!dev->pref_window)
return;
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;

View File

@ -434,6 +434,10 @@ static void reassign_resources_sorted(struct list_head *realloc_head,
dev = add_res->dev;
idx = pci_resource_num(dev, res);
/* Skip this resource if not found in head list */
if (!res_to_dev_res(head, res))
continue;
/*
* Skip resource that failed the earlier assignment and is
* not optional as it would just fail again.
@ -442,10 +446,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head,
!pci_resource_is_optional(dev, idx))
goto out;
/* Skip this resource if not found in head list */
if (!res_to_dev_res(head, res))
continue;
res_name = pci_resource_name(dev, idx);
add_size = add_res->add_size;
align = add_res->min_align;
@ -1035,7 +1035,7 @@ resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
#define PCI_P2P_DEFAULT_IO_ALIGN SZ_4K
#define PCI_P2P_DEFAULT_IO_ALIGN_1K SZ_1K
static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type)
resource_size_t pci_min_window_alignment(struct pci_bus *bus, unsigned long type)
{
resource_size_t align = 1, arch_align;
@ -1084,7 +1084,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t add_size,
if (resource_assigned(b_res))
return;
min_align = window_alignment(bus, IORESOURCE_IO);
min_align = pci_min_window_alignment(bus, IORESOURCE_IO);
list_for_each_entry(dev, &bus->devices, bus_list) {
struct resource *r;
@ -1333,13 +1333,20 @@ static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res,
r_size = resource_size(r);
size += max(r_size, align);
aligns[order] += align;
/*
* If resource's size is larger than its alignment,
* some configurations result in an unwanted gap in
* the head space that the larger resource cannot
* fill.
*/
if (r_size <= align)
aligns[order] += align;
if (order > max_order)
max_order = order;
}
}
win_align = window_alignment(bus, b_res->flags);
win_align = pci_min_window_alignment(bus, b_res->flags);
min_align = calculate_head_align(aligns, max_order);
min_align = max(min_align, win_align);
size0 = calculate_memsize(size, realloc_head ? 0 : add_size,
@ -1837,6 +1844,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
resource_size_t new_size)
{
resource_size_t add_size, size = resource_size(res);
struct pci_dev_resource *dev_res;
if (resource_assigned(res))
return;
@ -1849,9 +1857,46 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
pci_dbg(bridge, "bridge window %pR extended by %pa\n", res,
&add_size);
} else if (new_size < size) {
int idx = pci_resource_num(bridge, res);
/*
* hpio/mmio/mmioprefsize hasn't been included at all? See the
* add_size param at the callsites of calculate_memsize().
*/
if (!add_list)
return;
/* Only shrink if the hotplug extra relates to window size. */
switch (idx) {
case PCI_BRIDGE_IO_WINDOW:
if (size > pci_hotplug_io_size)
return;
break;
case PCI_BRIDGE_MEM_WINDOW:
if (size > pci_hotplug_mmio_size)
return;
break;
case PCI_BRIDGE_PREF_MEM_WINDOW:
if (size > pci_hotplug_mmio_pref_size)
return;
break;
default:
break;
}
dev_res = res_to_dev_res(add_list, res);
add_size = size - new_size;
pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res,
&add_size);
if (add_size < dev_res->add_size) {
dev_res->add_size -= add_size;
pci_dbg(bridge, "bridge window %pR optional size shrunken by %pa\n",
res, &add_size);
} else {
pci_dbg(bridge, "bridge window %pR optional size removed\n",
res);
pci_dev_res_remove_from_list(add_list, res);
}
return;
} else {
return;
}

View 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
@ -251,10 +286,13 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
*/
resource_size_t __weak pcibios_align_resource(void *data,
const struct resource *res,
const struct resource *empty_res,
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,

View File

@ -602,7 +602,8 @@ static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data,
static resource_size_t
pcmcia_align(void *align_data, const struct resource *res,
resource_size_t size, resource_size_t align)
const struct resource *empty_res,
resource_size_t size, resource_size_t align)
{
struct pcmcia_align_data *data = align_data;
struct resource_map *m;

View File

@ -202,6 +202,7 @@ enum {
* typedef resource_alignf - Resource alignment callback
* @data: Private data used by the callback
* @res: Resource candidate range (an empty resource space)
* @empty_res: Empty resource range without alignment applied
* @size: The minimum size of the empty space
* @align: Alignment from the constraints
*
@ -212,6 +213,7 @@ enum {
*/
typedef resource_size_t (*resource_alignf)(void *data,
const struct resource *res,
const struct resource *empty_res,
resource_size_t size,
resource_size_t align);
@ -304,14 +306,28 @@ static inline unsigned long resource_ext_type(const struct resource *res)
{
return res->flags & IORESOURCE_EXT_TYPE_BITS;
}
/* True iff r1 completely contains r2 */
static inline bool resource_contains(const struct resource *r1, const struct resource *r2)
/*
* For checking if @r1 completely contains @r2 for resources that have real
* addresses but are not yet crafted into the resource tree. Normally
* resource_contains() should be used instead of this function as it checks
* also IORESOURCE_UNSET flag.
*/
static inline bool __resource_contains_unbound(const struct resource *r1,
const struct resource *r2)
{
if (resource_type(r1) != resource_type(r2))
return false;
return r1->start <= r2->start && r1->end >= r2->end;
}
/* True iff r1 completely contains r2 */
static inline bool resource_contains(const struct resource *r1, const struct resource *r2)
{
if (r1->flags & IORESOURCE_UNSET || r2->flags & IORESOURCE_UNSET)
return false;
return r1->start <= r2->start && r1->end >= r2->end;
return __resource_contains_unbound(r1, r2);
}
/* True if any part of r1 overlaps r2 */

View File

@ -1212,9 +1212,15 @@ int __must_check pcibios_enable_device(struct pci_dev *, int mask);
char *pcibios_setup(char *str);
/* Used only when drivers/pci/setup.c is used */
resource_size_t pcibios_align_resource(void *, const struct resource *,
resource_size_t,
resource_size_t);
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 */

View File

@ -727,45 +727,46 @@ static int __find_resource_space(struct resource *root, struct resource *old,
struct resource_constraint *constraint)
{
struct resource *this = root->child;
struct resource tmp = *new, avail, alloc;
struct resource full_avail = *new, avail, alloc;
resource_alignf alignf = constraint->alignf;
tmp.start = root->start;
full_avail.start = root->start;
/*
* Skip past an allocated resource that starts at 0, since the assignment
* of this->start - 1 to tmp->end below would cause an underflow.
* of this->start - 1 to full_avail->end below would cause an underflow.
*/
if (this && this->start == root->start) {
tmp.start = (this == old) ? old->start : this->end + 1;
full_avail.start = (this == old) ? old->start : this->end + 1;
this = this->sibling;
}
for(;;) {
if (this)
tmp.end = (this == old) ? this->end : this->start - 1;
full_avail.end = (this == old) ? this->end : this->start - 1;
else
tmp.end = root->end;
full_avail.end = root->end;
if (tmp.end < tmp.start)
if (full_avail.end < full_avail.start)
goto next;
resource_clip(&tmp, constraint->min, constraint->max);
arch_remove_reservations(&tmp);
resource_clip(&full_avail, constraint->min, constraint->max);
arch_remove_reservations(&full_avail);
/* Check for overflow after ALIGN() */
avail.start = ALIGN(tmp.start, constraint->align);
avail.end = tmp.end;
avail.flags = new->flags & ~IORESOURCE_UNSET;
if (avail.start >= tmp.start) {
avail.start = ALIGN(full_avail.start, constraint->align);
avail.end = full_avail.end;
avail.flags = new->flags;
if (avail.start >= full_avail.start) {
alloc.flags = avail.flags;
if (alignf) {
alloc.start = alignf(constraint->alignf_data,
&avail, size, constraint->align);
&avail, &full_avail,
size, constraint->align);
} else {
alloc.start = avail.start;
}
alloc.end = alloc.start + size - 1;
if (alloc.start <= alloc.end &&
resource_contains(&avail, &alloc)) {
__resource_contains_unbound(&full_avail, &alloc)) {
new->start = alloc.start;
new->end = alloc.end;
return 0;
@ -776,7 +777,7 @@ next: if (!this || this->end == root->end)
break;
if (this != old)
tmp.start = this->end + 1;
full_avail.start = this->end + 1;
this = this->sibling;
}
return -EBUSY;