[PATCH] PCI: release empty sibling resources during bridge window resize

Geramy Loveless
2026-04-08T22:32:10Z

When pci_resize_resource() walks up the bridge hierarchy via
pbus_reassign_bridge_resources(), bridge windows with any child
resources are refused release. This prevents BAR resize on devices
behind multi-port PCIe switches (such as Thunderbolt docks) where
empty sibling downstream ports hold small reservations that block the
parent bridge window from being freed and re-sized.

Add pci_bus_release_bridge_resources_safe() which verifies that a
resource subtree contains no active children before releasing it,
and use it in pbus_reassign_bridge_resources() to clear empty sibling
reservations so the bridge window can grow.

Signed-off-by: Geramy Loveless <gloveless@jqluv.com>
---
drivers/pci/setup-bus.c | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 4cf120ebe..9a3a23819 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -2297,6 +2297,30 @@
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
* release it when possible. If the bridge window contains assigned
* resources, it cannot be released.
*/
+
+/*
+ * Release child resources from a bridge window so it can be freed and
+ * re-sized, but only if the entire subtree is empty (no active device
+ * resources underneath). Walks the resource tree recursively to handle
+ * arbitrarily deep bridge hierarchies. Returns true if the resources
+ * were released or the window was already empty.
+ */
+static bool pci_bus_release_bridge_resources_safe(struct pci_bus *bus,
+ struct resource *res)
+{
+ struct resource *child;
+
+ for (child = res->child; child; child = child->sibling) {
+ if (child->child &&
+ !pci_bus_release_bridge_resources_safe(bus, child))
+ return false;
+ }
+
+ if (res->child)
+ pci_bus_release_bridge_resources(bus, res, whole_subtree);
+ return true;
+}
+
static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct
resource *res,
struct list_head *saved)
{
@@ -2316,8 +2340,8 @@ static int pbus_reassign_bridge_resources(struct
pci_bus *bus, struct resource *
i = pci_resource_num(bridge, res);
- /* Ignore BARs which are still in use */
- if (!res->child) {
+ if (!res->child ||
+ pci_bus_release_bridge_resources_safe(bridge->subordinate, res)) {
ret = pci_dev_res_add_to_list(saved, bridge, res, 0, 0);
if (ret)
return ret;
@@ -2327,7 +2351,7 @@ static int pbus_reassign_bridge_resources(struct
pci_bus *bus, struct resource *
const char *res_name = pci_resource_name(bridge, i);
pci_warn(bridge,
- "%s %pR: was not released (still contains assigned resources)\n",
+ "%s %pR: not released, active children present\n",
res_name, res);
}
--
2.51.0