135 lines
4.5 KiB
Diff
135 lines
4.5 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||
|
Date: Thu, 7 Sep 2023 16:02:30 +0300
|
||
|
Subject: [PATCH] thunderbolt: Restart XDomain discovery handshake after
|
||
|
failure
|
||
|
|
||
|
Alex reported that after rebooting the other host the peer-to-peer link
|
||
|
does not come up anymore. The reason for this is that the host that was
|
||
|
not rebooted tries to send the UUID request only 10 times according to
|
||
|
the USB4 Inter-Domain spec and gives up if it does not get reply. Then
|
||
|
when the other side is actually ready it cannot get the link established
|
||
|
anymore. The USB4 Inter-Domain spec requires that the discovery protocol
|
||
|
is restarted in that case so implement this now.
|
||
|
|
||
|
Reported-by: Alex Balcanquall <alex@alexbal.com>
|
||
|
Fixes: 8e1de7042596 ("thunderbolt: Add support for XDomain lane bonding")
|
||
|
Cc: stable@vger.kernel.org
|
||
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||
|
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
|
||
|
---
|
||
|
drivers/thunderbolt/xdomain.c | 58 +++++++++++++++++++++++++----------
|
||
|
1 file changed, 41 insertions(+), 17 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
|
||
|
index 3c51e47dd86b..0b17a4d4e9b9 100644
|
||
|
--- a/drivers/thunderbolt/xdomain.c
|
||
|
+++ b/drivers/thunderbolt/xdomain.c
|
||
|
@@ -704,6 +704,27 @@ static void update_property_block(struct tb_xdomain *xd)
|
||
|
mutex_unlock(&xdomain_lock);
|
||
|
}
|
||
|
|
||
|
+static void start_handshake(struct tb_xdomain *xd)
|
||
|
+{
|
||
|
+ xd->state = XDOMAIN_STATE_INIT;
|
||
|
+ queue_delayed_work(xd->tb->wq, &xd->state_work,
|
||
|
+ msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
|
||
|
+}
|
||
|
+
|
||
|
+/* Can be called from state_work */
|
||
|
+static void __stop_handshake(struct tb_xdomain *xd)
|
||
|
+{
|
||
|
+ cancel_delayed_work_sync(&xd->properties_changed_work);
|
||
|
+ xd->properties_changed_retries = 0;
|
||
|
+ xd->state_retries = 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void stop_handshake(struct tb_xdomain *xd)
|
||
|
+{
|
||
|
+ cancel_delayed_work_sync(&xd->state_work);
|
||
|
+ __stop_handshake(xd);
|
||
|
+}
|
||
|
+
|
||
|
static void tb_xdp_handle_request(struct work_struct *work)
|
||
|
{
|
||
|
struct xdomain_request_work *xw = container_of(work, typeof(*xw), work);
|
||
|
@@ -766,6 +787,15 @@ static void tb_xdp_handle_request(struct work_struct *work)
|
||
|
case UUID_REQUEST:
|
||
|
tb_dbg(tb, "%llx: received XDomain UUID request\n", route);
|
||
|
ret = tb_xdp_uuid_response(ctl, route, sequence, uuid);
|
||
|
+ /*
|
||
|
+ * If we've stopped the discovery with an error such as
|
||
|
+ * timing out, we will restart the handshake now that we
|
||
|
+ * received UUID request from the remote host.
|
||
|
+ */
|
||
|
+ if (!ret && xd && xd->state == XDOMAIN_STATE_ERROR) {
|
||
|
+ dev_dbg(&xd->dev, "restarting handshake\n");
|
||
|
+ start_handshake(xd);
|
||
|
+ }
|
||
|
break;
|
||
|
|
||
|
case LINK_STATE_STATUS_REQUEST:
|
||
|
@@ -1522,6 +1552,13 @@ static void tb_xdomain_queue_properties_changed(struct tb_xdomain *xd)
|
||
|
msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
|
||
|
}
|
||
|
|
||
|
+static void tb_xdomain_failed(struct tb_xdomain *xd)
|
||
|
+{
|
||
|
+ xd->state = XDOMAIN_STATE_ERROR;
|
||
|
+ queue_delayed_work(xd->tb->wq, &xd->state_work,
|
||
|
+ msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
|
||
|
+}
|
||
|
+
|
||
|
static void tb_xdomain_state_work(struct work_struct *work)
|
||
|
{
|
||
|
struct tb_xdomain *xd = container_of(work, typeof(*xd), state_work.work);
|
||
|
@@ -1548,7 +1585,7 @@ static void tb_xdomain_state_work(struct work_struct *work)
|
||
|
if (ret) {
|
||
|
if (ret == -EAGAIN)
|
||
|
goto retry_state;
|
||
|
- xd->state = XDOMAIN_STATE_ERROR;
|
||
|
+ tb_xdomain_failed(xd);
|
||
|
} else {
|
||
|
tb_xdomain_queue_properties_changed(xd);
|
||
|
if (xd->bonding_possible)
|
||
|
@@ -1613,7 +1650,7 @@ static void tb_xdomain_state_work(struct work_struct *work)
|
||
|
if (ret) {
|
||
|
if (ret == -EAGAIN)
|
||
|
goto retry_state;
|
||
|
- xd->state = XDOMAIN_STATE_ERROR;
|
||
|
+ tb_xdomain_failed(xd);
|
||
|
} else {
|
||
|
xd->state = XDOMAIN_STATE_ENUMERATED;
|
||
|
}
|
||
|
@@ -1624,6 +1661,8 @@ static void tb_xdomain_state_work(struct work_struct *work)
|
||
|
break;
|
||
|
|
||
|
case XDOMAIN_STATE_ERROR:
|
||
|
+ dev_dbg(&xd->dev, "discovery failed, stopping handshake\n");
|
||
|
+ __stop_handshake(xd);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
@@ -1793,21 +1832,6 @@ static void tb_xdomain_release(struct device *dev)
|
||
|
kfree(xd);
|
||
|
}
|
||
|
|
||
|
-static void start_handshake(struct tb_xdomain *xd)
|
||
|
-{
|
||
|
- xd->state = XDOMAIN_STATE_INIT;
|
||
|
- queue_delayed_work(xd->tb->wq, &xd->state_work,
|
||
|
- msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
|
||
|
-}
|
||
|
-
|
||
|
-static void stop_handshake(struct tb_xdomain *xd)
|
||
|
-{
|
||
|
- cancel_delayed_work_sync(&xd->properties_changed_work);
|
||
|
- cancel_delayed_work_sync(&xd->state_work);
|
||
|
- xd->properties_changed_retries = 0;
|
||
|
- xd->state_retries = 0;
|
||
|
-}
|
||
|
-
|
||
|
static int __maybe_unused tb_xdomain_suspend(struct device *dev)
|
||
|
{
|
||
|
stop_handshake(tb_to_xdomain(dev));
|