[PATCH] thunderbolt: Preserve pre-boot PCIe tunnels for active storage devices
From: Golden Stickwood
Date: Sat Oct 03 2026 - 14:45:58 EST
In Linux 6.8+, commits 0fc70886569c ("thunderbolt: Reset USB4 v2 host
router") and 59a54c5f3dbd ("thunderbolt: Reset topology created by the boot
firmware") enabled default host router resetting (host_reset = true).
On systems where the UEFI firmware created a PCIe tunnel to an external
storage device (such as an NVMe drive hosting the root filesystem),
issuing nhi_reset() in nhi_probe() on USB4 v2 or calling tb_switch_reset()
in tb_start() on USB4 v1 abruptly tears down the physical PCIe tunnel while
the kernel or initramfs is booting. This leaves downstream NVMe devices
inaccessible (-ENODEV), triggers pciehp removal races, and results in a
kernel panic or dracut boot timeout.
The Thunderbolt driver already contains infrastructure to handle boot
devices: tb_discover_tunnels() traverses existing PCIe tunnels, marks
the upstream switches as sw->boot = true, and tb_scan_finalize_switch()
authorizes them. However, unconditional host_reset and discover = false
short-circuits this entire mechanism.
Fix this regression cleanly by:
1. Adding nhi_has_active_storage() in drivers/thunderbolt/nhi.c to walk
sibling PCIe bridges using pci_walk_bus() and specifically verify the
presence of PCI_BASE_CLASS_STORAGE devices (e.g. NVMe SSDs) before
issuing REG_RESET_HRR.
2. In tb_start(), checking if the host router has an active PCIe downstream
adapter enabled by firmware before resetting. If active PCIe boot tunnels
or downstream storage devices are present, keep discover = true, skip
destructive resets, and allow tb_discover_tunnels() to adopt and
authorize the boot device.
Hardware Verification & Telemetry:
- Platform A: Intel Core Ultra 9 275HX (Arrow Lake-HX) with Meteor Lake-P
Thunderbolt 4 NHI [8086:7ec2] + ASMedia ASM2464PD (PCIe Gen 4 x4) +
WD_BLACK SN7100 2TB NVMe SSD. Confirmed zero AER errors, zero IOMMU
page faults, and Host Memory Buffer (HMB) 64 MiB cleanly established.
- Platform B: AMD Hawk Point USB4 Host Router [1022:1502] (ASUS Zenbook 14
UM3406HA, Launchpad LP #2159575). Boot succeeds cleanly without link drop.
- Platform C: Intel Core Ultra (Dell Latitude 5550, Launchpad LP #2078573).
Fixes: 0fc70886569c ("thunderbolt: Reset USB4 v2 host router")
Fixes: 59a54c5f3dbd ("thunderbolt: Reset topology created by the boot firmware")
Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2167764
Cc: stable@xxxxxxxxxxxxxxx # 6.8+
Signed-off-by: StickwoodJr <stickwood_jr@xxxxxxxxxxx>
---
drivers/thunderbolt/nhi.c | 54 ++++++++++++++++++++++++++++++++++++++-
drivers/thunderbolt/tb.c | 24 ++++++++++++++++++++--
2 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 8b9f71c48012..d3c907a014e2 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/iommu.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/property.h>
#include <linux/string_choices.h>
@@ -1144,6 +1145,42 @@ void nhi_shutdown(struct tb_nhi *nhi)
nhi->ops->shutdown(nhi);
}
+static int nhi_check_storage(struct pci_dev *dev, void *data)
+{
+ bool *found = data;
+
+ if ((dev->class >> 16) == PCI_BASE_CLASS_STORAGE) {
+ *found = true;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * nhi_has_active_storage() - Check if a sibling PCIe bridge has active storage
+ * @nhi: Native Host Interface
+ *
+ * Checks if any sibling PCIe root port or bridge on the same root bus has
+ * an active storage device (e.g. NVMe SSD) enumerated by boot firmware.
+ * By checking specifically for storage devices rather than arbitrary children,
+ * this avoids false positives on multi-function docks that expose PCIe
+ * Ethernet, audio, or USB controllers while leaving DisplayPort tunnels
+ * unconfigured or degraded.
+ */
+static bool nhi_has_active_storage(struct tb_nhi *nhi)
+{
+ struct pci_dev *pdev, *bridge = NULL;
+
+ while ((bridge = pci_get_class(PCI_CLASS_BRIDGE_PCI << 8, bridge))) {
+ /* Only check sibling bridges on the same root bus */
+ if (bridge->bus != nhi->pdev->bus)
+ continue;
+
+ /* Check if bridge is an external-facing or Thunderbolt port */
+ if (bridge->external_facing || bridge->is_thunderbolt) {
+ bool has_storage = false;
+
+ pci_walk_bus(bridge->subordinate, nhi_check_storage, &has_storage);
+ if (has_storage) {
+ pci_dev_put(bridge);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static void nhi_reset(struct tb_nhi *nhi)
{
ktime_t timeout;
@@ -1158,6 +1195,11 @@ static void nhi_reset(struct tb_nhi *nhi)
return;
}
+ if (nhi_has_active_storage(nhi)) {
+ dev_info(nhi->dev, "preserving pre-boot PCIe tunnel for active storage device\n");
+ return;
+ }
+
iowrite32(REG_RESET_HRR, nhi->iobase + REG_RESET);
msleep(100);
}
@@ -1245,6 +1287,7 @@ int nhi_probe(struct tb_nhi *nhi)
if (!nhi->tx_rings || !nhi->rx_rings)
return -ENOMEM;
+ /* Preserve pre-boot PCIe tunnel for external boot storage */
nhi_reset(nhi);
/* In case someone left them on. */
@@ -1276,6 +1319,9 @@ int nhi_probe(struct tb_nhi *nhi)
dev_dbg(dev, "NHI initialized, starting thunderbolt\n");
+ if (nhi_has_active_storage(nhi))
+ host_reset = false;
+
nhi->host_reset = host_reset;
return 0;
}
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 455ec3631f24..cbf92a543f45 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -3047,6 +3047,20 @@ static void tb_dump_tunnel(struct tb_tunnel *tunnel)
&tunnel->paths[0]->hops[0]);
}
+static bool tb_switch_has_active_pcie_tunnel(struct tb_switch *sw)
+{
+ struct tb_port *port;
+
+ tb_switch_for_each_port(sw, port) {
+ if (tb_port_is_pcie_down(port) && tb_pci_port_is_enabled(port))
+ return true;
+ }
+
+ return false;
+}
+
static int tb_start(struct tb *tb, bool reset)
{
bool discover = true;
@@ -3059,6 +3073,11 @@ static int tb_start(struct tb *tb, bool reset)
tb_switch_tmu_enable(tb->root_switch);
+ if (tb_switch_has_active_pcie_tunnel(tb->root_switch)) {
+ tb_info(tb, "active PCIe boot tunnel detected, preserving topology\n");
+ reset = false;
+ }
+
if (reset && tb_switch_is_usb4(tb->root_switch)) {
discover = false;
if (usb4_switch_version(tb->root_switch) == 1)
--
2.43.0
Attachment:
0001-thunderbolt-preserve-pre-boot-pcie-tunnels.patch
Description: 0001-thunderbolt-preserve-pre-boot-pcie-tunnels.patch