thunderbolt: Do not call PM runtime functions in tb_retimer_scan()
authorMika Westerberg <mika.westerberg@linux.intel.com>
Thu, 29 Dec 2022 12:10:30 +0000 (14:10 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Mon, 9 Jan 2023 08:43:51 +0000 (10:43 +0200)
We cannot call PM runtime functions in tb_retimer_scan() because it will
also be called when retimers are scanned from userspace (happens when
there is no device connected on ChromeOS for instance) and at the same
USB4 port runtime resume hook. This leads to hang because neither can
proceed.

Fix this by runtime resuming USB4 ports in tb_scan_port() instead. This
makes sure the ports are runtime PM active when retimers are added under
it while avoiding the reported hang as well.

Reported-by: Utkarsh Patel <utkarsh.h.patel@intel.com>
Fixes: 1e56c88adecc ("thunderbolt: Runtime resume USB4 port when retimers are scanned")
Cc: stable@vger.kernel.org
Acked-by: Yehezkel Bernat <YehezkelShB@gmail.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/retimer.c
drivers/thunderbolt/tb.c

index 6ebe7a2886ec79df3e4a8cf0b95a17a8dbd3dd97..56008eb91e2e448b9d584c318d6bc55c9af00a5b 100644 (file)
@@ -427,13 +427,6 @@ int tb_retimer_scan(struct tb_port *port, bool add)
 {
        u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
        int ret, i, last_idx = 0;
-       struct usb4_port *usb4;
-
-       usb4 = port->usb4;
-       if (!usb4)
-               return 0;
-
-       pm_runtime_get_sync(&usb4->dev);
 
        /*
         * Send broadcast RT to make sure retimer indices facing this
@@ -441,7 +434,7 @@ int tb_retimer_scan(struct tb_port *port, bool add)
         */
        ret = usb4_port_enumerate_retimers(port);
        if (ret)
-               goto out;
+               return ret;
 
        /*
         * Enable sideband channel for each retimer. We can do this
@@ -471,11 +464,11 @@ int tb_retimer_scan(struct tb_port *port, bool add)
                        break;
        }
 
-       ret = 0;
        if (!last_idx)
-               goto out;
+               return 0;
 
        /* Add on-board retimers if they do not exist already */
+       ret = 0;
        for (i = 1; i <= last_idx; i++) {
                struct tb_retimer *rt;
 
@@ -489,10 +482,6 @@ int tb_retimer_scan(struct tb_port *port, bool add)
                }
        }
 
-out:
-       pm_runtime_mark_last_busy(&usb4->dev);
-       pm_runtime_put_autosuspend(&usb4->dev);
-
        return ret;
 }
 
index 4628458044270afca638a14d8d22f3403b1a1137..3f1ab30c4fb1589029d6a98f60f844e97a72a9a8 100644 (file)
@@ -628,11 +628,15 @@ static void tb_scan_port(struct tb_port *port)
                         * Downstream switch is reachable through two ports.
                         * Only scan on the primary port (link_nr == 0).
                         */
+
+       if (port->usb4)
+               pm_runtime_get_sync(&port->usb4->dev);
+
        if (tb_wait_for_port(port, false) <= 0)
-               return;
+               goto out_rpm_put;
        if (port->remote) {
                tb_port_dbg(port, "port already has a remote\n");
-               return;
+               goto out_rpm_put;
        }
 
        tb_retimer_scan(port, true);
@@ -647,12 +651,12 @@ static void tb_scan_port(struct tb_port *port)
                 */
                if (PTR_ERR(sw) == -EIO || PTR_ERR(sw) == -EADDRNOTAVAIL)
                        tb_scan_xdomain(port);
-               return;
+               goto out_rpm_put;
        }
 
        if (tb_switch_configure(sw)) {
                tb_switch_put(sw);
-               return;
+               goto out_rpm_put;
        }
 
        /*
@@ -681,7 +685,7 @@ static void tb_scan_port(struct tb_port *port)
 
        if (tb_switch_add(sw)) {
                tb_switch_put(sw);
-               return;
+               goto out_rpm_put;
        }
 
        /* Link the switches using both links if available */
@@ -733,6 +737,12 @@ static void tb_scan_port(struct tb_port *port)
 
        tb_add_dp_resources(sw);
        tb_scan_switch(sw);
+
+out_rpm_put:
+       if (port->usb4) {
+               pm_runtime_mark_last_busy(&port->usb4->dev);
+               pm_runtime_put_autosuspend(&port->usb4->dev);
+       }
 }
 
 static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)