Linux USB
 help / color / mirror / Atom feed
From: Mika Westerberg <mika.westerberg@linux.intel.com>
To: Basavaraj Natikar <bnatikar@amd.com>
Cc: Basavaraj Natikar <Basavaraj.Natikar@amd.com>,
	andreas.noever@gmail.com, westeri@kernel.org,
	YehezkelShB@gmail.com, linux-usb@vger.kernel.org,
	Mario Limonciello <superm1@kernel.org>,
	Sanath S <Sanath.S@amd.com>
Subject: Re: [PATCH v3] thunderbolt: Assert downstream port reset on shutdown
Date: Wed, 10 Jun 2026 16:23:17 +0200	[thread overview]
Message-ID: <20260610142317.GS2990@black.igk.intel.com> (raw)
In-Reply-To: <ef95085b-8c7c-4214-ad81-1ece2adf805f@amd.com>

Hi,

On Wed, Jun 10, 2026 at 07:02:02PM +0530, Basavaraj Natikar wrote:
> Hi,
> 
> 
> On 6/10/2026 5:28 PM, Mika Westerberg wrote:
> > On Wed, Jun 10, 2026 at 05:02:56PM +0530, Basavaraj Natikar wrote:
> > > On 6/10/2026 4:27 PM, Mika Westerberg wrote:
> > > > Hi,
> > > > 
> > > > On Wed, Jun 10, 2026 at 02:20:06PM +0530, Basavaraj Natikar wrote:
> > > > > On shutdown the connection manager tears down the router tree without
> > > > > signalling connected devices. A Thunderbolt 3 device directly connected
> > > > > to a USB4 host never receives a disconnect indication and during shutdown
> > > > > this can cause polling the dead link for up to 60 seconds. On some
> > > > > platforms this behavior leads to a warm reset instead of a shutdown due
> > > > > to this timeout.
> > > > > 
> > > > > Fix this by asserting PORT_CS_19.DPR on each connected downstream port
> > > > > before tearing down the router tree. This drives SBTX low (USB4 spec
> > > > > section 6.9), causing the device to detect SBRX low and transition to
> > > > > Uninitialized Unplugged state immediately.
> > > > > 
> > > > > Always do this on system shutdown/reboot by forcing host_reset in the
> > > > > PCI ->shutdown callback. On plain driver unload only do it when the host
> > > > > router was actually reset on load (host_reset=1), since in that case the
> > > > > tunnels are not preserved across reload anyway; with host_reset=0 the
> > > > > tunnels are kept alive across unload/reload so the links are left intact.
> > > > > Restrict the reset to Thunderbolt 3 devices.
> > > > > 
> > > > > Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
> > > > > Co-developed-by: Sanath S <Sanath.S@amd.com>
> > > > > Signed-off-by: Sanath S <Sanath.S@amd.com>
> > > > > Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
> > > > > ---
> > > > > v3:
> > > > >    - Rebase on top of the thunderbolt next branch; the PCI ->shutdown hook
> > > > >      now lives in drivers/thunderbolt/pci.c (nhi_pci_driver_shutdown()).
> > > > >    - Add kernel-doc for tb_port_reset() now that it is exported.
> > > > >    - Move the host_reset flag into struct tb_nhi instead of struct tb, and
> > > > >      set it from nhi_probe() instead of tb_domain_add().
> > > > >    - Spell out "Thunderbolt 3" instead of "TBT3" in the kernel-doc.
> > > > >    - Reword the debug message to "downstream port reset failed, continuing".
> > > > > 
> > > > > v2:
> > > > >    - Add Reviewed-by tag from Mario Limonciello.
> > > > >    - Restrict the DPR assertion to Thunderbolt 3 devices, since the issue
> > > > >      only affects Thunderbolt 3 devices.
> > > > >    - Only assert DPR on system shutdown/reboot (the PCI ->shutdown callback
> > > > >      forces host_reset) or when the host router was actually reset on load.
> > > > >    - Reword comments and commit message to refer to the "router tree"
> > > > >      instead of the "switch tree".
> > > > > ---
> > > > >    drivers/thunderbolt/nhi.c    |  2 ++
> > > > >    drivers/thunderbolt/pci.c    | 16 +++++++++++++++-
> > > > >    drivers/thunderbolt/switch.c | 11 ++++++++++-
> > > > >    drivers/thunderbolt/tb.c     | 21 +++++++++++++++++++++
> > > > >    drivers/thunderbolt/tb.h     |  1 +
> > > > >    include/linux/thunderbolt.h  |  6 ++++++
> > > > >    6 files changed, 55 insertions(+), 2 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
> > > > > index 0f795ea58756..698fb124d529 100644
> > > > > --- a/drivers/thunderbolt/nhi.c
> > > > > +++ b/drivers/thunderbolt/nhi.c
> > > > > @@ -1235,6 +1235,8 @@ int nhi_probe(struct tb_nhi *nhi)
> > > > >    	init_completion(&nhi->domain_released);
> > > > > +	nhi->host_reset = host_reset;
> > > > > +
> > > > >    	res = tb_domain_add(tb, host_reset);
> > > > >    	if (res) {
> > > > >    		/*
> > > > > diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c
> > > > > index bbd186c29ef7..be63a4647b59 100644
> > > > > --- a/drivers/thunderbolt/pci.c
> > > > > +++ b/drivers/thunderbolt/pci.c
> > > > > @@ -493,6 +493,20 @@ static void nhi_pci_remove(struct pci_dev *pdev)
> > > > >    	nhi_shutdown(nhi);
> > > > >    }
> > > > > +static void nhi_pci_driver_shutdown(struct pci_dev *pdev)
> > > > nhi_pci_shutdown()
> > > > 
> > > > > +{
> > > > > +	struct tb *tb = pci_get_drvdata(pdev);
> > > > > +	struct tb_nhi *nhi = tb->nhi;
> > > > > +
> > > > > +	/*
> > > > > +	 * Force host_reset so the connection manager asserts DPR and signals
> > > > > +	 * disconnect to connected devices before the router tree is removed.
> > > > > +	 * Only Thunderbolt 3 devices are reset.
> > > > > +	 */
> > > > > +	nhi->host_reset = true;
> > > > > +	nhi_pci_remove(pdev);
> > > > > +}
> > > > > +
> > > > >    static struct pci_device_id nhi_ids[] = {
> > > > >    	/*
> > > > >    	 * We have to specify class, the TB bridges use the same device and
> > > > > @@ -593,7 +607,7 @@ static struct pci_driver nhi_driver = {
> > > > >    	.id_table = nhi_ids,
> > > > >    	.probe = nhi_pci_probe,
> > > > >    	.remove = nhi_pci_remove,
> > > > > -	.shutdown = nhi_pci_remove,
> > > > > +	.shutdown = nhi_pci_driver_shutdown,
> > > > nhi_pci_shutdown
> > > > 
> > > > Otherwise looks good.
> > > Thank you Mika for the review.
> > > 
> > > There is already a static nhi_pci_shutdown() in pci.c (the
> > > tb_nhi_ops.shutdown handler at line 233, also called from nhi_shutdown()),
> > > so reusing that name for the pci_driver ->shutdown callback would collide
> > > and fail to build.
> > > 
> > > https://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt.git/tree/drivers/thunderbolt/pci.c?h=next#n233
> > > 
> > > Would you prefer:
> > > 
> > >    1. Keeping a distinct name for the pci_driver hook, e.g.
> > >       nhi_pci_driver_shutdown() as in this version, or
> > > 
> > >    2. Renaming the existing tb_nhi_ops handler (say, to
> > >       nhi_pci_ops_shutdown()) so the new pci_driver ->shutdown callback
> > >       can take the nhi_pci_shutdown() name?
> > > 
> > > I'm fine with either; please let me know which you prefer and I'll send v4
> > > accordingly.
> > Put this code to nhi.c::nhi_shutdown() as that's applicable also to non-PCI
> > hosts, right?
> 
> I tried that, but nhi_shutdown() runs too late in the teardown to be
> useful here. On the shutdown/remove path:
>   nhi_pci_remove()
>     -> tb_domain_remove()
>          -> cm_ops->stop() == tb_stop()   /* reads host_reset, asserts DPR */
>     -> nhi_shutdown()                      /* ring/HW teardown, runs last */
> tb_stop() is what reads host_reset and asserts the DPR, and it runs
> inside tb_domain_remove() -- before nhi_shutdown(). So host_reset has to
> be set *before* tb_domain_remove(), and nhi_shutdown() would set it after
> tb_stop() has already run.

Ah okay.

> To still keep it generic for non-PCI hosts, how about a small generic
> helper in nhi.c that just forces the flag, called from the bus ->shutdown
> callback before the normal removal:
>   /* nhi.c */
>   void nhi_host_shutdown(struct tb_nhi *nhi)
>   {
>     /* Force DPR on connected Thunderbolt 3 devices, see tb_stop(). */
>     nhi->host_reset = true;
>   }
>   /* pci.c */
>   static void nhi_pci_host_shutdown(struct pci_dev *pdev)
>   {
>     struct tb *tb = pci_get_drvdata(pdev);
>     nhi_host_shutdown(tb->nhi);
>     nhi_pci_remove(pdev);
>   }
>   ...
>     .shutdown = nhi_pci_host_shutdown,
> 
> This leaves nhi_pci_remove() (and nhi_shutdown()) unchanged, and the
> host_reset logic lives in nhi.c so a future non-PCI host can reuse
> nhi_host_shutdown() the same way. Does that work for you, or would you
> 
> prefer it shaped differently?

I really would like to name the PCI driver shutdown hook as
nhi_pci_shutdown. What about this:

1. Rename nhi_pci_shutdown -> nhi_pci_release_irq as that's what it does
   (keep the callback member name shutdown still)

2. Rename nhi_pci_remove -> nhi_pci_do_remove that takes new parameter
   'reset' or so and passes this down to the tb_domain_remove() and also
   ->tb_stop(). 

3. Add nhi_pci_shutdown() that calls nhi_pci_do_remove(pdev, true);
4. Add nhi_pci_remove() that calls nhi_pci_do_remove(pdev, false);

Or something along those lines. If it looks horrible then we can do the
same but with the flag instead of passing the parameter down.

      reply	other threads:[~2026-06-10 14:23 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-10  8:50 [PATCH v3] thunderbolt: Assert downstream port reset on shutdown Basavaraj Natikar
2026-06-10 10:57 ` Mika Westerberg
2026-06-10 11:32   ` Basavaraj Natikar
2026-06-10 11:58     ` Mika Westerberg
2026-06-10 13:32       ` Basavaraj Natikar
2026-06-10 14:23         ` 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=20260610142317.GS2990@black.igk.intel.com \
    --to=mika.westerberg@linux.intel.com \
    --cc=Basavaraj.Natikar@amd.com \
    --cc=Sanath.S@amd.com \
    --cc=YehezkelShB@gmail.com \
    --cc=andreas.noever@gmail.com \
    --cc=bnatikar@amd.com \
    --cc=linux-usb@vger.kernel.org \
    --cc=superm1@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