thunderbolt: Handle connecting device in place of host properly

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: d1ff70241a ("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
This commit is contained in:
Mika Westerberg 2018-02-09 17:29:38 +03:00
Родитель a03e828915
Коммит 79fae98751
1 изменённых файлов: 17 добавлений и 9 удалений

Просмотреть файл

@ -383,6 +383,15 @@ static void remove_switch(struct tb_switch *sw)
tb_switch_remove(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 static void
icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) 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 tb_switch *sw, *parent_sw;
struct icm *icm = tb_priv(tb); struct icm *icm = tb_priv(tb);
bool authorized = false; bool authorized = false;
struct tb_xdomain *xd;
u8 link, depth; u8 link, depth;
u64 route; u64 route;
int ret; int ret;
@ -467,6 +477,13 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
tb_switch_put(sw); 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); parent_sw = tb_switch_find_by_link_depth(tb, link, depth - 1);
if (!parent_sw) { if (!parent_sw) {
tb_err(tb, "failed to find parent switch for %u.%u\n", 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); 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 static void
icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
{ {