mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Mika Westerberg <mika.westerberg@linux.intel.com>
To: fy15309206903@gmail.com
Cc: Andreas Noever <andreas.noever@gmail.com>,
	Mika Westerberg <westeri@kernel.org>,
	Yehezkel Bernat <YehezkelShB@gmail.com>,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/2] thunderbolt: Stop waiting on a path pending bit that never clears
Date: Mon, 10 Aug 2026 06:42:59 +0200	[thread overview]
Message-ID: <20260810044259.GC893316@black.igk.intel.com> (raw)
In-Reply-To: <20260809-b4-tb-teardown-v1-2-c88bbfe5c127@gmail.com>

Hi,

On Sun, Aug 09, 2026 at 02:38:13AM +0000, Fan Ye via B4 Relay wrote:
> From: Fan Ye <fy15309206903@gmail.com>
> 
> __tb_path_deactivate_hop() disables a hop and then waits up to 500 ms for
> its pending bit to read back as clear. On an ASMedia ASM4242 host router
> the bit belonging to the host interface adapter latches once the DMA ring
> feeding it has wrapped around, and from then on it never clears. Every
> teardown of a host-to-host DMA tunnel spends the full 500 ms on that hop
> and gives up anyway, which on a link that is brought up and down
> repeatedly is 500 ms every time for an answer that is already known.
> 
> The bit does not track anything the adapter is doing. A path that has
> just been activated and has never carried a single frame reads it set,
> and it is back to clear within a few seconds of that activation with no
> traffic at all - the activation is what gets it there, not time passing,
> which is why the polling further down never sees it move,
> and on a hop entry left behind by an earlier teardown it still reads set
> while the rest of the entry reads as zero - 129 times over a day of
> testing, against zero occurrences on any other adapter of the same
> router, which has four lane adapters, two PCIe down, two USB3 down and
> two DP in.
> 
> Reading the hop config space either side of the disable write shows the
> write lands and the controller does react to it, and that pending is all
> that is left:
> 
>   dw0  80000808 -> 00000808   enable cleared
>   dw1  127ff501 -> 107ff501   egress FC cleared by the controller
>         ^ bit 28, the only bit still set
> 
> Nothing reachable from the driver clears it from there. It still reads
> set after 30 s of polling (237528 config space reads), after 60 s of idle
> before the teardown is even started, and across a module unload and
> reload. The other hop of the same tunnel, the one on the lane adapter,
> drains on its first read in about 125 us.
> 
> What decides it is the ring, not an amount of traffic. With the tbnet
> ring at 128 and at 256 descriptors the first frame count whose teardown
> fails moves with it:
> 
>   ring 128    100 frames  0/5 fail    120 frames  5/5 fail
>   ring 256    240 frames  0/5 fail    260 frames  5/5 fail
> 
> Every point is either five failures out of five or none, and the pending
> bit read back before the disable write agrees with the outcome in each of
> those 40 rounds. Traffic keeps flowing at full rate afterwards, so
> nothing is stuck behind the bit either.
> 
> I would like to say the trigger is the ring wrapping round, and for the
> 256 ring the threshold does straddle 256. It does not for the 128 one:
> 120 frames is already enough there, short of a full lap. So the two sizes
> show that the point moves with the ring, not that the wrap itself is what
> does it. A third size would say more than I can.
> 
> The connection manager already gives up when the wait times out - the
> caller only warns and carries on. So the question is not whether to wait
> it out, but whether to spend the timeout re-learning the same answer on
> every subsequent teardown. Remember it on the adapter instead and skip
> the wait after that.
> 
> The first timeout is still reported, because __tb_path_deactivate_hops()
> warns on it before anything is remembered. That holds for teardowns; the
> other caller of __tb_path_deactivate_hop() is tb_path_activate(), which
> discards the return value, so a first timeout arriving there would set
> the flag without a word. In the runs below it never did - the hop reads
> back disabled by then and the function returns early - but that is an
> observation, not something the code guarantees.
> 
> This needs no vendor matching: an adapter whose pending bit behaves never
> sets the flag, so nothing changes for hardware that works, whoever made
> it. It is however limited to host interface adapters. A lane, PCIe, USB3
> or DP adapter that latched the bit the same way would still burn the
> timeout on every teardown. I scoped it that way because that is where I
> have evidence - 466 timeouts on the host interface adapter of this router
> against 3 on its lane adapter over the same day, and the lane adapter
> drained on the first read every other time - not because I know the
> others cannot do it.
> 
> Measured by cycling the interface down and up 200 times over 80 minutes
> between two of these hosts, from a cold boot, with no module reloads in
> between. Only the thunderbolt module differs between the two runs:
> 
>                                         without      with
>     hop deactivation failed              213 / 206    1 / 1
>     wall clock per cycle                 23.4 s       22.9 s
>                                      (host A / host B)
> 
> One report per adapter instead of one per teardown, and the run comes out
> 110 seconds shorter over its 200 cycles - 4682 against 4572 seconds of
> wall clock, which is where the per-cycle figures above come from. The
> script's own delays are fixed, so that difference is the timeout itself:
> 200 cycles at half a second each would be 100 seconds, and the measured
> 110 is that plus whatever the rest of the run varied by.
> 
> The report lands on the first teardown, which is where a latched bit
> shows up, not during the deactivate that tb_path_activate() does before
> programming a hop.
> 
> Signed-off-by: Fan Ye <fy15309206903@gmail.com>

