* [PATCH 0/5] PCI: Resource placement algorithm fixes
@ 2026-09-23 13:17 Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 1/5] resource: Mark free space assigned Ilpo Järvinen
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Ilpo Järvinen @ 2026-09-23 13:17 UTC (permalink / raw)
To: Maciej Grochowski, Nikolas Joshua Britton, Geramy Loveless,
Eric Auger, Alexey Fomenko, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Krzysztof Wilczyński
Cc: linux-kernel, Ilpo Järvinen
Hi,
This series generalizes resource placement algorithm. The commit
9036bd0efcb6 ("PCI: Align head space better") special cased one
composite resource remainder case to fix a regression (in a long chain
of regression fixes). Unfortunately, it introduced another regression
(or a few, to be more accurate).
Instead of building more special tricks, generalize the PCI resource
placement algorithm so it returns placements that gives better chances
for the greedy assignment algorithm to find suitable space for
subsequent assignments.
In short words, the new approach tries to find a placement for the
current resource that blocks as little of the free space range as
possible, considering both alignment and continuous free span of the
remaining free space.
In addition, the series corrects composite resource sizing to account
for gaps that have to be added due to alignment constaints and
remainder space not fully connecting (filling space all the way to
the bridge window align).
More detailed explanations in the patches themselves.
Unfortunately, this also causes yet another regression due to shuffling
resources around, even if the resulting arrangement seems just as valid
as any other (see the workaround quirk in the patch 5 for more details).
Ilpo Järvinen (5):
resource: Mark free space assigned
PCI: Fix nesting windows with remainder at the left edge
PCI: Place resources to either edge of the window
PCI: Fix composite resource sizing
PCI/quirks: Avoid certain BAR 0 address with igb
drivers/pci/pci.h | 4 +
drivers/pci/quirks.c | 56 +++++++++++
drivers/pci/setup-bus.c | 83 ++++++++++++++--
drivers/pci/setup-res.c | 207 +++++++++++++++++++++++++++++++++++-----
kernel/resource.c | 1 +
5 files changed, 322 insertions(+), 29 deletions(-)
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/5] resource: Mark free space assigned
2026-09-23 13:17 [PATCH 0/5] PCI: Resource placement algorithm fixes Ilpo Järvinen
@ 2026-09-23 13:17 ` Ilpo Järvinen
2026-09-23 15:58 ` Bradley Morgan
2026-09-23 13:17 ` [PATCH 2/5] PCI: Fix nesting windows with remainder at the left edge Ilpo Järvinen
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Ilpo Järvinen @ 2026-09-23 13:17 UTC (permalink / raw)
To: Maciej Grochowski, Nikolas Joshua Britton, Geramy Loveless,
Eric Auger, Alexey Fomenko, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Krzysztof Wilczyński, linux-kernel
Cc: Ilpo Järvinen
Free space ranges are (part of) assigned ranges. Since new resources
have IORESOURCE_UNSET, the flag gets copied also to full_avail.flags,
which hampers printing them with %pR.
Clear IORESOURCE_UNSET for the free space indicator resource.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
kernel/resource.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/kernel/resource.c b/kernel/resource.c
index e60539a55541..17e7fccd859c 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -731,6 +731,7 @@ static int __find_resource_space(struct resource *root, struct resource *old,
resource_alignf alignf = constraint->alignf;
full_avail.start = root->start;
+ full_avail.flags &= ~IORESOURCE_UNSET;
/*
* Skip past an allocated resource that starts at 0, since the assignment
* of this->start - 1 to full_avail->end below would cause an underflow.
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 2/5] PCI: Fix nesting windows with remainder at the left edge
2026-09-23 13:17 [PATCH 0/5] PCI: Resource placement algorithm fixes Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 1/5] resource: Mark free space assigned Ilpo Järvinen
@ 2026-09-23 13:17 ` Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 3/5] PCI: Place resources to either edge of the window Ilpo Järvinen
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Ilpo Järvinen @ 2026-09-23 13:17 UTC (permalink / raw)
To: Maciej Grochowski, Nikolas Joshua Britton, Geramy Loveless,
Eric Auger, Alexey Fomenko, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Krzysztof Wilczyński, Ilpo Järvinen,
linux-pci, linux-kernel
If a bridge window has its non-aligning remainder at the left edge of
the window, assigning a nested bridge window of same size will fail
because resource side code will only allow starting a candicate range
from an aligning address. In such case, aligned address is somewhere in
the middle, not at the left edge of the window. Since the entire
available space is required for full-sized nested bridge window,
resource side code rejects the free space.
To solve this problem, PCI side code has to bypass the resource side
alignment code by giving a smaller alignment and must check the
aligment requirement itself. This allows resource side code to consider
the entire available space as candidate to fit the required size.
Wrap pcibios_align_resource() with PCI core function that performs the
alignment checks and left edge recalculation if needed. As the
alignment given for the resource side code is artificially small, the
real alignment has to be passed through alignf_data.
Fixes: 9036bd0efcb6 ("PCI: Align head space better")
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
I've not seen a report about this problem (thus no log quotes) but with
nested thunderbolt topologies it seems well within realms of possibility
of occurring.
---
drivers/pci/setup-res.c | 64 +++++++++++++++++++++++++++++++++++++----
1 file changed, 59 insertions(+), 5 deletions(-)
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 376f09630a4a..eacce9e2486b 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -13,6 +13,8 @@
* Resource sorting
*/
+#include <linux/align.h>
+#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/pci.h>
@@ -260,6 +262,12 @@ resource_size_t pci_resource_alignment(const struct pci_dev *dev,
return resource_alignment(res);
}
+static resource_size_t pci_resreq_remainder(resource_size_t size,
+ resource_size_t align)
+{
+ return size - ALIGN_DOWN(size, align);
+}
+
/*
* For mem bridge windows, try to relocate tail remainder space to space
* before res->start if there's enough free space there. This enables
@@ -276,10 +284,17 @@ resource_size_t pci_align_resource(struct pci_dev *dev,
if (!(res->flags & IORESOURCE_MEM))
return res->start;
- if (IS_ALIGNED(size, align))
+ remainder = pci_resreq_remainder(size, align);
+ if (!remainder)
+ return res->start;
+
+ /*
+ * Size constraints forced an early start move in
+ * pci_check_and_align_resource()?
+ */
+ if (!IS_ALIGNED(res->start, 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;
@@ -311,15 +326,54 @@ resource_size_t __weak pcibios_align_resource(void *data,
return pci_align_resource(dev, res, empty_res, size, align);
}
+struct pci_resreq_data {
+ struct pci_dev *dev;
+ resource_size_t real_align;
+};
+
+static resource_size_t pci_resreq_check(void *data,
+ const struct resource *res,
+ const struct resource *empty_res,
+ resource_size_t size,
+ resource_size_t min_align)
+{
+ struct pci_resreq_data *rr = data;
+ resource_size_t align = rr->real_align;
+ resource_size_t remainder, start;
+ struct resource tmp = *res;
+
+ if (res->flags & IORESOURCE_MEM && !IS_ALIGNED(res->start, align)) {
+ resource_set_range(&tmp, ALIGN(res->start, align), size);
+ if (!__resource_contains_unbound(empty_res, &tmp)) {
+ remainder = pci_resreq_remainder(size, align);
+ resource_set_range(&tmp, tmp.start - remainder, size);
+ if (!__resource_contains_unbound(empty_res, &tmp))
+ return tmp.start; /* caller skips range */
+ }
+ }
+
+ start = pcibios_align_resource(rr->dev, &tmp, empty_res, size, align);
+ WARN_ON_ONCE(!IS_ALIGNED(start, min_align));
+
+ return start;
+}
+
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
int resno, resource_size_t size, resource_size_t align)
{
+ struct pci_resreq_data rr = { .dev = dev, .real_align = align };
struct resource *res = pci_resource_n(dev, resno);
resource_size_t min;
int ret;
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
+ if ((res->flags & IORESOURCE_MEM) && pci_resreq_remainder(size, align)) {
+ align = min;
+ if (pci_resource_is_bridge_win(resno))
+ align = pci_min_window_alignment(bus, res->flags);
+ }
+
/*
* First, try exact prefetching match. Even if a 64-bit
* prefetchable bridge window is below 4GB, we can't put a 32-bit
@@ -329,7 +383,7 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
*/
ret = pci_bus_alloc_resource(bus, res, size, align, min,
IORESOURCE_PREFETCH | IORESOURCE_MEM_64,
- pcibios_align_resource, dev);
+ pci_resreq_check, &rr);
if (ret == 0)
return 0;
@@ -341,7 +395,7 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
(IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) {
ret = pci_bus_alloc_resource(bus, res, size, align, min,
IORESOURCE_PREFETCH,
- pcibios_align_resource, dev);
+ pci_resreq_check, &rr);
if (ret == 0)
return 0;
}
@@ -354,7 +408,7 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
*/
if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
- pcibios_align_resource, dev);
+ pci_resreq_check, &rr);
return ret;
}
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 3/5] PCI: Place resources to either edge of the window
2026-09-23 13:17 [PATCH 0/5] PCI: Resource placement algorithm fixes Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 1/5] resource: Mark free space assigned Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 2/5] PCI: Fix nesting windows with remainder at the left edge Ilpo Järvinen
@ 2026-09-23 13:17 ` Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 4/5] PCI: Fix composite resource sizing Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 5/5] PCI/quirks: Avoid certain BAR 0 address with igb Ilpo Järvinen
4 siblings, 0 replies; 7+ messages in thread
From: Ilpo Järvinen @ 2026-09-23 13:17 UTC (permalink / raw)
To: Maciej Grochowski, Nikolas Joshua Britton, Geramy Loveless,
Eric Auger, Alexey Fomenko, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Krzysztof Wilczyński, Ilpo Järvinen,
linux-pci, linux-kernel
Cc: Eric Auger
PCI resource assignment phase based on a greedy algorithm. The
resources are assigned in descending order of required alignment.
Because the PCI BARs are power-of-two sized resources, it mostly works
(bridge windows and VF BARs are composite resources that might not
necessarily have power-of-two size but internally they are still
composed of BARs).
The resource assignment prior to the commit 9036bd0efcb6 ("PCI: Align
head space better") placed resource to/towards the left edge of the
window. The commit 9036bd0efcb6 ("PCI: Align head space better")
altered assignment for resources whose size is not a perfect multiple
of the required alignment by moving the remainder before the left edge
of the window if possible.
The behavior after the commit 9036bd0efcb6 ("PCI: Align head space
better") may result in problems when a bridge window consists of one
large alignment resource and a composite one with a smaller alignment
(this is a typical setup for GPUs PF BAR and VF BARs). The resource
with largest alignment is assigned first and placed such that the
remainder space is left of the assigned resource, which effectively
splits the remaining space into two. While the VF BAR could fit to the
remaining space, it requires continuous free space that is no longer
there because the large resource is now in the middle.
In this log exceprt, BAR 2 is placed in the middle of the window blocking
the larger VF BAR 2 from fitting anywhere:
pci 0000:03:01.0: bridge window [mem 0xa9f8000000-0xbfffffffff 64bit pref]: assigned
pci 0000:04:00.0: BAR 2 [mem 0xb000000000-0xb7ffffffff 64bit pref]: assigned
pci 0000:04:00.0: VF BAR 2 [mem size 0xe00000000 64bit pref]: can't assign; no space
pci 0000:04:00.0: VF BAR 2 [mem size 0xe00000000 64bit pref]: failed to assign
The old behavior, despite being greedy, naturally consumed space from
the left edge leaving the remainder space adjacent to the other free
space. When remainder space is at the left edge of the window, the
greedy algorithm should instead assign to the right edge of the window.
(If both ends do align, either end works equally.)
While defining window edge aware resource assignment algorithm, one
additional thing is useful to note. In DT setups the bridge
windows/root bus resources are often very precisely sized, with right
edge of the window having much smaller alignment that the left edge.
They also often come with a small BAR that should be placed to the
right edge to not block bridge window placed to the left edge of the
window. The bridge window may be entirely optional at this point
because of hotplug. The resource assignment fallback phase assigns
mandatory resources first and in such a case, small BAR gets assigned
first.
One example where mandatory BAR 0 blocks bridge windows from fitting
(both bridge windows wouldn't fit because of qemu not providing enough
space for both bridge windows but one should fit like it was originally
setup by the platform):
pci_bus 0000:0a: root bus resource [mem 0x10a00000-0x10c00fff window]
pci 0000:0a:00.0: [1b36:000c] type 01 class 0x060400 PCIe Root Port
pci 0000:0a:00.0: BAR 0 [mem 0x10c00000-0x10c00fff]
pci 0000:0a:00.0: PCI bridge to [bus 0b-0d]
pci 0000:0a:00.0: bridge window [mem 0x10a00000-0x10bfffff]
pci 0000:0a:00.0: enabling Extended Tags
pci 0000:0a:00.0: bridge window [mem 0x00100000-0x000fffff 64bit pref] to [bus 0b-0d] add_size 200000 add_align 100000
pci 0000:0a:00.0: bridge window [mem 0x00100000-0x000fffff] to [bus 0b-0d] add_size 200000 add_align 100000
pci 0000:0a:00.0: bridge window [mem 0x10a00000-0x10bfffff]: assigned
pci 0000:0a:00.0: bridge window [mem size 0x00200000 64bit pref]: can't assign; no space
pci 0000:0a:00.0: bridge window [mem size 0x00200000 64bit pref]: failed to assign
pci 0000:0a:00.0: BAR 0 [mem 0x10c00000-0x10c00fff]: assigned
pci 0000:0a:00.0: bridge window [mem 0x10a00000-0x10bfffff]: releasing
pci 0000:0a:00.0: BAR 0 [mem 0x10c00000-0x10c00fff]: releasing
pci 0000:0a:00.0: BAR 0 [mem 0x10a00000-0x10a00fff]: assigned
pci 0000:0a:00.0: bridge window [mem size 0x00200000]: can't assign; no space
pci 0000:0a:00.0: bridge window [mem size 0x00200000]: failed to assign
pci 0000:0a:00.0: bridge window [mem size 0x00200000 64bit pref]: can't assign; no space
pci 0000:0a:00.0: bridge window [mem size 0x00200000 64bit pref]: failed to assign
pci_bus 0000:0a: Some PCI device resources are unassigned, try booting with pci=realloc
To achieve best generalization of the approach, the solution should
also avoid consuming space from the window edge that can fit largest
aligning resources in the future, whenever possible.
Reported-by: Alexey Fomenko <alexey.fomenko@intel.com>
Tested-by: Alexey Fomenko <alexey.fomenko@intel.com>
Reported-by: Eric Auger <eauger@redhat.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Fixes: 9036bd0efcb6 ("PCI: Align head space better")
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
Both reports are private discussions (Eric's report started as public
and produced another fix but further problem was only visible in the
privately send logs after that). Thus no links.
Unfortunately, those debug resource prints cannot currently use
pci_resource_name() because the original resource (the one within
pci_dev's resource array) isn't available. I'll change that eventually
so that pci_dev's resource is passed to pcibios_align_resource() but
that will require changing its signature again which is a bit tedious
as it requires touching all those arch/ functions.
---
drivers/pci/pci.h | 4 +
drivers/pci/setup-bus.c | 4 -
drivers/pci/setup-res.c | 157 ++++++++++++++++++++++++++++++++++------
3 files changed, 137 insertions(+), 28 deletions(-)
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fddddc2..691711e56597 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -109,6 +109,10 @@ struct pcie_tlp_log;
#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
+#define PCI_RES_TYPE_MASK \
+ (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\
+ IORESOURCE_MEM_64)
+
extern const unsigned char pcie_link_speed[];
unsigned char pcie_get_link_speed(unsigned int speed);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index e8c94aa1d3c1..7ca0e9f4ffb6 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -31,10 +31,6 @@
#include <linux/acpi.h>
#include "pci.h"
-#define PCI_RES_TYPE_MASK \
- (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\
- IORESOURCE_MEM_64)
-
unsigned int pci_flags;
EXPORT_SYMBOL_GPL(pci_flags);
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index eacce9e2486b..1ab5d167ab5c 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -17,6 +17,9 @@
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/export.h>
+#include <linux/limits.h>
+#include <linux/log2.h>
+#include <linux/minmax.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
@@ -262,16 +265,80 @@ resource_size_t pci_resource_alignment(const struct pci_dev *dev,
return resource_alignment(res);
}
+static resource_size_t pci_max_natural_size(const struct resource *res,
+ resource_size_t *max_align)
+{
+ resource_size_t size = resource_size(res);
+ resource_size_t powof2, natural_start;
+
+ *max_align = 1;
+ if (!size)
+ return 0;
+
+ powof2 = rounddown_pow_of_two(size);
+ natural_start = ALIGN(res->start, powof2);
+ if (natural_start >= ALIGN_DOWN(res->end + 1, powof2)) {
+ powof2 = max(powof2 / 2, 1U);
+ natural_start = ALIGN(res->start, powof2);
+ }
+
+ if (natural_start) {
+ *max_align <<= __ffs(natural_start);
+ } else {
+ /*
+ * Zero address has infinite alignment, return the largest
+ * representable number even if it's not a power of two.
+ */
+ *max_align = RESOURCE_SIZE_MAX;
+ }
+
+ return powof2;
+}
+
static resource_size_t pci_resreq_remainder(resource_size_t size,
resource_size_t align)
{
return size - ALIGN_DOWN(size, align);
}
-/*
- * 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.
+/**
+ * pci_align_resource - Places resource into empty space range
+ * @dev: PCI device resource belongs to
+ * @res: Candidate range calculated by caller (not among @dev's resources!)
+ * @empty_res: Full free space range
+ * @size: Required size for the resource
+ * @align: Required alignment (see below for details)
+ *
+ * Places resource inside @empty_res honoring @size and @align. Following
+ * special logic only applies to mem resources currently. For composite
+ * resources not divisable by @align, @align does not apply to non-aligning
+ * remainder part giving some leeway for its placement.
+ *
+ * There are 4 candidate positions:
+ *
+ * W0WWW0WWW0WWW
+ * 1. AAAAr
+ * 2. rAAAA
+ * 3. AAAAr
+ * 4. rAAAA
+ *
+ * W = bridge window
+ * 0 = bridge window offset matching align
+ * A = aligning part of size (size rounded down by align)
+ * r = non-aligning remainder
+ *
+ * Cases 1 & 3 and 2 & 4 may degenerate to the same candidate.
+ *
+ * Select resource placement based on the remaining free space. Pick the
+ * candidate with which the remaining free space has (in decreasing order of
+ * priority):
+ *
+ * 1. the largest naturally aligning power-of-two-sized address range within,
+ * 2. the largest continous free space,
+ * 3. the largest alignment of the start address for the naturally aligning
+ * free space range (from check 1).
+ *
+ * Cases 1 & 2 check only right edge free space and 3 & 4 the left edge.
*/
resource_size_t pci_align_resource(struct pci_dev *dev,
const struct resource *res,
@@ -279,35 +346,77 @@ resource_size_t pci_align_resource(struct pci_dev *dev,
resource_size_t size,
resource_size_t align)
{
- resource_size_t remainder, start_addr;
+ unsigned long type = res->flags & PCI_RES_TYPE_MASK;
+ resource_size_t best_natural_size = 0, best_size = 0, best_maxalign = 0;
+ resource_size_t aligning, remainder;
+ struct resource candidate[4];
+ unsigned int i;
+ int best = -1;
if (!(res->flags & IORESOURCE_MEM))
return res->start;
remainder = pci_resreq_remainder(size, align);
- if (!remainder)
- return res->start;
+ aligning = size - remainder;
+
+ candidate[0] = DEFINE_RES(ALIGN(empty_res->start, align), size, type);
+ candidate[1] = DEFINE_RES(ALIGN(empty_res->start, align) - remainder,
+ size, type);
+ candidate[2] = DEFINE_RES(ALIGN_DOWN(empty_res->end + 1 - remainder, align) -
+ aligning, size, type);
+ candidate[3] = DEFINE_RES(ALIGN_DOWN(empty_res->end + 1, align) - size,
+ size, type);
+
+ for (i = 0; i < ARRAY_SIZE(candidate); i++) {
+ struct resource remaining;
+ resource_size_t natural_size, size, maxalign;
+
+ if ((candidate[i].start > candidate[i].end) ||
+ !__resource_contains_unbound(empty_res, &candidate[i])) {
+ pci_dbg(dev, "%pR: candidate %u %pR not within free space %pR\n",
+ res, i, &candidate[i], empty_res);
+ continue;
+ }
- /*
- * Size constraints forced an early start move in
- * pci_check_and_align_resource()?
- */
- if (!IS_ALIGNED(res->start, align))
- return res->start;
+ remaining.flags = type;
+ if (i <= 1) {
+ remaining.start = candidate[i].end + 1;
+ remaining.end = empty_res->end;
+ } else {
+ remaining.start = empty_res->start;
+ remaining.end = candidate[i].start - 1;
+ }
- /* 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)
+ natural_size = pci_max_natural_size(&remaining, &maxalign);
+ size = resource_size(&remaining);
+ pci_dbg(dev, "%pR: candidate %u %pR, free space naturalsize=%llx size=%llx maxalign=%llx\n",
+ res, i, &candidate[i],
+ (unsigned long long)natural_size,
+ (unsigned long long)size,
+ (unsigned long long)maxalign);
+ if ((best < 0) ||
+ (natural_size > best_natural_size) ||
+ (natural_size == best_natural_size && size > best_size) ||
+ (natural_size == best_natural_size && size == best_size &&
+ maxalign > best_maxalign)) {
+ best = i;
+ best_natural_size = natural_size;
+ best_size = size;
+ best_maxalign = maxalign;
+ }
+ }
+
+ /* None fits? Return some address and let the caller deal with it. */
+ if (best == -1)
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;
+ pci_dbg(dev, "%pR: picked candidate %u (free space %pR), size: %llx + %llx, align: %llx\n",
+ &candidate[best], best, empty_res,
+ (unsigned long long)aligning,
+ (unsigned long long)remainder,
+ (unsigned long long)align);
+
+ return candidate[best].start;
}
/*
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 4/5] PCI: Fix composite resource sizing
2026-09-23 13:17 [PATCH 0/5] PCI: Resource placement algorithm fixes Ilpo Järvinen
` (2 preceding siblings ...)
2026-09-23 13:17 ` [PATCH 3/5] PCI: Place resources to either edge of the window Ilpo Järvinen
@ 2026-09-23 13:17 ` Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 5/5] PCI/quirks: Avoid certain BAR 0 address with igb Ilpo Järvinen
4 siblings, 0 replies; 7+ messages in thread
From: Ilpo Järvinen @ 2026-09-23 13:17 UTC (permalink / raw)
To: Maciej Grochowski, Nikolas Joshua Britton, Geramy Loveless,
Eric Auger, Alexey Fomenko, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Krzysztof Wilczyński, Ilpo Järvinen,
linux-pci, linux-kernel
The bridge window sizing algorithm aims to pack child resources back to
back. With multiple composite child resources whose sizes do not align
to the calculated minimal alignment for the bridge window, back-to-back
placement may not be possible. The non-aligning remainder placement is
limited because it must be adjacent to the rest of the composite
resource.
Effectively, two remainder parts may be placed into the same align
sized block, but sum of their size might not match align. In such case,
a gap is required to meet the alignment requirement of both resources.
Add bridge window gap size calculator. Basic rules:
1) Gaps are only necessary if there is more than one non-aligning
composite resource within a single bridge window.
2) If there are only two remainder parts that amount to less than align
together, the required gap is the difference of align and the sum of
remainder sizes.
3) On other cases, round each remainder part to align to get the gap
size. Sometimes, smaller size may be possible but due to how sizing
and assignment are made in different phases, it is not always
possible to predict where each resource is assigned. Thus, the
sizing has to play safe.
The gap is calculated based on the minimal alignment for the bridge
window, which may be different for the case with only required
resources and the case with optional resources.
Fixes: 9036bd0efcb6 ("PCI: Align head space better")
Reported-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Bjorn Helgaas <bhelgaas@google.com>
Reported-by: Nikolas Joshua Britton <nbritton@exabit.io>
Link: https://lore.kernel.org/linux-pci/20260903063124.9316-1-nbritton@exabit.io/
Reported-by: Maciej Grochowski <Maciej.Grochowski@sony.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
drivers/pci/setup-bus.c | 79 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 76 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 7ca0e9f4ffb6..9d828a59bd00 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -1164,6 +1164,76 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
return min_align;
}
+/*
+ * Bridge window gap size calculator.
+ *
+ * Calculates gap (empty space) necessary because of non-aligning composite
+ * resources (VF BARs, bridge windows).
+ *
+ * Rules:
+ *
+ * 1) Gaps are only necessary if there is more than one non-aligning
+ * composite resource within a single bridge window.
+ *
+ * 2) If there are only two remainder parts that amount to less than win_align
+ * together, the required gap is the difference of win_align and the sum of
+ * remainder sizes.
+ *
+ * 3) On other cases, round each remainder part to win_align to get the gap
+ * size. Sometimes, tighter packing might be possible but due to how
+ * sizing and assignment are made in different phases, it is not always
+ * possible to predict where each resource is assigned. Thus, the sizing
+ * has to play safe even if it may overestimate in some cases.
+ */
+static resource_size_t calculate_win_gap_size(struct pci_bus *bus,
+ struct resource *b_res,
+ resource_size_t win_align,
+ bool optional)
+{
+ resource_size_t safe_gap = 0, remainders = 0;
+ unsigned int nonaligning = 0;
+ struct pci_dev *dev;
+
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ struct resource *r;
+ int i;
+
+ pci_dev_for_each_resource(dev, r, i) {
+ resource_size_t r_size, remainder, aligning;
+
+ if (!pdev_resources_assignable(dev) ||
+ !pdev_resource_should_fit(dev, r))
+ continue;
+ if (b_res != pbus_select_window(bus, r))
+ continue;
+
+ if (!optional && pci_resource_is_optional(dev, i))
+ continue;
+
+ r_size = resource_size(r);
+ if (r_size <= win_align)
+ continue;
+
+ aligning = ALIGN_DOWN(r_size, win_align);
+ remainder = r_size - aligning;
+ if (!remainder)
+ continue;
+
+ nonaligning++;
+ remainders += remainder;
+ safe_gap += win_align - remainder;
+ }
+ }
+
+ if (nonaligning == 2 && (remainders <= win_align))
+ return win_align - remainders;
+
+ if (nonaligning >= 2)
+ return safe_gap;
+
+ return 0;
+}
+
/*
* Calculate bridge window head alignment that leaves no gaps in between
* resources.
@@ -1281,6 +1351,7 @@ static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res,
int order, max_order;
resource_size_t children_add_size = 0;
resource_size_t add_align = 0;
+ resource_size_t gap_size;
if (!b_res)
return;
@@ -1345,7 +1416,8 @@ static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res,
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,
+ gap_size = calculate_win_gap_size(bus, b_res, min_align, false);
+ size0 = calculate_memsize(size + gap_size, realloc_head ? 0 : add_size,
0, win_align);
if (size0) {
@@ -1355,8 +1427,9 @@ static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res,
if (realloc_head && (add_size > 0 || children_add_size > 0)) {
add_align = max(min_align, add_align);
- size1 = calculate_memsize(size, add_size, children_add_size,
- win_align);
+ gap_size = calculate_win_gap_size(bus, b_res, add_align, true);
+ size1 = calculate_memsize(size + gap_size, add_size,
+ children_add_size, win_align);
}
if (!size0 && !size1) {
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 5/5] PCI/quirks: Avoid certain BAR 0 address with igb
2026-09-23 13:17 [PATCH 0/5] PCI: Resource placement algorithm fixes Ilpo Järvinen
` (3 preceding siblings ...)
2026-09-23 13:17 ` [PATCH 4/5] PCI: Fix composite resource sizing Ilpo Järvinen
@ 2026-09-23 13:17 ` Ilpo Järvinen
4 siblings, 0 replies; 7+ messages in thread
From: Ilpo Järvinen @ 2026-09-23 13:17 UTC (permalink / raw)
To: Maciej Grochowski, Nikolas Joshua Britton, Geramy Loveless,
Eric Auger, Alexey Fomenko, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Krzysztof Wilczyński, linux-pci, linux-kernel
Cc: Ilpo Järvinen
While testing the resource placement changes, my tests hit a case where
igb fails to probe when BAR 0 is placed at 0x9c000000:
90000000-9cffffff : PCI Bus 0000:a0
- 90000000-902fffff : PCI Bus 0000:a1
- 90000000-900fffff : 0000:a1:00.0
- 90000000-900fffff : igb
- 90100000-901fffff : 0000:a1:00.0
- 90200000-90203fff : 0000:a1:00.0
- 90200000-90203fff : igb
+ 9be00000-9c0fffff : PCI Bus 0000:a1
+ 9be00000-9befffff : 0000:a1:00.0
+ 9bf00000-9bf03fff : 0000:a1:00.0
+ 9c000000-9c0fffff : 0000:a1:00.0
9c100000-9c17ffff : amd_iommu
9c180000-9c1803ff : IOAPIC 8
- Region 0: Memory at 90000000 (32-bit, non-prefetchable) [size=1M]
- Region 3: Memory at 90200000 (32-bit, non-prefetchable) [size=16K]
- Expansion ROM at 90100000 [disabled] [size=1M]
+ Region 0: Memory at 9c000000 (32-bit, non-prefetchable) [size=1M]
+ Region 3: Memory at 9bf00000 (32-bit, non-prefetchable) [size=16K]
+ Expansion ROM at 9be00000 [disabled] [size=1M]
igb 0000:a1:00.0 0000:a1:00.0 (uninitialized): PCIe link lost
------------[ cut here ]------------
igb: Failed to read reg 0x18!
WARNING: drivers/net/ethernet/intel/igb/igb_main.c:724 at igb_rd32.cold+0x3c/0x4f [igb], CPU#32: kworker/32:1/706
...
igb_get_invariants_82575+0xff/0xf00 [igb]
igb_probe+0x3c8/0x1190 [igb]
local_pci_probe+0x3b/0x80
Apparently, the igb driver bails out, after its initial sanity check
detects an unexpected ~0 read. Hacking around the sanity check just
results in more failures down the road so the sanity check itself is not
the cause for the failure.
The resource placement looks valid so the actual placement patches seem
to work normally.
All other possible 1M address I could test (with a hack patch) did work.
Add quirk to reshuffle igb resources, use BAR 3 to block the problematic
address.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
I know this is ugly and I don't like it either but do not know better
way to avoid the regression.
I've tried with iommu=off and that did not resolve the issue.
I also managed to prove igb works with the same resource layout in
another system. So identifying the case should probably be tightened
by matching with more devices than the one used by igb. This is open
to discussion.
---
drivers/pci/quirks.c | 56 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index de9bbccda21f..e6f3e2ab1fd4 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -6288,6 +6288,62 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1536, rom_bar_overlap_defect);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1537, rom_bar_overlap_defect);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1538, rom_bar_overlap_defect);
+/*
+ * The igb driver probe (due to reads returning ~0 unexpected) when BAR 0
+ * appears at 0x9c000000. The cause is unknown.
+ *
+ * Use BAR 3 to block 0x9c000000 address.
+ */
+static void bar0_address_breakage(struct pci_dev *dev)
+{
+ struct resource *bar0 = pci_resource_n(dev, 0);
+ struct resource *bar3 = pci_resource_n(dev, 3);
+ resource_size_t broken_addr = 0x9c000000;
+ struct resource *res;
+ int i, ret;
+
+ if (bar0->start != broken_addr)
+ return;
+
+ /*
+ * HW BAR sizes seems to vary. Exclude non-1M BAR 0 case and
+ * sanity check BAR 0 & 3 before attempting this quirk.
+ */
+ if (resource_type(bar0) != IORESOURCE_MEM ||
+ resource_size(bar0) != SZ_1M ||
+ resource_type(bar3) != IORESOURCE_MEM)
+ return;
+
+ pci_info(dev, "%pR: relocating BAR\n", bar0);
+
+ pci_dev_for_each_resource(dev, res, i) {
+ if (!resource_assigned(res) ||
+ resource_type(res) != IORESOURCE_MEM)
+ continue;
+
+ pci_release_resource(dev, i);
+ }
+
+ resource_set_range(bar3, broken_addr, resource_size(bar3));
+ bar3->flags &= ~IORESOURCE_UNSET;
+ pci_claim_resource(dev, 3);
+ if (!resource_assigned(bar3)) {
+ bar3->flags |= IORESOURCE_UNSET;
+ pci_warn(dev, "resource relocation failed\n");
+ }
+
+ pci_dev_for_each_resource(dev, res, i) {
+ if (resource_assigned(res) ||
+ resource_type(res) != IORESOURCE_MEM)
+ continue;
+
+ ret = pci_assign_resource(dev, i);
+ if (ret)
+ pci_warn(dev, "resource relocation failed\n");
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1533, bar0_address_breakage);
+
#ifdef CONFIG_PCIEASPM
/*
* Several Intel DG2 graphics devices advertise that they can only tolerate
--
2.47.3
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/5] resource: Mark free space assigned
2026-09-23 13:17 ` [PATCH 1/5] resource: Mark free space assigned Ilpo Järvinen
@ 2026-09-23 15:58 ` Bradley Morgan
0 siblings, 0 replies; 7+ messages in thread
From: Bradley Morgan @ 2026-09-23 15:58 UTC (permalink / raw)
To: ilpo.jarvinen
Cc: Maciej.Grochowski, alexey.fomenko, bhelgaas, eauger, gloveless,
kw, linux-kernel, lorenzo.pieralisi, nbritton, robh
On 23 September 2026 14:17:51 BST, "Ilpo Järvinen"
<ilpo.jarvinen@linux.intel.com> wrote:
>Free space ranges are (part of) assigned ranges. Since new resources
>have IORESOURCE_UNSET, the flag gets copied also to full_avail.flags,
>which hampers printing them with %pR.
>
>Clear IORESOURCE_UNSET for the free space indicator resource.
>
LGTM, thanks.
Reviewed-by: Bradley Morgan <brads@mainlining.org>
>Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
>---
> kernel/resource.c | 1 +
> 1 file changed, 1 insertion(+)
>
>diff --git a/kernel/resource.c b/kernel/resource.c
>index e60539a55541..17e7fccd859c 100644
>--- a/kernel/resource.c
>+++ b/kernel/resource.c
>@@ -731,6 +731,7 @@ static int __find_resource_space(struct resource *root, struct resource *old,
> resource_alignf alignf = constraint->alignf;
>
> full_avail.start = root->start;
>+ full_avail.flags &= ~IORESOURCE_UNSET;
> /*
> * Skip past an allocated resource that starts at 0, since the assignment
> * of this->start - 1 to full_avail->end below would cause an underflow.
>
--- Thanks!
"I'm not a very positive person" - Linus torvalds
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-23 15:58 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 13:17 [PATCH 0/5] PCI: Resource placement algorithm fixes Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 1/5] resource: Mark free space assigned Ilpo Järvinen
2026-09-23 15:58 ` Bradley Morgan
2026-09-23 13:17 ` [PATCH 2/5] PCI: Fix nesting windows with remainder at the left edge Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 3/5] PCI: Place resources to either edge of the window Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 4/5] PCI: Fix composite resource sizing Ilpo Järvinen
2026-09-23 13:17 ` [PATCH 5/5] PCI/quirks: Avoid certain BAR 0 address with igb Ilpo Järvinen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®