diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-04-13 12:50:05 -0500 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-04-13 12:50:05 -0500 |
| commit | 6cf4941ba9b11c28bbd7c9a7c1a461b94cd486c3 (patch) | |
| tree | d7a48bf700c47930685502f300b93a24ab2adabd /drivers/pci | |
| parent | 12b56ec723d2d736feb16ea6ea2505520de3cc58 (diff) | |
| parent | 8cb081667377709f4924ab6b3a88a0d7a761fe91 (diff) | |
| download | lwn-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.h | 3 | ||||
| -rw-r--r-- | drivers/pci/probe.c | 6 | ||||
| -rw-r--r-- | drivers/pci/setup-bus.c | 65 | ||||
| -rw-r--r-- | drivers/pci/setup-res.c | 40 |
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, |
