Re: [PATCH v6] PCI: Fix BAR resize for devices on a root bus
From: Bjorn Helgaas
Date: Fri Sep 18 2026 - 15:28:42 EST
On Fri, Sep 18, 2026 at 03:56:33AM +0000, Liz Fong-Jones wrote:
> pci_do_resource_release_and_resize() releases the device BARs that
> share a bridge window with the BAR being resized, but when the device
> sits directly on a root bus (pdev->bus->self == NULL) it then skips
> resource assignment entirely and returns success, leaving the BARs it
> just released unassigned (IORESOURCE_UNSET).
>
> Skipping pbus_reassign_bridge_resources() is correct in that case --
> there is no bridge window to adjust -- but the device BARs still have
> to be reassigned. Before the BAR release was consolidated into the PCI
> core, this case worked for amdgpu because the driver released the BARs
> itself and then called pci_assign_unassigned_bus_resources()
> unconditionally after the resize, which assigns unassigned device BARs
> also on a root bus. Commit db92e3fef53e ("drm/amdgpu: Remove driver
> side BAR release before resize") removed that call, so nothing assigns
> the released BARs anymore.
>
> This breaks amdgpu completely on the SolidRun HoneyComb LX2K (NXP
> LX2160A, arm64, ACPI), where the GPU endpoint is enumerated directly
> on the root bus of its segment (there is no root port device, so
> pdev->bus->self is NULL):
>
> amdgpu 0004:01:00.0: BAR 0 [mem 0xa400000000-0xa40fffffff 64bit pref]: releasing
> amdgpu 0004:01:00.0: BAR 2 [mem 0xa410000000-0xa4101fffff 64bit pref]: releasing
> amdgpu 0004:01:00.0: sw_init of IP block <gmc_v8_0> failed -19
> amdgpu 0004:01:00.0: amdgpu_device_ip_init failed
> amdgpu 0004:01:00.0: Fatal error during GPU init
>
> No error is logged because the resize path reports success; amdgpu
> then finds BAR0 IORESOURCE_UNSET and bails out with -ENODEV.
>
> When there is no upstream bridge, call pci_bus_assign_resources() on
> the root bus to place the BARs released above, using the same
> alignment-sorted algorithm as normal enumeration instead of a manual
> per-BAR loop. This also walks the rest of the hierarchy under the
> root bus, as pci_assign_unassigned_bus_resources() used to for amdgpu
> before commit db92e3fef53e ("drm/amdgpu: Remove driver side BAR
> release before resize") removed that call -- the core-side fix that
> commit asked for ("such a problem should be fixed inside
> pci_resize_resource() instead").
>
> pci_bus_assign_resources() returns void, so failure is detected by
> checking whether the released BARs are still assigned afterward; if
> not, roll back as in the bridged case. This is stricter than the
> bridged path -- it fails on any unplaced resource, not just required
> ones -- since a root bus typically has one shared window, and failing
> loudly seemed better than leaving something silently unassigned.
>
> The root bus path also had a locking bug that any fix here necessarily
> touches: the old "goto out" jumped to up_read(&pci_bus_sem) without a
> matching down_read() (as does the "goto restore" taken when
> pci_dev_res_add_to_list() fails in the release loop). Take pci_bus_sem
> before the BAR release loop so every path through the function holds
> it exactly once.
>
> Use pci_upstream_bridge() rather than testing pdev->bus->self
> directly. The two are usually equivalent, but pci_upstream_bridge()
> is the canonical test -- pci_is_root_bus(), which it's built on,
> warns that bus->self == NULL doesn't necessarily mean a root bus
> (SR-IOV virtual buses from virtfn_add_bus() are the same).
>
> Fixes: 337b1b566db0 ("PCI: Fix restoring BARs on BAR resize rollback path")
> Cc: stable@xxxxxxxxxxxxxxx
> Link: https://bugs.launchpad.net/ubuntu/+source/linux-hwe-7.0/+bug/2159596
> Suggested-by: Bjorn Helgaas <bhelgaas@xxxxxxxxxx>
> Suggested-by: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>
> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>
> Assisted-by: Claude:claude-fable-5 checkpatch
> Assisted-by: Claude:claude-sonnet-5
> Signed-off-by: Liz Fong-Jones <lizf@xxxxxxxxxxxx>
Applied to pci/for-linus for v7.3, thank you!
> ---
>
> Confirmed against v7.3-rc3: drivers/pci/setup-bus.c is unpatched
> there, pci_do_resource_release_and_resize() still returns without
> reassigning the released BARs when bus->self is NULL.
>
> Tested on the real root-bus hardware this fixes (SolidRun HoneyComb
> LX2K): both a manual resize via the resource0_resize sysfs attribute
> (shrink to 256M then back to 4G, forcing the release+reassign path
> both directions) and amdgpu's own natural probe-time resize (with the
> amdgpu.rebar=0 workaround removed) succeed cleanly -- VRAM and BAR
> size match, no "Fatal error during GPU init", full IP block init.
>
> Changes in v6:
> - Repost, confirmed still affects v7.3-rc3
> - Added real-hardware test confirmation (both a manual sysfs-triggered
> resize and amdgpu's own natural probe-time resize)
> - Picked up Reviewed-by from Ilpo
> - Link to v5: https://patch.msgid.link/20260908-pci-rebar-root-bus-v5-1-a210f405ea81@xxxxxxxxxxxx
>
> drivers/pci/setup-bus.c | 23 +++++++++++++++++------
> 1 file changed, 17 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
> index e8c94aa1d3c12..ed16ef7c26fa7 100644
> --- a/drivers/pci/setup-bus.c
> +++ b/drivers/pci/setup-bus.c
> @@ -2380,6 +2380,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
> struct resource *res = pci_resource_n(pdev, resno);
> struct pci_dev_resource *dev_res;
> struct pci_bus *bus = pdev->bus;
> + struct pci_dev *bridge = pci_upstream_bridge(pdev);
> struct resource *b_win, *r;
> LIST_HEAD(saved);
> unsigned int i;
> @@ -2397,6 +2398,8 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
> if (ret)
> return ret;
>
> + down_read(&pci_bus_sem);
> +
> pci_dev_for_each_resource(pdev, r, i) {
> if (i >= PCI_BRIDGE_RESOURCES)
> break;
> @@ -2415,13 +2418,21 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
>
> pci_resize_resource_set_size(pdev, resno, size);
>
> - if (!bus->self)
> - goto out;
> + if (bridge) {
> + ret = pbus_reassign_bridge_resources(bus, res, &saved);
> + if (ret)
> + goto restore;
> + } else {
> + /* No bridge window to adjust; let the core reassign the bus. */
> + pci_bus_assign_resources(bus);
>
> - down_read(&pci_bus_sem);
> - ret = pbus_reassign_bridge_resources(bus, res, &saved);
> - if (ret)
> - goto restore;
> + list_for_each_entry(dev_res, &saved, list) {
> + if (!resource_assigned(dev_res->res)) {
> + ret = -ENOSPC;
> + goto restore;
> + }
> + }
> + }
>
> out:
> up_read(&pci_bus_sem);
> --
> 2.53.0
>