All of lore.kernel.org
 help / color / mirror / Atom feed
From: Juan Martinez <juan.martinez@amd.com>
To: <westeri@kernel.org>
Cc: <mario.limonciello@amd.com>, <andreas.noever@gmail.com>,
	<YehezkelShB@gmail.com>, <Basavaraj.Natikar@amd.com>,
	<Sanath.S@amd.com>, <linux-usb@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>,
	Juan Martinez <juan.martinez@amd.com>
Subject: [PATCH v3] thunderbolt: Fix tb->lock deadlock during hot-unplug on AMD USB4 routers
Date: Thu, 27 Aug 2026 16:57:40 -0500	[thread overview]
Message-ID: <20260827215740.415496-1-juan.martinez@amd.com> (raw)
In-Reply-To: <20260825214237.4179813-1-juan.martinez@amd.com>

Commit f1de1fc5f632 ("thunderbolt: Add quirk to reset host interface on
DMA path teardown for AMD USB4 routers") introduced a deadlock when
physically unplugging a Thunderbolt cable on AMD systems.

The problem occurs because tb_handle_hotplug() holds tb->lock while
processing the unplug event. When it removes the XDomain services,
tbnet_remove() calls tb_xdomain_disable_paths() which eventually calls
tb_domain_reset_interface(). That function tries to acquire tb->lock
via guard(mutex), but the hotplug worker already holds it, causing a
self-deadlock.

The deadlock manifests as a complete network hang because
tb_handle_hotplug() holds RTNL while waiting on its own mutex, blocking
all network operations system-wide.

The existing code already handles this scenario partially: when
xd->is_unplugged is true, tb_disconnect_xdomain_paths() intentionally
skips the DMA teardown because the hotplug handler will do it later
via __tb_disconnect_xdomain_paths(). However, the reset was still
being called unconditionally.

Fix this by:
1. Splitting tb_domain_reset_interface() into a locked inner function
   __tb_domain_reset_interface_locked() and a locking wrapper
2. Skipping the reset in tb_domain_disconnect_xdomain_paths() when
   xd->is_unplugged is true (matching the existing teardown skip logic)
3. Calling __tb_domain_reset_interface_locked() from tb_handle_hotplug()
   after __tb_disconnect_xdomain_paths() where the actual DMA teardown
   happens and tb->lock is already held

This preserves the reset behavior for normal shutdown paths while
avoiding the deadlock during physical cable unplug.

Fixes: f1de1fc5f632 ("thunderbolt: Add quirk to reset host interface on DMA path teardown for AMD USB4 routers")
Signed-off-by: Juan Martinez <juan.martinez@amd.com>
---
 drivers/thunderbolt/domain.c | 26 +++++++++++++++++++++-----
 drivers/thunderbolt/tb.c     |  1 +
 drivers/thunderbolt/tb.h     |  1 +
 3 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index 12c88509a54f..8f33baafe9ae 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -788,14 +788,19 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
 			transmit_ring, receive_path, receive_ring);
 }
 
-static void tb_domain_reset_interface(struct tb *tb)
+/*
+ * __tb_domain_reset_interface_locked - Reset host interface (lock held)
+ *
+ * Caller must hold tb->lock. Used by hotplug path where lock is already held.
+ */
+void __tb_domain_reset_interface_locked(struct tb *tb)
 {
 	struct tb_nhi *nhi = tb->nhi;
 
-	if (!nhi->ops->reset_interface)
-		return;
+	lockdep_assert_held(&tb->lock);
 
-	guard(mutex)(&tb->lock);
+	if (!nhi->ops->reset_interface || !(nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN))
+		return;
 
 	/* The reset clears the ring state so stop the control channel */
 	tb_ctl_stop(tb->ctl);
@@ -803,6 +808,17 @@ static void tb_domain_reset_interface(struct tb *tb)
 	tb_ctl_start(tb->ctl);
 }
 
+static void tb_domain_reset_interface(struct tb *tb)
+{
+	struct tb_nhi *nhi = tb->nhi;
+
+	if (!nhi->ops->reset_interface)
+		return;
+
+	guard(mutex)(&tb->lock);
+	__tb_domain_reset_interface_locked(tb);
+}
+
 /**
  * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain
  * @tb: Domain disabling the DMA paths
@@ -835,7 +851,7 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
 	if (ret)
 		return ret;
 
-	if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN)
+	if (!xd->is_unplugged)
 		tb_domain_reset_interface(tb);
 
 	return 0;
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index b7cc6894a598..89dfb3381345 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -2489,6 +2489,7 @@ static void tb_handle_hotplug(struct work_struct *work)
 			tb_xdomain_remove(xd);
 			port->xdomain = NULL;
 			__tb_disconnect_xdomain_paths(tb, xd, -1, -1, -1, -1);
+			__tb_domain_reset_interface_locked(tb);
 			tb_xdomain_put(xd);
 			tb_port_unconfigure_xdomain(port);
 		} else if (tb_port_is_dpout(port) || tb_port_is_dpin(port)) {
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 4373336d9425..2e6e0920cb1f 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -789,6 +789,7 @@ int tb_domain_disconnect_pcie_paths(struct tb *tb);
 int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
 				    int transmit_path, int transmit_ring,
 				    int receive_path, int receive_ring);
+void __tb_domain_reset_interface_locked(struct tb *tb);
 int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
 				       int transmit_path, int transmit_ring,
 				       int receive_path, int receive_ring);
-- 
2.43.0


  parent reply	other threads:[~2026-08-27 21:58 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-25 21:42 [PATCH v2] thunderbolt: Fix tb->lock deadlock during hot-unplug on AMD USB4 routers juan.martinez
2026-08-26  3:02 ` Mario Limonciello
2026-08-27 21:57 ` Juan Martinez [this message]
2026-08-28  4:43   ` [PATCH v3] " Mario Limonciello
2026-08-28  5:19   ` [PATCH v4] " Juan Martinez
2026-08-28 14:58     ` Mario Limonciello
2026-08-31 11:11     ` Mika Westerberg
2026-08-31 12:55       ` Mario Limonciello
2026-08-31 13:06         ` Mika Westerberg
2026-08-31 13:07           ` Mario Limonciello
2026-08-31 16:16           ` [PATCH v5] " juan.martinez
2026-09-01 22:16             ` Mario Limonciello
2026-09-02  5:48               ` Mika Westerberg
2026-09-09 19:06                 ` Mario Limonciello
2026-09-10 14:19                   ` S, Sanath
2026-09-10 17:24                     ` Mario Limonciello

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=20260827215740.415496-1-juan.martinez@amd.com \
    --to=juan.martinez@amd.com \
    --cc=Basavaraj.Natikar@amd.com \
    --cc=Sanath.S@amd.com \
    --cc=YehezkelShB@gmail.com \
    --cc=andreas.noever@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mario.limonciello@amd.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.