[PATCH 4/4] thunderbolt: prevent pcie tunnel activation/teardown warnings
Chunfeng Yun (云春峰)
Chunfeng.Yun at mediatek.com
Tue Nov 3 00:33:51 PST 2026
On Fri, 2026-10-30 at 09:56 +0100, Mika Westerberg wrote:
> On Fri, Oct 30, 2026 at 04:25:39PM +0800, Chunfeng Yun wrote:
> > When usb4 enter sleep mode, echo 1 to authorized of sysfile,
> > will wakeup usb4 driver, but fail to create pcie tunnel, cause
> > the warning due to a race condition between switch disconnect
>
> It's router
will modify it.
>
> > during resume and user authorization
[ 278.831186] usb4_hi NVDA8100:00: 0:1: is unplugged (state: 7),
retrying...
[ 278.943031] usb4_hi NVDA8100:00: 0:1: failed to reach state
TB_PORT_UP. Ignoring port...
[ 278.951125] usb4_hi NVDA8100:00: 0:1: lost during suspend,
disconnecting
[ 278.957819] usb4_hi NVDA8100:00: 0:4 <-> 1:16 (USB3): deactivating
[ 278.964794] usb4_hi NVDA8100:00: deactivating USB3 Down path from
0:4 to 1:16
[ 278.972737] usb4_hi NVDA8100:00: deactivating USB3 Up path from 1:16
to 0:4
[ 278.980504] usb4_hi NVDA8100:00: 0: TMU: mode change off -> bi-
directional, HiFi requested
[ 278.991265] usb4_hi NVDA8100:00: 0: TMU: mode set to: bi-
directional, HiFi
[ 278.999511] usb4_hi NVDA8100:00: 0:1: total credits changed 66 -> 66
[ 279.006408] usb4_hi NVDA8100:00: 1: link width set to symmetric,
dual lanes
[ 279.013763] usb4_hi NVDA8100:00: 0:7 <-> 1:9 (PCI): activating
[ 279.019587] usb4_hi NVDA8100:00: activating PCIe Down path from 0:7
to 1:9
[ 279.026468] usb4_hi NVDA8100:00: 1:1: Writing hop 1
[ 279.031335] usb4_hi NVDA8100:00: 1:1: In HopID: 8 => Out port: 9
Out HopID: 8
[ 279.038546] usb4_hi NVDA8100:00: 1:1: Weight: 1 Priority: 3
Credits: 32 Drop: 0 PM: 0
[ 279.046536] usb4_hi NVDA8100:00: 1:1: Counter enabled: 0 Counter
index: 2047
[ 279.053834] usb4_hi NVDA8100:00: 1:1: Flow Control (In/Eg): 1/0
Shared Buffer (In/Eg): 0/0
[ 279.062259] usb4_hi NVDA8100:00: 1:1: Unknown1: 0x0 Unknown2: 0x0
Unknown3: 0x0
[ 279.069730] ------------[ cut here ]------------
[ 279.074335] usb4_hi NVDA8100:00: PCIe Down path activation failed
[ 279.080426] WARNING: CPU: 5 PID: 2433 at
drivers/thunderbolt/path.c:587 tb_path_activate+0x1a4/0x5b0
...
[ 279.220285] Call trace:
[ 279.222722] tb_path_activate+0x1a4/0x5b0 (P)
[ 279.227068] tb_tunnel_activate+0x140/0x498
[ 279.231241] tb_tunnel_pci+0x180/0x490
[ 279.234980] tb_domain_approve_switch+0x74/0xb0
[ 279.239500] authorized_store+0x214/0x270
[ 279.243498] dev_attr_store+0x20/0x70
...
[ 279.288888] ---[ end trace 0000000000000000 ]---
[ 279.293497] usb4_hi NVDA8100:00: 0:7 <-> 1:9 (PCI): activation
failed
root cause:
The warning were caused by a race condition between switch disconnect
during resume and user authorization:
1. While the system was in runtime suspend, userspace executed:
echo 1 > /sys/bus/thunderbolt/devices/0-1/authorized
2. Writing to this sysfs attribute woke up the USB4 controller
(nhi_runtime_resume).
3. During resume, the downstream port 0:1 failed to complete link
training within the timeout and was declared lost:
[ 278.943031] usb4_hi NVDA8100:00: 0:1: failed to reach state
TB_PORT_UP. Ignoring port...
[ 278.951125] usb4_hi NVDA8100:00: 0:1: lost during suspend,
disconnecting
This called tb_sw_set_unplugged(port->remote->sw), setting sw-
>is_unplugged = true on switch 0-1.
4. However, authorized_store() had already taken the lock and
proceeded to create the PCIe tunnel via tb_tunnel_pci() ->
tb_tunnel_activate() -> tb_path_activate().
5. When tb_path_activate() tried to program routing Hop 1 into port
1:1 of switch 0-1, tb_port_write() refused because the switch was
marked unplugged:
/* include/linux/thunderbolt.h / tb.h */
static inline int tb_port_write(...) {
if (port->sw->is_unplugged)
return -ENODEV; /* Returns -19 (0xffffffed) */
...
}
6. Because tb_port_write() returned -ENODEV, tb_path_activate()
bailed out to err: and triggered the kernel warning:
/* drivers/thunderbolt/path.c:587 */
err:
tb_WARN(path->tb, "%s path activation failed\n", path-
>name);
return res;
Userspace received -EIO (-bash: echo: write error: Input/output
error).
>
> >
> > When usb4 enter sleep mode, echo 0 to authorized of sysfile,
> > will wakeup usb4 driver, but fail to disconnect pcie due to
> > the PCIe tunnel had already been torn down and freed if the port
> > is fail to resume:
>
> So this is runtime resume when there is no PCIe tunnel yet,
It is already destroyed before due to the lane link timeout
> the device is
> unplugged and the userspace is at the same time writing to the
> "authorized"
> attribute?
[ 545.887822] usb4_hi NVDA8100:00: 0:1: is unplugged (state: 7),
retrying...
[ 545.998689] usb4_hi NVDA8100:00: 0:1: failed to reach state
TB_PORT_UP. Ignoring port...
[ 546.006778] usb4_hi NVDA8100:00: 0:1: lost during suspend,
disconnecting
[ 546.013474] usb4_hi NVDA8100:00: 0:4 <-> 1:16 (USB3): deactivating
[ 546.020449] usb4_hi NVDA8100:00: deactivating USB3 Down path from
0:4 to 1:16
[ 546.027959] usb4_hi NVDA8100:00: deactivating USB3 Up path from 1:16
to 0:4
[ 546.035649] usb4_hi NVDA8100:00: 0:7 <-> 1:9 (PCI): deactivating
[ 546.042463] usb4_hi NVDA8100:00: deactivating PCIe Down path from
0:7 to 1:9
[ 546.050320] usb4_hi NVDA8100:00: deactivating PCIe Up path from 1:9
to 0:7
[ 546.057790] usb4_hi NVDA8100:00: 0: TMU: mode change off -> bi-
directional, HiFi requested
[ 546.067910] usb4_hi NVDA8100:00: 0: TMU: mode set to: bi-
directional, HiFi
[ 546.076120] usb4_hi NVDA8100:00: 0:1: total credits changed 66 -> 66
[ 546.082844] usb4_hi NVDA8100:00: 1: link width set to symmetric,
dual lanes
[ 546.089815] ------------[ cut here ]------------
[ 546.094420] WARNING: CPU: 7 PID: 2433 at
drivers/thunderbolt/tb.c:2245 tb_disconnect_pci+0x108/0x120
...
[ 546.238810] Call trace:
[ 546.241246] tb_disconnect_pci+0x108/0x120 (P)
[ 546.245680] tb_domain_disapprove_switch+0x24/0x60
[ 546.250464] disapprove_switch+0xbc/0xe8
[ 546.254378] authorized_store+0x19c/0x270
[ 546.258378] dev_attr_store+0x20/0x70
...
[ 546.303767] ---[ end trace 0000000000000000 ]---
root cause:
The warning occurred because the PCIe tunnel had already been torn
down and freed by tb_free_invalid_tunnels() just milliseconds before
tb_disconnect_pci() was called,
tb_disconnect_pci() assumes that if userspace writes 0 to authorized
on an authorized switch, the corresponding PCIe tunnel must still exist
in tcm->tunnel_list. However, in this scenario, the tunnel was already
destroyed during the wake-up sequence, leaving tunnel == NULL.
>
> Then the PCIe tunnel creation fails as expected.
>
> What warning you are trying to silence?
if we find the router is disconnect, no need find the PCIe tunnel
anymore.
Thanks
>
> >
> > The check prevents a redundant lookup and eliminates the warning
> >
> > Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> > ---
> > drivers/thunderbolt/switch.c | 5 +++++
> > drivers/thunderbolt/tb.c | 7 +++++++
> > 2 files changed, 12 insertions(+)
> >
> > diff --git a/drivers/thunderbolt/switch.c
> > b/drivers/thunderbolt/switch.c
> > index e7ee9639c54c..7902172f4e99 100644
> > --- a/drivers/thunderbolt/switch.c
> > +++ b/drivers/thunderbolt/switch.c
> > @@ -1849,6 +1849,11 @@ static int tb_switch_set_authorized(struct
> > tb_switch *sw, unsigned int val)
> > if (!!sw->authorized == !!val)
> > goto unlock;
> >
> > + if (sw->is_unplugged) {
> > + ret = -ENODEV;
> > + goto unlock;
> > + }
> > +
> > switch (val) {
> > /* Disapprove switch */
> > case 0:
> > diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
> > index 57c11ba13459..69310622d786 100644
> > --- a/drivers/thunderbolt/tb.c
> > +++ b/drivers/thunderbolt/tb.c
> > @@ -2266,6 +2266,10 @@ static int tb_disconnect_pci(struct tb *tb,
> > struct tb_switch *sw)
> > struct tb_tunnel *tunnel;
> > struct tb_port *up;
> >
> > + /* If the switch has already been unplugged, tunnels are
> > already freed */
> > + if (sw->is_unplugged)
> > + return 0;
> > +
> > up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP);
> > if (WARN_ON(!up))
> > return -ENODEV;
> > @@ -2288,6 +2292,9 @@ static int tb_tunnel_pci(struct tb *tb,
> > struct tb_switch *sw)
> > struct tb_cm *tcm = tb_priv(tb);
> > struct tb_tunnel *tunnel;
> >
> > + if (sw->is_unplugged)
> > + return -ENODEV;
> > +
> > up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP);
> > if (!up)
> > return 0;
> > --
> > 2.43.0
More information about the linux-arm-kernel
mailing list