* [PATCH v2 0/2] PCI: Allow non-prefetchable BARs in prefetchable windows
@ 2026-09-25 16:47 Ard Biesheuvel
2026-09-25 16:47 ` [PATCH v2 1/2] PCI: Add __pci_bus_alloc_resource() to allocate with explicit flags Ard Biesheuvel
2026-09-25 16:47 ` [PATCH v2 2/2] PCI: Allow non-prefetchable BARs in prefetchable windows Ard Biesheuvel
0 siblings, 2 replies; 3+ messages in thread
From: Ard Biesheuvel @ 2026-09-25 16:47 UTC (permalink / raw)
To: 0001-PCI-Tolerate-non-prefetchable-64-bit-BARs-in-prefetc.patch
Cc: linux-kernel, Ard Biesheuvel, Bjorn Helgaas, Ilpo Järvinen,
Lorenzo Pieralisi
From: Ard Biesheuvel <ardb@kernel.org>
The non-prefetchable window of a PCI-to-PCI bridge can only decode
32-bit addresses, and so even 64-bit non-prefetchable BARs of devices
below a bridge compete for the scarce MMIO space below 4 GB.
The PCIe spec notes that marking such a BAR prefetchable still permits
correct operation as long as the entire path from the host to the device
is PCIe (PCIe r7.0, sec 7.5.1.2.1). The same applies when the OS places
a non-prefetchable BAR in a prefetchable bridge window, and so this
series permits that unless there is a bridge on the path to the root bus
that is not a PCIe Root Port or Switch Port, allowing 64-bit
non-prefetchable BARs to be placed above 4 GB.
How a host bridge treats the windows that firmware describes as
prefetchable is platform specific, however, and so this is only done if
the host bridge has no prefetchable windows at all, in which case all
prefetchable bridge windows are carved out of non-prefetchable host
bridge windows. This also means that devices on a root bus are never
affected.
Patch #1 is a preparatory refactor of pci_bus_alloc_resource() with no
functional change, and patch #2 implements the relaxation.
The series is based on pci/for-linus, as patch #2 touches the same
lines in pci_do_resource_release_and_resize() as commit d58384c22739
("PCI: Fix BAR resize for devices on a root bus") queued there for
v7.3.
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
Cc: Lorenzo Pieralisi <lpieralisi@kernel.org>
Link: https://lore.kernel.org/linux-pci/20260910143440.3865663-2-ardb+git@google.com/
Ard Biesheuvel (2):
PCI: Add __pci_bus_alloc_resource() to allocate with explicit flags
PCI: Allow non-prefetchable BARs in prefetchable windows
drivers/pci/bus.c | 70 ++++++++++++++++--------
drivers/pci/pci.c | 115 +++++++++++++++++++++++++++++++++++++++-
drivers/pci/pci.h | 11 +++-
drivers/pci/setup-bus.c | 58 +++++++++++++-------
drivers/pci/setup-res.c | 29 ++++++----
5 files changed, 228 insertions(+), 55 deletions(-)
base-commit: 9a8daf68b9b30c536b0991d9f098ed0db80abc1c
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v2 1/2] PCI: Add __pci_bus_alloc_resource() to allocate with explicit flags
2026-09-25 16:47 [PATCH v2 0/2] PCI: Allow non-prefetchable BARs in prefetchable windows Ard Biesheuvel
@ 2026-09-25 16:47 ` Ard Biesheuvel
2026-09-25 16:47 ` [PATCH v2 2/2] PCI: Allow non-prefetchable BARs in prefetchable windows Ard Biesheuvel
1 sibling, 0 replies; 3+ messages in thread
From: Ard Biesheuvel @ 2026-09-25 16:47 UTC (permalink / raw)
To: 0001-PCI-Tolerate-non-prefetchable-64-bit-BARs-in-prefetc.patch
Cc: linux-kernel, Ard Biesheuvel, Bjorn Helgaas, Ilpo Järvinen,
Lorenzo Pieralisi
From: Ard Biesheuvel <ardb@kernel.org>
pci_bus_alloc_resource() decides which bus resources a resource may be
allocated from based on the flags of that resource: the type bits
selected by @type_mask must match, a non-prefetchable resource is never
allocated from a prefetchable bus resource, and only a 64-bit resource
may be placed above 4 GB.
In preparation for placing some non-prefetchable BARs in prefetchable
bridge windows, factor out __pci_bus_alloc_resource(), which takes the
flags to match against the bus resources as a separate argument, and
implement pci_bus_alloc_resource() on top of it by passing the flags of
the resource itself.
No functional change intended.
Assisted-by: LLM sparse
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
drivers/pci/bus.c | 70 ++++++++++++++++++++++++++++++++---------------
drivers/pci/pci.h | 6 ++++
2 files changed, 54 insertions(+), 22 deletions(-)
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 655ed53436..65a54d6e04 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -188,9 +188,9 @@ static void pci_clip_resource_to_region(struct pci_bus *bus,
}
static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
- resource_size_t size, resource_size_t align,
- resource_size_t min, unsigned long type_mask,
- resource_alignf alignf,
+ unsigned long flags, resource_size_t size,
+ resource_size_t align, resource_size_t min,
+ unsigned long type_mask, resource_alignf alignf,
void *alignf_data,
struct pci_bus_region *region)
{
@@ -210,13 +210,13 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
continue;
/* type_mask must match */
- if ((res->flags ^ r->flags) & type_mask)
+ if ((flags ^ r->flags) & type_mask)
continue;
/* We cannot allocate a non-prefetching resource
from a pre-fetching area */
if ((r->flags & IORESOURCE_PREFETCH) &&
- !(res->flags & IORESOURCE_PREFETCH))
+ !(flags & IORESOURCE_PREFETCH))
continue;
avail = *r;
@@ -247,9 +247,10 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
}
/**
- * pci_bus_alloc_resource - allocate a resource from a parent bus
+ * __pci_bus_alloc_resource - allocate a resource from a parent bus
* @bus: PCI bus
* @res: resource to allocate
+ * @flags: resource flags to match against the bus resources
* @size: size of resource to allocate
* @align: alignment of resource to allocate
* @min: minimum /proc/iomem address to allocate
@@ -257,36 +258,61 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
* @alignf: resource alignment function
* @alignf_data: data argument for resource alignment function
*
- * Given the PCI bus a device resides on, the size, minimum address,
- * alignment and type, try to find an acceptable resource allocation
- * for a specific device resource.
+ * Like pci_bus_alloc_resource(), but match the bus resources against @flags
+ * rather than @res->flags. This permits allocating a non-prefetchable
+ * resource from a prefetchable bus resource by passing @flags with
+ * IORESOURCE_PREFETCH set, which the caller must have established to be safe.
*/
-int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
- resource_size_t size, resource_size_t align,
- resource_size_t min, unsigned long type_mask,
- resource_alignf alignf,
- void *alignf_data)
+int __pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
+ unsigned long flags, resource_size_t size,
+ resource_size_t align, resource_size_t min,
+ unsigned long type_mask, resource_alignf alignf,
+ void *alignf_data)
{
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
int rc;
- if (res->flags & IORESOURCE_MEM_64) {
- rc = pci_bus_alloc_from_region(bus, res, size, align, min,
- type_mask, alignf, alignf_data,
- &pci_high);
+ if (flags & IORESOURCE_MEM_64) {
+ rc = pci_bus_alloc_from_region(bus, res, flags, size, align,
+ min, type_mask, alignf,
+ alignf_data, &pci_high);
if (rc == 0)
return 0;
- return pci_bus_alloc_from_region(bus, res, size, align, min,
- type_mask, alignf, alignf_data,
- &pci_64_bit);
+ return pci_bus_alloc_from_region(bus, res, flags, size, align,
+ min, type_mask, alignf,
+ alignf_data, &pci_64_bit);
}
#endif
- return pci_bus_alloc_from_region(bus, res, size, align, min,
+ return pci_bus_alloc_from_region(bus, res, flags, size, align, min,
type_mask, alignf, alignf_data,
&pci_32_bit);
}
+
+/**
+ * pci_bus_alloc_resource - allocate a resource from a parent bus
+ * @bus: PCI bus
+ * @res: resource to allocate
+ * @size: size of resource to allocate
+ * @align: alignment of resource to allocate
+ * @min: minimum /proc/iomem address to allocate
+ * @type_mask: IORESOURCE_* type flags
+ * @alignf: resource alignment function
+ * @alignf_data: data argument for resource alignment function
+ *
+ * Given the PCI bus a device resides on, the size, minimum address,
+ * alignment and type, try to find an acceptable resource allocation
+ * for a specific device resource.
+ */
+int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
+ resource_size_t size, resource_size_t align,
+ resource_size_t min, unsigned long type_mask,
+ resource_alignf alignf, void *alignf_data)
+{
+ return __pci_bus_alloc_resource(bus, res, res->flags, size, align, min,
+ type_mask, alignf, alignf_data);
+}
EXPORT_SYMBOL(pci_bus_alloc_resource);
/*
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fdddd..8297cfb5dc 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -567,6 +567,12 @@ static inline int pci_resource_num(const struct pci_dev *dev,
return resno;
}
+int __pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
+ unsigned long flags, resource_size_t size,
+ resource_size_t align, resource_size_t min,
+ unsigned long type_mask, resource_alignf alignf,
+ void *alignf_data);
+
void pbus_validate_busn(struct pci_bus *bus);
struct resource *pbus_select_window(struct pci_bus *bus,
const struct resource *res);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v2 2/2] PCI: Allow non-prefetchable BARs in prefetchable windows
2026-09-25 16:47 [PATCH v2 0/2] PCI: Allow non-prefetchable BARs in prefetchable windows Ard Biesheuvel
2026-09-25 16:47 ` [PATCH v2 1/2] PCI: Add __pci_bus_alloc_resource() to allocate with explicit flags Ard Biesheuvel
@ 2026-09-25 16:47 ` Ard Biesheuvel
1 sibling, 0 replies; 3+ messages in thread
From: Ard Biesheuvel @ 2026-09-25 16:47 UTC (permalink / raw)
To: 0001-PCI-Tolerate-non-prefetchable-64-bit-BARs-in-prefetc.patch
Cc: linux-kernel, Ard Biesheuvel, Bjorn Helgaas, Ilpo Järvinen,
Lorenzo Pieralisi
From: Ard Biesheuvel <ardb@kernel.org>
The non-prefetchable memory window of a PCI-to-PCI bridge can only
decode 32-bit addresses, and so non-prefetchable BARs of devices below a
bridge can only be allocated from the part of the host bridge memory
space below 4 GB. This is the case even for 64-bit BARs, which could
easily be placed above 4 GB if there was a bridge window to put them in,
and on many platforms, 32-bit addressable MMIO space is scarce.
The PCIe spec addresses this in the implementation note "Additional
Guidance on the Prefetchable Bit in Memory Space BARs" (PCIe r7.0, sec
7.5.1.2.1): on PCIe, setting the Prefetchable bit of a BAR still permits
correct operation even if the range has read side effects or cannot
tolerate write merging, as long as the entire path from the host to the
device is PCIe, given that PCIe Memory Reads always carry an explicit
length, and PCIe Switches never prefetch or merge writes. The same
reasoning applies when it is the OS that places a non-prefetchable BAR
in a prefetchable bridge window: PCIe Root Ports and Switch Ports
forward requests that hit either window in exactly the same way.
This reasoning does not extend to the host bridge, though: how it treats
the windows that firmware describes as prefetchable is platform
specific. For instance, the V3 Semiconductor V360EPC (pci-v3-semi)
enables prefetching for its prefetchable window, the MPC52xx uses Memory
Read Multiple for it, and Freescale PCI/PCIe host bridges (fsl_pci)
enable relaxed ordering for it. However, if the host bridge has no
prefetchable windows at all (as appears to be the case with many x86
PCs), all prefetchable bridge windows are carved out of its
non-prefetchable windows, and so the host bridge does not treat them any
differently.
Note that this only concerns where a BAR is placed. How it is mapped is
decided by the driver and by the attributes of the BAR itself (e.g.,
pci_iomap_wc() and the sysfs resource<N>_wc files only honour
IORESOURCE_PREFETCH on the BAR), and this change does not modify the
flags of any BAR.
So add pci_resource_placement_flags(), which returns the flags of a
device resource with IORESOURCE_PREFETCH set if it is a non-prefetchable
memory BAR (including SR-IOV VF BARs), unless there is a bridge between
the device and the root bus that is not a PCIe Root Port or PCIe Switch
Port, or the host bridge has a prefetchable memory window, and use it
wherever the resource allocator decides which bridge window a device
resource belongs in:
- when sizing bridge windows, via pbus_select_window(), which now takes
the device rather than its bus;
- when allocating the resource in __pci_assign_resource(), by passing
its result to __pci_bus_alloc_resource();
- when claiming a resource assigned by firmware, in
pci_find_parent_resource();
- when releasing bridge windows to retry failed assignments, or to
resize a BAR.
As a result, an eligible non-prefetchable BAR is handled exactly like a
prefetchable BAR of the same width: a 64-bit BAR is placed in the 64-bit
prefetchable window of the upstream bridge, which may be above 4 GB,
while a 32-bit BAR only ends up in the prefetchable window if that
window is 32-bit only. As before, allocation falls back to the
non-prefetchable window if the prefetchable one has no space.
Bridge windows are not affected, and neither are devices below
conventional PCI or CardBus bridges, or below PCIe to PCI/PCI-X bridges
(in either direction), or below host bridges that have a prefetchable
window. Devices on a root bus are not affected either, as there is no
prefetchable window for their BARs to go to.
Tested on QEMU arm64 'virt' (DT, all resources assigned by Linux) with a
qemu-xhci below a Root Port, an NVMe below a Switch, a qemu-xhci below a
PCIe-to-PCI bridge and another qemu-xhci on the root bus: the 64-bit
non-prefetchable BARs of the first two (and of the PCIe-to-PCI bridge
itself) move from the 32-bit non-prefetchable windows into the 64-bit
prefetchable windows above 4 GB, while the others stay where they were.
When the 64-bit host bridge window is marked prefetchable in the DT, all
resources are assigned exactly as without this patch. Both drivers
work, also after hot removal and rescan of the endpoints and of the
Switch. Also tested on QEMU x86_64 q35 with SeaBIOS, which assigns all
resources itself: the firmware assignment is claimed as before, and
after hot removal and rescan, the eligible BARs are placed in the
prefetchable windows.
Assisted-by: LLM sparse
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
drivers/pci/pci.c | 115 +++++++++++++++++++++++++++++++++++++++-
drivers/pci/pci.h | 5 +-
drivers/pci/setup-bus.c | 58 +++++++++++++-------
drivers/pci/setup-res.c | 29 ++++++----
4 files changed, 174 insertions(+), 33 deletions(-)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b2879a6be5..dfaeb3104b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -736,6 +736,111 @@ static bool pci_dev_config_accessible(struct pci_dev *dev, char *msg)
return true;
}
+/*
+ * Return true if there is a bridge between @bus and the root bus that is not a
+ * PCIe Root Port or PCIe Switch Port, such as a conventional PCI or CardBus
+ * bridge, or a PCIe to PCI/PCI-X bridge in either direction.
+ */
+static bool pci_bus_behind_non_pcie_port(struct pci_bus *bus)
+{
+ for (; !pci_is_root_bus(bus); bus = bus->parent) {
+ struct pci_dev *bridge = bus->self;
+
+ /* Virtual buses for SR-IOV VFs have no bridge of their own */
+ if (!bridge)
+ continue;
+
+ if (!pci_is_pcie(bridge))
+ return true;
+
+ switch (pci_pcie_type(bridge)) {
+ case PCI_EXP_TYPE_ROOT_PORT:
+ case PCI_EXP_TYPE_UPSTREAM:
+ case PCI_EXP_TYPE_DOWNSTREAM:
+ break;
+ default:
+ /* PCIe to PCI/PCI-X bridges, in either direction */
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Return true if the host bridge above @bus has a prefetchable memory window.
+ */
+static bool pci_host_bridge_has_pref_window(struct pci_bus *bus)
+{
+ struct resource *r;
+
+ while (!pci_is_root_bus(bus))
+ bus = bus->parent;
+
+ pci_bus_for_each_resource(bus, r) {
+ if (r && resource_type(r) == IORESOURCE_MEM &&
+ (r->flags & IORESOURCE_PREFETCH))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * pci_resource_placement_flags - Get the flags to use for placing a resource
+ * @dev: PCI device
+ * @res: Resource of @dev
+ * @flags: Resource flags of @res (which may have been cleared in @res itself)
+ *
+ * Non-prefetchable memory BARs are normally only placed in non-prefetchable
+ * bridge windows. However, the PCIe spec notes that setting the Prefetchable
+ * bit of a BAR permits correct operation even if the range has read side
+ * effects or cannot tolerate write merging, as long as the entire path from
+ * the host to the device is PCIe: PCIe Memory Reads always carry an explicit
+ * length, and PCIe Switches never prefetch or merge writes (PCIe r7.0, sec
+ * 7.5.1.2.1, Implementation Note "Additional Guidance on the Prefetchable Bit
+ * in Memory Space BARs").
+ *
+ * So treat non-prefetchable memory BARs of @dev as prefetchable when choosing,
+ * sizing and allocating bridge windows, unless there is a bridge between @dev
+ * and the root bus that is not a PCIe Root Port or PCIe Switch Port, such as a
+ * conventional PCI bridge, which may prefetch from its prefetchable window.
+ * This permits 64-bit non-prefetchable BARs to be placed in a 64-bit
+ * prefetchable window, which may be above 4GB, rather than competing for space
+ * in the non-prefetchable window of a PCI-to-PCI bridge, which is always below
+ * 4GB. This only affects where a BAR is placed, not how it is mapped.
+ *
+ * How a host bridge treats the windows that firmware describes as prefetchable
+ * is platform specific, however, and some prefetch from them, or relax the
+ * ordering of accesses to them. So only do this if the host bridge has no
+ * prefetchable windows at all, in which case all prefetchable bridge windows
+ * are carved out of its non-prefetchable windows. As a consequence, the
+ * non-prefetchable BARs of devices on a root bus are never placed in a
+ * prefetchable window. Bridge windows are never treated as prefetchable.
+ *
+ * Return: @flags, with IORESOURCE_PREFETCH set if @res may be placed in a
+ * prefetchable bridge window.
+ */
+unsigned long pci_resource_placement_flags(const struct pci_dev *dev,
+ const struct resource *res,
+ unsigned long flags)
+{
+ if ((flags & (IORESOURCE_TYPE_BITS | IORESOURCE_PREFETCH)) !=
+ IORESOURCE_MEM)
+ return flags;
+
+ if (pci_resource_is_bridge_win(pci_resource_num(dev, res)))
+ return flags;
+
+ if (pci_bus_behind_non_pcie_port(dev->bus))
+ return flags;
+
+ if (pci_host_bridge_has_pref_window(dev->bus))
+ return flags;
+
+ return flags | IORESOURCE_PREFETCH;
+}
+
/**
* pci_find_parent_resource - return resource region of parent bus of given
* region
@@ -749,8 +854,13 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
struct resource *res)
{
const struct pci_bus *bus = dev->bus;
+ unsigned long flags = res->flags;
struct resource *r;
+ /* @res is not necessarily one of @dev's resources */
+ if (res >= &dev->resource[0] && res < &dev->resource[PCI_NUM_RESOURCES])
+ flags = pci_resource_placement_flags(dev, res, flags);
+
pci_bus_for_each_resource(bus, r) {
if (!r)
continue;
@@ -758,10 +868,11 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
/*
* If the window is prefetchable but the BAR is
- * not, the allocator made a mistake.
+ * not (and may not be treated as such), the
+ * allocator made a mistake.
*/
if (r->flags & IORESOURCE_PREFETCH &&
- !(res->flags & IORESOURCE_PREFETCH))
+ !(flags & IORESOURCE_PREFETCH))
return NULL;
/*
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 8297cfb5dc..3fdb1fbd41 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -567,6 +567,9 @@ static inline int pci_resource_num(const struct pci_dev *dev,
return resno;
}
+unsigned long pci_resource_placement_flags(const struct pci_dev *dev,
+ const struct resource *res,
+ unsigned long flags);
int __pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
unsigned long flags, resource_size_t size,
resource_size_t align, resource_size_t min,
@@ -574,7 +577,7 @@ int __pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
void *alignf_data);
void pbus_validate_busn(struct pci_bus *bus);
-struct resource *pbus_select_window(struct pci_bus *bus,
+struct resource *pbus_select_window(const struct pci_dev *dev,
const struct resource *res);
void pci_reassigndev_resource_alignment(struct pci_dev *dev);
void pci_disable_bridge_window(struct pci_dev *dev);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index ed16ef7c26..e700a39878 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -253,15 +253,17 @@ static struct resource *pbus_select_window_for_type(struct pci_bus *bus,
/**
* pbus_select_window - Select bridge window for a resource
- * @bus: PCI bus
+ * @dev: PCI device owning @res
* @res: Resource
*
- * Select the bridge window for @res. If the resource is already assigned,
- * return the current bridge window.
+ * Select the bridge window on the bus of @dev for @res. If the resource is
+ * already assigned, return the current bridge window.
*
* For memory resources, the selection is done as follows:
*
- * Any non-prefetchable resource is put into the non-prefetchable window.
+ * Any non-prefetchable resource is put into the non-prefetchable window,
+ * unless it may be treated as prefetchable according to
+ * pci_resource_placement_flags().
*
* If there is no prefetchable MMIO window, put all memory resources into the
* non-prefetchable window.
@@ -274,13 +276,16 @@ static struct resource *pbus_select_window_for_type(struct pci_bus *bus,
*
* Return: the bridge window resource or NULL if no bridge window is found.
*/
-struct resource *pbus_select_window(struct pci_bus *bus,
+struct resource *pbus_select_window(const struct pci_dev *dev,
const struct resource *res)
{
+ unsigned long flags;
+
if (resource_assigned(res))
return res->parent;
- return pbus_select_window_for_type(bus, res->flags);
+ flags = pci_resource_placement_flags(dev, res, res->flags);
+ return pbus_select_window_for_type(dev->bus, flags);
}
static bool pdev_resources_assignable(struct pci_dev *dev)
@@ -514,6 +519,13 @@ static void assign_requested_resources_sorted(struct list_head *head,
}
}
+/* Get the placement flags of a tracked resource */
+static unsigned long pci_dev_res_placement_flags(struct pci_dev_resource *dev_res)
+{
+ return pci_resource_placement_flags(dev_res->dev, dev_res->res,
+ dev_res->flags);
+}
+
static unsigned long pci_fail_res_type_mask(struct list_head *fail_head)
{
struct pci_dev_resource *fail_res;
@@ -521,7 +533,7 @@ static unsigned long pci_fail_res_type_mask(struct list_head *fail_head)
/* Check failed type */
list_for_each_entry(fail_res, fail_head, list)
- mask |= fail_res->flags;
+ mask |= pci_dev_res_placement_flags(fail_res);
/*
* One pref failed resource will set IORESOURCE_MEM, as we can
@@ -564,8 +576,9 @@ static bool pci_required_resource_failed(struct list_head *fail_head,
list_for_each_entry(fail_res, fail_head, list) {
int idx = pci_resource_num(fail_res->dev, fail_res->res);
+ unsigned long flags = pci_dev_res_placement_flags(fail_res);
- if (type && (fail_res->flags & PCI_RES_TYPE_MASK) != type)
+ if (type && (flags & PCI_RES_TYPE_MASK) != type)
continue;
if (!pci_resource_is_optional(fail_res->dev, idx))
@@ -1307,7 +1320,7 @@ static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res,
if (!pdev_resources_assignable(dev) ||
!pdev_resource_should_fit(dev, r))
continue;
- if (b_res != pbus_select_window(bus, r))
+ if (b_res != pbus_select_window(dev, r))
continue;
align = pci_resource_alignment(dev, r);
@@ -1942,7 +1955,7 @@ static void remove_dev_resources(struct pci_dev *dev,
int idx;
pci_dev_for_each_resource(dev, res) {
- b_win = pbus_select_window(dev->bus, res);
+ b_win = pbus_select_window(dev, res);
if (!b_win)
continue;
@@ -2158,10 +2171,11 @@ static void pci_prepare_next_assign_round(struct list_head *fail_head,
* enough to contain child device resources.
*/
list_for_each_entry(fail_res, fail_head, list) {
+ unsigned long flags = pci_dev_res_placement_flags(fail_res);
struct pci_bus *bus = fail_res->dev->bus;
struct resource *b_win;
- b_win = pbus_select_window_for_type(bus, fail_res->flags);
+ b_win = pbus_select_window_for_type(bus, flags);
if (!b_win)
continue;
pci_bus_release_bridge_resources(bus, b_win, rel_type);
@@ -2301,16 +2315,18 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
/*
- * Walk to the root bus, find the bridge window relevant for @res and
- * release it when possible. If the bridge window contains assigned
+ * Walk to the root bus, find the bridge window relevant for @res of @pdev
+ * and release it when possible. If the bridge window contains assigned
* resources, it cannot be released.
*/
-static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res,
+static int pbus_reassign_bridge_resources(struct pci_dev *pdev,
+ struct resource *res,
struct list_head *saved)
{
- unsigned long type = res->flags;
+ unsigned long type = pci_resource_placement_flags(pdev, res, res->flags);
+ struct pci_dev *bridge = NULL, *owner = pdev;
+ struct pci_bus *bus = pdev->bus;
struct pci_dev_resource *dev_res;
- struct pci_dev *bridge = NULL;
LIST_HEAD(add_list);
LIST_HEAD(failed);
unsigned int i;
@@ -2318,7 +2334,7 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
while (!pci_is_root_bus(bus)) {
bridge = bus->self;
- res = pbus_select_window(bus, res);
+ res = pbus_select_window(owner, res);
if (!res)
break;
@@ -2339,6 +2355,8 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
res_name, res);
}
+ /* @res is now a window of @bridge */
+ owner = bridge;
bus = bus->parent;
}
@@ -2386,7 +2404,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
unsigned int i;
int old, ret;
- b_win = pbus_select_window(bus, res);
+ b_win = pbus_select_window(pdev, res);
if (!b_win)
return -EINVAL;
@@ -2407,7 +2425,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
if (exclude_bars & BIT(i))
continue;
- if (b_win != pbus_select_window(bus, r))
+ if (b_win != pbus_select_window(pdev, r))
continue;
ret = pci_dev_res_add_to_list(&saved, pdev, r, 0, 0);
@@ -2419,7 +2437,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
pci_resize_resource_set_size(pdev, resno, size);
if (bridge) {
- ret = pbus_reassign_bridge_resources(bus, res, &saved);
+ ret = pbus_reassign_bridge_resources(pdev, res, &saved);
if (ret)
goto restore;
} else {
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 376f09630a..0aea68ec45 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -315,11 +315,19 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
int resno, resource_size_t size, resource_size_t align)
{
struct resource *res = pci_resource_n(dev, resno);
+ unsigned long flags;
resource_size_t min;
int ret;
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
+ /*
+ * A non-prefetchable BAR may be placed as if it were prefetchable
+ * (see pci_resource_placement_flags()), in which case any upstream
+ * bridge windows were sized accordingly, so use the same flags here.
+ */
+ flags = pci_resource_placement_flags(dev, res, 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
@@ -327,9 +335,9 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
* 64-bit window will contain no 32-bit resources. If we assign
* things differently than they were sized, not everything will fit.
*/
- ret = pci_bus_alloc_resource(bus, res, size, align, min,
- IORESOURCE_PREFETCH | IORESOURCE_MEM_64,
- pcibios_align_resource, dev);
+ ret = __pci_bus_alloc_resource(bus, res, flags, size, align, min,
+ IORESOURCE_PREFETCH | IORESOURCE_MEM_64,
+ pcibios_align_resource, dev);
if (ret == 0)
return 0;
@@ -337,11 +345,11 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
* If the prefetchable window is only 32 bits wide, we can put
* 64-bit prefetchable resources in it.
*/
- if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) ==
+ if ((flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) ==
(IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) {
- ret = pci_bus_alloc_resource(bus, res, size, align, min,
- IORESOURCE_PREFETCH,
- pcibios_align_resource, dev);
+ ret = __pci_bus_alloc_resource(bus, res, flags, size, align,
+ min, IORESOURCE_PREFETCH,
+ pcibios_align_resource, dev);
if (ret == 0)
return 0;
}
@@ -352,9 +360,10 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
* non-prefetchable, the first call already tried the only possibility
* so we don't need to try again.
*/
- if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))
- ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
- pcibios_align_resource, dev);
+ if (flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))
+ ret = __pci_bus_alloc_resource(bus, res, flags, size, align,
+ min, 0, pcibios_align_resource,
+ dev);
return ret;
}
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-25 16:48 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-25 16:47 [PATCH v2 0/2] PCI: Allow non-prefetchable BARs in prefetchable windows Ard Biesheuvel
2026-09-25 16:47 ` [PATCH v2 1/2] PCI: Add __pci_bus_alloc_resource() to allocate with explicit flags Ard Biesheuvel
2026-09-25 16:47 ` [PATCH v2 2/2] PCI: Allow non-prefetchable BARs in prefetchable windows Ard Biesheuvel
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®