summaryrefslogtreecommitdiff
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
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
-rw-r--r--arch/alpha/kernel/pci.c1
-rw-r--r--arch/arm/kernel/bios32.c9
-rw-r--r--arch/m68k/kernel/pcibios.c8
-rw-r--r--arch/mips/pci/pci-generic.c8
-rw-r--r--arch/mips/pci/pci-legacy.c3
-rw-r--r--arch/parisc/kernel/pci.c17
-rw-r--r--arch/powerpc/kernel/pci-common.c6
-rw-r--r--arch/s390/pci/pci.c1
-rw-r--r--arch/sh/drivers/pci/pci.c6
-rw-r--r--arch/x86/pci/i386.c5
-rw-r--r--arch/xtensa/kernel/pci.c3
-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
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c3
-rw-r--r--include/linux/ioport.h22
-rw-r--r--include/linux/pci.h12
-rw-r--r--kernel/resource.c33
19 files changed, 199 insertions, 52 deletions
diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c
index 51a8a4c4572a..11df411b1d18 100644
--- a/arch/alpha/kernel/pci.c
+++ b/arch/alpha/kernel/pci.c
@@ -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;
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index b5793e8fbdc1..ac0e890510da 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -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;
}
diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c
index e6ab3f9ff5d8..7a9e60df79c5 100644
--- a/arch/m68k/kernel/pcibios.c
+++ b/arch/m68k/kernel/pcibios.c
@@ -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;
}
diff --git a/arch/mips/pci/pci-generic.c b/arch/mips/pci/pci-generic.c
index d2d68bac3d25..c2e23d0c1d77 100644
--- a/arch/mips/pci/pci-generic.c
+++ b/arch/mips/pci/pci-generic.c
@@ -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;
}
diff --git a/arch/mips/pci/pci-legacy.c b/arch/mips/pci/pci-legacy.c
index d04b7c1294b6..dae6dafdd6e0 100644
--- a/arch/mips/pci/pci-legacy.c
+++ b/arch/mips/pci/pci-legacy.c
@@ -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;
diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c
index cf285b17a5ae..b8007c7400d4 100644
--- a/arch/parisc/kernel/pci.c
+++ b/arch/parisc/kernel/pci.c
@@ -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;
}
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index a7a2fb605971..8efe95a0c4ff 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -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;
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index 2a430722cbe4..39bd2adfc240 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -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)
{
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
index a3903304f33f..878a27a1acfb 100644
--- a/arch/sh/drivers/pci/pci.c
+++ b/arch/sh/drivers/pci/pci.c
@@ -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;
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c
index c4ec39ad276b..e2de26b82940 100644
--- a/arch/x86/pci/i386.c
+++ b/arch/x86/pci/i386.c
@@ -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;
diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c
index 62c900e400d6..305031551136 100644
--- a/arch/xtensa/kernel/pci.c
+++ b/arch/xtensa/kernel/pci.c
@@ -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;
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,
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 0679dd434719..949e69921fe9 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -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;
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 5533a5debf3f..3c73c9c0d4f7 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -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 */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index cc98713e3ce8..5aa1695fb4fe 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -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 */
diff --git a/kernel/resource.c b/kernel/resource.c
index bb966699da31..d02a53fb95d8 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -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;