thunderbolt: Handle connecting device in place of host properly
authorMika Westerberg <mika.westerberg@linux.intel.com>
Fri, 9 Feb 2018 14:29:38 +0000 (17:29 +0300)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Fri, 9 Mar 2018 09:54:09 +0000 (12:54 +0300)
If the system is suspended and user disconnects cable to another host
and connects it to a Thunderbolt device instead we get a warning from
driver core about adding duplicate sysfs attribute and adding the new
device fails.

Handle this properly so that we first remove the existing XDomain
connection before adding new devices.

Fixes: d1ff70241a27 ("thunderbolt: Add support for XDomain discovery protocol")
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Cc: stable@vger.kernel.org
drivers/thunderbolt/icm.c

index ab02d13f40b705fd67955ec0c26517f904319282..182226023acbbaf76f57b4f440c3b6c7c763c5d7 100644 (file)
@@ -383,6 +383,15 @@ static void remove_switch(struct tb_switch *sw)
        tb_switch_remove(sw);
 }
 
+static void remove_xdomain(struct tb_xdomain *xd)
+{
+       struct tb_switch *sw;
+
+       sw = tb_to_switch(xd->dev.parent);
+       tb_port_at(xd->route, sw)->xdomain = NULL;
+       tb_xdomain_remove(xd);
+}
+
 static void
 icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
 {
@@ -391,6 +400,7 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
        struct tb_switch *sw, *parent_sw;
        struct icm *icm = tb_priv(tb);
        bool authorized = false;
+       struct tb_xdomain *xd;
        u8 link, depth;
        u64 route;
        int ret;
@@ -467,6 +477,13 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
                tb_switch_put(sw);
        }
 
+       /* Remove existing XDomain connection if found */
+       xd = tb_xdomain_find_by_link_depth(tb, link, depth);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
        parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1);
        if (!parent_sw) {
                tb_err(tb, "failed to find parent switch for %u.%u\n",
@@ -529,15 +546,6 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
        tb_switch_put(sw);
 }
 
-static void remove_xdomain(struct tb_xdomain *xd)
-{
-       struct tb_switch *sw;
-
-       sw = tb_to_switch(xd->dev.parent);
-       tb_port_at(xd->route, sw)->xdomain = NULL;
-       tb_xdomain_remove(xd);
-}
-
 static void
 icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
 {