[PATCH 2/2] thunderbolt: Don't resume the software CM if the host controller is gone
From: Cole Leavitt
Date: Sun Oct 18 2026 - 12:44:01 EST
nhi_resume_noirq() sets nhi->going_away when the host controller is not
present anymore on resume (commit bdccf295d7cd ("thunderbolt: Do not
touch the hardware if the NHI is gone on resume")). With going_away set
tb_ring_start() refuses to start the control channel rings, but the
software connection manager's tb_resume_noirq() still goes through the
whole resume sequence. Every control channel request then fails with
-ESHUTDOWN from __tb_ring_enqueue(), and tb_cfg_read()/tb_cfg_write()
WARN for each one:
tb_cfg_write: -108
WARNING: drivers/thunderbolt/ctl.c:1157 at tb_cfg_write+0x94/0xf0 [thunderbolt]
Call Trace:
tb_switch_configure+0x8f/0x1d0 [thunderbolt]
tb_switch_resume+0xb9/0x3d0 [thunderbolt]
tb_resume_noirq+0x5d/0x2e0 [thunderbolt]
tb_domain_resume_noirq+0x42/0x60 [thunderbolt]
...
tb_cfg_read: -108
WARNING: drivers/thunderbolt/ctl.c:1131 at tb_cfg_read+0x94/0xf0 [thunderbolt]
...
thunderbolt 0000:8a:00.0: 0: failed to restore TMU configuration
This happens on a discrete Barlow Ridge host when the root port link
does not come back in time on resume from s2idle: pciehp marks the
subtree disconnected in pciehp_resume_noirq(), so pci_device_is_present()
returns false for the NHI and the (about to be removed) domain is then
"resumed" through dead rings.
The firmware connection manager already honors the flag in
icm_complete(). Do the same in the software connection manager and skip
tb_resume_noirq() when the host controller is gone. The domain is torn
down when the controller is removed, and hotplug_active stays false so
no events are processed in the meantime.
Fixes: bdccf295d7cd ("thunderbolt: Do not touch the hardware if the NHI is gone on resume")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Cole Leavitt <cole@xxxxxxxxx>
---
Notes:
Tested on a Lenovo ThinkPad P16 Gen 3 (Core Ultra 9 275HX, JHL9580
Barlow Ridge host, I226 Ethernet), in daily use with s2idle since
2026-09-17.
drivers/thunderbolt/tb.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 4e5d0578fd4b..d236de1e5568 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -3177,6 +3177,19 @@ static int tb_resume_noirq(struct tb *tb)
unsigned int usb3_delay = 0;
LIST_HEAD(tunnels);
+ /*
+ * The host controller is gone (for example the last device was
+ * unplugged, or the PCIe hotplug driver found the controller
+ * removed or replaced during system sleep). The control channel
+ * rings are not started in that case so every config space access
+ * below would just fail. Leave the domain as is; it is torn down
+ * when the controller is removed.
+ */
+ if (tb->nhi->going_away) {
+ tb_dbg(tb, "host controller not present, skipping resume\n");
+ return 0;
+ }
+
tb_dbg(tb, "resuming...\n");
/*
--
2.54.0