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: [email protected]
> Link: https://bugs.launchpad.net/ubuntu/+source/linux-hwe-7.0/+bug/2159596
> Suggested-by: Bjorn Helgaas <[email protected]>
> Suggested-by: Ilpo Järvinen <[email protected]>
> Reviewed-by: Ilpo Järvinen <[email protected]>
> Assisted-by: Claude:claude-fable-5 checkpatch
> Assisted-by: Claude:claude-sonnet-5
> Signed-off-by: Liz Fong-Jones <[email protected]>

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/[email protected]
> 
>  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
> 

Reply via email to