Re: [PATCH v2] thunderbolt: Stop waiting on a path pending bit that never clears

Mika Westerberg <[email protected]>
Newsgroups org.kernel.vger.linux-usb
Message-ID <[email protected]>
Hi,

On Wed, Aug 12, 2026 at 01:21:52AM +0000, Fan Ye via B4 Relay wrote:
> From: Fan Ye <[email protected]>
> 
> __tb_path_deactivate_hop() waits up to 500 ms for a hop's pending bit to
> read back clear. On an ASMedia ASM4242 host router the host interface
> adapter latches it once enough frames have gone through the DMA ring and
> never clears it again: the teardown finds it already set, seconds after
> the last frame and with the path still up. USB4 v2 table 8-23 has the
> field read only and zero unless packets belonging to the path are waiting
> to be dequeued, so the wait is right and this adapter is not.
> 
> Quirk those routers by hardware id and let a quirked adapter that has
> timed out once answer -ETIMEDOUT immediately from then on. Bringing the
> interface down then took 8 ms across 200 cycles, where unpatched it took
> 508 ms in 161 of 162, at an unchanged failure rate of about one per
> cycle.
> 
> Assisted-by: Claude:claude-opus-5
> Signed-off-by: Fan Ye <[email protected]>
> ---
> Measured on two ASM4242 hosts wired to each other, hw_vendor_id 0x174c,
> hw_device_id 0x2428, NVM 200011.250708, on a base that already carries
> 68bf02b6b4ad.

I suppose there is not errata about this?

> Read before the teardown writes anything, path still up and the ring idle
> for seconds: at 200 frames it is clear in 5 of 5 rounds and the hop drains
> on the first read, at 260 it is set in 5 of 5 and the wait runs out. So it
> is set before the teardown starts rather than by it, and the clear case is
> why the flag is learned rather than taken from the quirk alone.
> 
> 5.4.1 has a connection manager wait tTeardown after the router sets the
> bit to 0b before making the path valid again. On an adapter where it
> never reads 0b there is no such point, patched or not.
> 
> v2:
> - Add the Assisted-by tag.
> - Answer the spec question from USB4 v2 table 8-23.
> - Return -ETIMEDOUT instead of 0; gate the flag on a 0x174c/0x2428 quirk.
> - Drop the ring-wrap claim.
> - Cut the text down.
> 
> v1: https://lore.kernel.org/linux-usb/[email protected]/
> ---
>  drivers/thunderbolt/path.c   | 8 ++++++++
>  drivers/thunderbolt/quirks.c | 8 ++++++++
>  drivers/thunderbolt/tb.h     | 4 ++++
>  3 files changed, 20 insertions(+)
> 
> diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
> index b2c322e76b8a..5cf87b8af2b2 100644
> --- a/drivers/thunderbolt/path.c
> +++ b/drivers/thunderbolt/path.c
> @@ -397,6 +397,10 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
>  	if (ret)
>  		return ret;
>  
> +	/* It never clears on this adapter, so the wait would time out again. */
> +	if (port->pending_stuck)
> +		return -ETIMEDOUT;

Instead of this, let's do it so that you introduce port->pp_timeout_msec
that gets initialized with 500. In the quirk_stuck_pending() you check for
the NHI adapter and then set that to 0. Here you just check that field and
if it is 0 then skip the wait.

I think that would be more "future" proof in case there will be other
issues like this on various vendor's adapters.

> +
>  	/* Wait until it is drained */
>  	timeout = ktime_add_ms(ktime_get(), 500);
>  	do {
> @@ -430,6 +434,10 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
>  		usleep_range(10, 20);
>  	} while (ktime_before(ktime_get(), timeout));
>  
> +	/* Remember it only on an adapter known to latch it. */
> +	if (tb_port_is_nhi(port) && (port->sw->quirks & QUIRK_STUCK_PENDING))
> +		port->pending_stuck = true;
> +
>  	return -ETIMEDOUT;
>  }
>  
> diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c
> index 9f7914ac2f48..6ec90a85bbaf 100644
> --- a/drivers/thunderbolt/quirks.c
> +++ b/drivers/thunderbolt/quirks.c
> @@ -52,6 +52,12 @@ static void quirk_block_rpm_in_redrive(struct tb_switch *sw)
>  	tb_sw_dbg(sw, "preventing runtime PM in DP redrive mode\n");
>  }
>  
> +static void quirk_stuck_pending(struct tb_switch *sw)
> +{
> +	sw->quirks |= QUIRK_STUCK_PENDING;
> +	tb_sw_dbg(sw, "host interface adapter pending bit does not clear\n");
> +}
> +
>  struct tb_quirk {
>  	u16 hw_vendor_id;
>  	u16 hw_device_id;
> @@ -114,6 +120,8 @@ static const struct tb_quirk tb_quirks[] = {
>  	{ 0x0438, 0x0209, 0x0000, 0x0000, quirk_clx_disable },
>  	{ 0x0438, 0x020a, 0x0000, 0x0000, quirk_clx_disable },
>  	{ 0x0438, 0x020b, 0x0000, 0x0000, quirk_clx_disable },
> +	/* ASM4242 never clears the Pending Packets bit of its NHI adapter */
> +	{ 0x174c, 0x2428, 0x0000, 0x0000, quirk_stuck_pending },
>  };
>  
>  /**
> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
> index ec9192b61bc0..00eb8d85755a 100644
> --- a/drivers/thunderbolt/tb.h
> +++ b/drivers/thunderbolt/tb.h
> @@ -26,6 +26,8 @@
>  #define QUIRK_NO_CLX					BIT(1)
>  /* Need to keep power on while USB4 port is in redrive mode */
>  #define QUIRK_KEEP_POWER_IN_DP_REDRIVE			BIT(2)
> +/* Pending Packets bit of the host interface adapter never clears */
> +#define QUIRK_STUCK_PENDING				BIT(3)
>  
>  /**
>   * struct tb_nvm - Structure holding NVM information
> @@ -273,6 +275,7 @@ struct tb_bandwidth_group {
>   * @max_bw: Maximum possible bandwidth through this adapter if set to
>   *	    non-zero.
>   * @redrive: For DP IN, if true the adapter is in redrive mode.
> + * @pending_stuck: Pending bit did not clear, so it is not waited on again
>   *
>   * In USB4 terminology this structure represents an adapter (protocol or
>   * lane adapter).
> @@ -302,6 +305,7 @@ struct tb_port {
>  	struct list_head group_list;
>  	unsigned int max_bw;
>  	bool redrive;
> +	bool pending_stuck;
>  };
>  
>  /**
> 
> ---
> base-commit: f5bbbfec59b4e2fb7520a91de3df8a6174325d6a
> change-id: 20260812-b4-tb-pending-v2-78ebc7bb0f58
> 
> Best regards,
> --  
> Fan Ye <[email protected]>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.