]> git.baikalelectronics.ru Git - kernel.git/commitdiff
thunderbolt: Restart XDomain discovery handshake after failure
authorMika Westerberg <mika.westerberg@linux.intel.com>
Thu, 7 Sep 2023 13:02:30 +0000 (16:02 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 19 Oct 2023 21:08:56 +0000 (23:08 +0200)
commit 308092d080852f8997126e5b3507536162416f4a upstream.

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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/thunderbolt/xdomain.c

index 9a3c52f6b8c97dafddb7a04b8f584f6be93a9c70..18e2ffd095a421d1fd6120c44d2546318bd103b2 100644 (file)
@@ -704,6 +704,27 @@ out_unlock:
        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));