Commit 86da809d authored by Mika Westerberg's avatar Mika Westerberg Committed by Greg Kroah-Hartman

thunderbolt: Do not handle ICM events after domain is stopped

If there is a long chain of devices connected when the driver is loaded
ICM sends device connected event for each and those are put to tb->wq
for later processing. Now if the driver gets unloaded in the middle, so
that the work queue is not yet empty it gets flushed by tb_domain_stop().
However, by that time the root switch is already removed so the driver
crashes when it tries to dereference it in ICM event handling callbacks.

Fix this by checking whether the root switch is already removed. If it
is we know that the domain is stopped and we should merely skip handling
the event.
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 70120405
...@@ -738,14 +738,6 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) ...@@ -738,14 +738,6 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
u8 link, depth; u8 link, depth;
u64 route; u64 route;
/*
* After NVM upgrade adding root switch device fails because we
* initiated reset. During that time ICM might still send
* XDomain connected message which we ignore here.
*/
if (!tb->root_switch)
return;
link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; link = pkg->link_info & ICM_LINK_INFO_LINK_MASK;
depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >>
ICM_LINK_INFO_DEPTH_SHIFT; ICM_LINK_INFO_DEPTH_SHIFT;
...@@ -1037,14 +1029,6 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) ...@@ -1037,14 +1029,6 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
if (pkg->hdr.packet_id) if (pkg->hdr.packet_id)
return; return;
/*
* After NVM upgrade adding root switch device fails because we
* initiated reset. During that time ICM might still send device
* connected message which we ignore here.
*/
if (!tb->root_switch)
return;
route = get_route(pkg->route_hi, pkg->route_lo); route = get_route(pkg->route_hi, pkg->route_lo);
authorized = pkg->link_info & ICM_LINK_INFO_APPROVED; authorized = pkg->link_info & ICM_LINK_INFO_APPROVED;
security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >> security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
...@@ -1408,19 +1392,26 @@ static void icm_handle_notification(struct work_struct *work) ...@@ -1408,19 +1392,26 @@ static void icm_handle_notification(struct work_struct *work)
mutex_lock(&tb->lock); mutex_lock(&tb->lock);
switch (n->pkg->code) { /*
case ICM_EVENT_DEVICE_CONNECTED: * When the domain is stopped we flush its workqueue but before
icm->device_connected(tb, n->pkg); * that the root switch is removed. In that case we should treat
break; * the queued events as being canceled.
case ICM_EVENT_DEVICE_DISCONNECTED: */
icm->device_disconnected(tb, n->pkg); if (tb->root_switch) {
break; switch (n->pkg->code) {
case ICM_EVENT_XDOMAIN_CONNECTED: case ICM_EVENT_DEVICE_CONNECTED:
icm->xdomain_connected(tb, n->pkg); icm->device_connected(tb, n->pkg);
break; break;
case ICM_EVENT_XDOMAIN_DISCONNECTED: case ICM_EVENT_DEVICE_DISCONNECTED:
icm->xdomain_disconnected(tb, n->pkg); icm->device_disconnected(tb, n->pkg);
break; break;
case ICM_EVENT_XDOMAIN_CONNECTED:
icm->xdomain_connected(tb, n->pkg);
break;
case ICM_EVENT_XDOMAIN_DISCONNECTED:
icm->xdomain_disconnected(tb, n->pkg);
break;
}
} }
mutex_unlock(&tb->lock); mutex_unlock(&tb->lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment