Linux PCI subsystem development
 help / color / mirror / Atom feed
* [PATCH v3 RESEND] PCI: release empty sibling bridge windows during window resize
@ 2026-04-28 22:55 Geramy Loveless
  2026-04-29 10:34 ` Ilpo Järvinen
  0 siblings, 1 reply; 2+ messages in thread
From: Geramy Loveless @ 2026-04-28 22:55 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: Cristian Cocos, Christian König, linux-pci, Geramy Loveless

pbus_reassign_bridge_resources() refuses to release bridge windows that
have child resources. On multi-port PCIe switches (e.g. Thunderbolt
docks), empty sibling downstream ports hold small reservations that
prevent the parent window from being freed and re-sized for rebar.

Walk descendants of the target window depth-first, saving and freeing
each empty bridge window so the parent can then be released and grown.

Without this change, attempts to grow a window deep below a switch
fabric leave nested empty windows pinning the ancestor:

  pcieport 0000:65:00.0: bridge window [mem 0x8880000000-0x98800fffff
    64bit pref]: not released, active children present
  pcieport 0000:00:03.2: bridge window [mem 0x8880000000-0x98800fffff
    64bit pref]: not released, active children present

With the bottom-up walk and upstream ancestry check, the chain unwinds
and the rebar succeeds:

  pcieport 0000:96:00.0: bridge window [...]: releasing
  pcieport 0000:95:00.0: bridge window [...]: releasing
  pcieport 0000:94:00.0: bridge window [...]: releasing
  pcieport 0000:65:00.0: bridge window [...]: releasing
  amdgpu 0000:97:00.0: BAR 0 [mem 0x9000000000-0x97ffffffff 64bit pref]:
    assigned

Suggested-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Geramy Loveless <gloveless@jqluv.com>
---
RESEND: my earlier v3 (Message-ID 20260428225146.3104063-1-gloveless@jqluv.com)
was sent with stale code — the diff was byte-identical to v2 because the
v3 edits weren't staged before commit --amend. This resend contains the
actual v3 content described below. Apologies for the noise.

v3:
 - Restructure helper: outer loop only recurses; inner iterates bus
   resources via pci_bus_for_each_resource(). The release path is now
   unified, so the caller-side block in pbus_reassign_bridge_resources()
   is gone.
 - Walk the resource tree upstream when checking ancestry of @b_win,
   so descendants in nested topologies are also released (Ilpo).
 - Drop pci_resource_is_bridge_win() filter; bus resources are
   inherently bridge windows.
 - Rename pci_bus_release_empty_bridge_resources() to
   pbus_release_empty_bridge_resources().
 - Drop misleading "Uses PCI bus/device iterators..." paragraph from
   commit message.
 - Kerneldoc tweaks per Ilpo's review.

v2:
 - Use PCI bus/device iterators instead of walking raw resource tree.
 - Save released resources to rollback list.
 - Filter via pci_resource_is_bridge_win() (now removed in v3).
 - Call release before the !res->child check.
 - Check bridge->subordinate before recursion.
---
 drivers/pci/setup-bus.c | 78 ++++++++++++++++++++++++++++++++---------
 1 file changed, 61 insertions(+), 17 deletions(-)

diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 4cf120ebe5a..b47dc1a2925 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -2292,6 +2292,63 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
 }
 EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
 
+/*
+ * pbus_release_empty_bridge_resources - Bottom-up release of bridge windows
+ * in empty subtrees.
+ * @bus: PCI bus whose child bridges to process
+ * @b_win: Ancestor bridge window; only resources that are (grand)parented
+ *	   by @b_win are released
+ * @saved: List to store released resources into for rollback
+ *
+ * Recurses depth-first into subordinate buses, then releases bridge windows
+ * that are (grand)parented by @b_win on the way back up. Each resource is
+ * stored into @saved before release so the entire operation can be rolled
+ * back.
+ */
+static void pbus_release_empty_bridge_resources(struct pci_bus *bus,
+						struct resource *b_win,
+						struct list_head *saved)
+{
+	struct pci_dev *dev;
+	struct resource *r;
+	unsigned int i;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		if (dev->subordinate)
+			pbus_release_empty_bridge_resources(dev->subordinate,
+							    b_win, saved);
+	}
+
+	pci_bus_for_each_resource(bus, r, i) {
+		struct resource *p;
+		unsigned int idx;
+
+		if (!r || !resource_assigned(r))
+			continue;
+
+		/* Walk upstream to confirm r descends from (or equals) b_win */
+		for (p = r; p && p != b_win; p = p->parent)
+			;
+		if (!p)
+			continue;
+
+		idx = pci_resource_num(bus->self, r);
+
+		if (r->child) {
+			const char *res_name = pci_resource_name(bus->self, idx);
+
+			pci_info(bus->self, "%s %pR: not released, active children present\n",
+				 res_name, r);
+			continue;
+		}
+
+		if (pci_dev_res_add_to_list(saved, bus->self, r, 0, 0))
+			continue;
+
+		pci_release_resource(bus->self, idx);
+	}
+}
+
 /*
  * Walk to the root bus, find the bridge window relevant for @res and
  * release it when possible. If the bridge window contains assigned
@@ -2305,7 +2362,6 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
 	struct pci_dev *bridge = NULL;
 	LIST_HEAD(added);
 	LIST_HEAD(failed);
-	unsigned int i;
 	int ret = 0;
 
 	while (!pci_is_root_bus(bus)) {
@@ -2314,22 +2370,10 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
 		if (!res)
 			break;
 
-		i = pci_resource_num(bridge, res);
-
-		/* Ignore BARs which are still in use */
-		if (!res->child) {
-			ret = pci_dev_res_add_to_list(saved, bridge, res, 0, 0);
-			if (ret)
-				return ret;
-
-			pci_release_resource(bridge, i);
-		} else {
-			const char *res_name = pci_resource_name(bridge, i);
-
-			pci_warn(bridge,
-				 "%s %pR: was not released (still contains assigned resources)\n",
-				 res_name, res);
-		}
+		/* Release this bridge window and any empty descendants bottom-up */
+		if (bridge->subordinate)
+			pbus_release_empty_bridge_resources(bridge->subordinate,
+							    res, saved);
 
 		bus = bus->parent;
 	}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-04-29 10:34 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-28 22:55 [PATCH v3 RESEND] PCI: release empty sibling bridge windows during window resize Geramy Loveless
2026-04-29 10:34 ` 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