summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-04-13 12:50:05 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-04-13 12:50:05 -0500
commit6cf4941ba9b11c28bbd7c9a7c1a461b94cd486c3 (patch)
treed7a48bf700c47930685502f300b93a24ab2adabd /drivers/pci
parent12b56ec723d2d736feb16ea6ea2505520de3cc58 (diff)
parent8cb081667377709f4924ab6b3a88a0d7a761fe91 (diff)
downloadlwn-6cf4941ba9b11c28bbd7c9a7c1a461b94cd486c3.tar.gz
lwn-6cf4941ba9b11c28bbd7c9a7c1a461b94cd486c3.zip
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
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci.h3
-rw-r--r--drivers/pci/probe.c6
-rw-r--r--drivers/pci/setup-bus.c65
-rw-r--r--drivers/pci/setup-res.c40
4 files changed, 103 insertions, 11 deletions
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index a1d2ecb56207..9540f1f1709a 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -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
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 19d73f6132fb..675713548431 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -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;
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 61f769aaa2f6..4cf120ebe5ad 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -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;
}
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index bb2aef373d6f..fbc05cda96ee 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -245,16 +245,54 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
}
/*
+ * 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
* eventually go away.
*/
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,