There is already a fix for this I think. Can you try this:

https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=68bf02b6b4ad3f748c6db71fd77b6c0402d252f4

> 
> ---
> These four came out of one investigation on a pair of ASMedia ASM4242
> hosts wired to each other. Apply them in this order: the second one
> touches lines the first one adds, so it needs that one underneath to
> apply at all, and the last two want the first two under them for the
> reason below.
> 
>   1 net: thunderbolt: Release the Rx HopID that was handed out on mismatch
>   2 net: thunderbolt: Mark the connection down when bringing it up fails
>   3 thunderbolt: Report DMA path teardown failures to the caller
>   4 thunderbolt: Stop waiting on a path pending bit that never clears
> 
> This one is number 4 on that list.
> 
> 1 and 2 fix two separate things that happen to be reached through the
> same branch. Neither depends on the other for correctness - each leaves
> the other's defect in place - but 2 edits the lines 1 adds, so it will
> not apply on its own.
> 
> 1 and 2 were posted to netdev separately and are not in this series:
> https://lore.kernel.org/netdev/20260809-b4-tbnet-hopid-v1-0-97aaf2aa0fc7@gmail.com/
> 
> 3 and 4 do want 1 and 2 underneath: the warning splat that 2 removes
> fires throughout any prolonged run of link cycling, which is what 3 and 4
> have to be measured across. 3 makes teardown failures visible to the
> caller at all; 4 stops the teardown paying for one that cannot succeed.
> Note what that pair does on this particular router - 4 leaves the first
> failure to be reported and silences the rest, so 3's new signal fires
> once per adapter here rather than on every teardown. 4 is the one I am
> least sure of, for the reasons in its own notes.
> 
> Measured on two ASM4242 hosts linked to each other, hw_vendor_id 0x174c,
> hw_device_id 0x2428, NVM 200011.250708. The base is v6.17 with three
> later commits on top, which the link needs before it stays up long enough
> to measure anything across:
> 
>   7e49bb89df86 ("thunderbolt: Avoid reserved fields in path config space
>                  for USB4 routers")
>   1881f2efbf7f ("Revert "net: thunderbolt: Enable end-to-end flow control
>                  also in transmit"")
>   68bf02b6b4ad ("net: thunderbolt: Tear down DMA paths before stopping the
>                  rings")
> 
> plus the two thunderbolt-net patches earlier in this list.
> 
> This started out as a quirk keyed on that hardware id, and the one-shot
> form is better on every count I can think of. Three things about it are
> worth saying plainly.
> 
> One timeout is enough to set the flag, and that is a deliberate choice
> rather than an oversight. A host interface adapter whose bit works but
> whose poll happened to time out once - a busy control channel, say -
> would stop being waited on for the rest of that switch's lifetime. I have
> not seen that happen on a host interface adapter, but I have seen a
> single timeout on an adapter that was otherwise fine: the lane adapter of
> this router timed out three times over the same day and drained on the
> first read every other time. That adapter is out of scope here, so it
> would not have been flagged, but it is the shape of the thing I cannot
> rule out. I settled on one because the caller already ignores the
> timeout, so a false positive costs a diagnostic that was being thrown
> away anyway - it is a counter away from being stricter if you would
> rather it were.
> 
> I cannot show that skipping the wait is safe. Either controller can stop
> answering config space entirely (probe -110, cold boot to recover); over
> one day I logged 16 of those, five on one host and eleven on the other,
> on the distribution kernel and on the unmodified base as much as with
> anything of mine. None of them fell inside the four 200-cycle runs, which
> between them are about five and a half hours of nothing but bringing the
> link up and down - they cluster around module loads and deployments
> instead. So the runs above cannot speak to that rate either way, and I
> would rather say so than let them suggest the question is settled.
> 
> And I cannot answer the spec question. I do not have the USB4 Connection
> Manager guide, so I cannot tell whether the pending bit of a protocol
> adapter is vendor defined the way its IFC, ISE and Path Credits Allocated
> fields are - which 7e49bb89df86 already has the CM keep its hands off.
> 
> That last one is where I would most like help. If someone who has the
> guide could check that one field, it decides whether this patch is the
> right shape or whether the CM simply should not be waiting on that bit at
> all, in which case this is working around a symptom and should be
> dropped. It would also be worth ASMedia confirming the behaviour against
> the NVM above.
> 
> The other thing I would be glad of is coverage. Everything above was
> measured on one model of router, and the flag is only ever set by an
> adapter that has already failed to clear the bit within the existing
> 500 ms, so hardware that behaves should never reach it - but that is an
> argument, not a measurement. If anyone with an Intel or Barlow Ridge host
> can run a few hundred link cycles with this applied and confirm the flag
> stays clear, I would be much happier about it than I am now.
> ---
>  drivers/thunderbolt/path.c | 17 +++++++++++++++++
>  drivers/thunderbolt/tb.h   |  3 +++
>  2 files changed, 20 insertions(+)
> 
> diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
> index 81a79c78777e..0dcc3deb6f61 100644
> --- a/drivers/thunderbolt/path.c
> +++ b/drivers/thunderbolt/path.c
> @@ -397,6 +397,14 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
>  	if (ret)
>  		return ret;
>  
> +	/*
> +	 * This adapter has already been seen to leave the pending bit set
> +	 * for good, so there is nothing to wait for. The hop is disabled
> +	 * by the write above either way, which is what the caller needs.
> +	 */
> +	if (port->no_drain_status)
> +		return 0;
> +
>  	/* Wait until it is drained */
>  	timeout = ktime_add_ms(ktime_get(), 500);
>  	do {
> @@ -430,6 +438,15 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
>  		usleep_range(10, 20);
>  	} while (ktime_before(ktime_get(), timeout));
>  
> +	/*
> +	 * Some host interface adapters latch the pending bit and never
> +	 * clear it again, and there is nothing the connection manager can
> +	 * do about that from here. Report it once and stop spending the
> +	 * timeout on this adapter on every teardown from now on.
> +	 */
> +	if (tb_port_is_nhi(port))
> +		port->no_drain_status = true;
> +
>  	return -ETIMEDOUT;
>  }
>  
> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
> index a9a32b64fe2a..8c022d2724e9 100644
> --- a/drivers/thunderbolt/tb.h
> +++ b/drivers/thunderbolt/tb.h
> @@ -273,6 +273,8 @@ 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.
> + * @no_drain_status: The pending bit of this adapter's path config space
> + *		     stayed set once, so it is not waited on any more
>   *
>   * In USB4 terminology this structure represents an adapter (protocol or
>   * lane adapter).
> @@ -302,6 +304,7 @@ struct tb_port {
>  	struct list_head group_list;
>  	unsigned int max_bw;
>  	bool redrive;
> +	bool no_drain_status;
>  };
>  
>  /**
> 
> -- 
> 2.43.0
> 

      reply	other threads:[~2026-08-10  4:43 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-09  2:38 [PATCH 0/2] thunderbolt: fix teardown reporting and the pending-bit wait Fan Ye via B4 Relay
2026-08-09  2:38 ` [PATCH 1/2] thunderbolt: Report DMA path teardown failures to the caller Fan Ye via B4 Relay
2026-08-09  2:38 ` [PATCH 2/2] thunderbolt: Stop waiting on a path pending bit that never clears Fan Ye via B4 Relay
2026-08-10  4:42   ` Mika Westerberg [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260810044259.GC893316@black.igk.intel.com \
    --to=mika.westerberg@linux.intel.com \
    --cc=YehezkelShB@gmail.com \
    --cc=andreas.noever@gmail.com \
    --cc=fy15309206903@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=westeri@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®