Commit 027d351c authored by Mika Westerberg's avatar Mika Westerberg Committed by David S. Miller

net: thunderbolt: Run disconnect flow asynchronously when logout is received

The control channel calls registered callbacks when control messages
such as XDomain protocol messages are received. The control channel
handling is done in a worker running on system workqueue which means the
networking driver can't run tear down flow which includes sending
disconnect request and waiting for a reply in the same worker. Otherwise
reply is never received (as the work is already running) and the
operation times out.

To fix this run disconnect ThunderboltIP flow asynchronously once
ThunderboltIP logout message is received.

Fixes: e69b6c02 ("net: Add support for networking over Thunderbolt cable")
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8e021a14
...@@ -166,6 +166,8 @@ struct tbnet_ring { ...@@ -166,6 +166,8 @@ struct tbnet_ring {
* @connected_work: Worker that finalizes the ThunderboltIP connection * @connected_work: Worker that finalizes the ThunderboltIP connection
* setup and enables DMA paths for high speed data * setup and enables DMA paths for high speed data
* transfers * transfers
* @disconnect_work: Worker that handles tearing down the ThunderboltIP
* connection
* @rx_hdr: Copy of the currently processed Rx frame. Used when a * @rx_hdr: Copy of the currently processed Rx frame. Used when a
* network packet consists of multiple Thunderbolt frames. * network packet consists of multiple Thunderbolt frames.
* In host byte order. * In host byte order.
...@@ -190,6 +192,7 @@ struct tbnet { ...@@ -190,6 +192,7 @@ struct tbnet {
int login_retries; int login_retries;
struct delayed_work login_work; struct delayed_work login_work;
struct work_struct connected_work; struct work_struct connected_work;
struct work_struct disconnect_work;
struct thunderbolt_ip_frame_header rx_hdr; struct thunderbolt_ip_frame_header rx_hdr;
struct tbnet_ring rx_ring; struct tbnet_ring rx_ring;
atomic_t frame_id; atomic_t frame_id;
...@@ -445,7 +448,7 @@ static int tbnet_handle_packet(const void *buf, size_t size, void *data) ...@@ -445,7 +448,7 @@ static int tbnet_handle_packet(const void *buf, size_t size, void *data)
case TBIP_LOGOUT: case TBIP_LOGOUT:
ret = tbnet_logout_response(net, route, sequence, command_id); ret = tbnet_logout_response(net, route, sequence, command_id);
if (!ret) if (!ret)
tbnet_tear_down(net, false); queue_work(system_long_wq, &net->disconnect_work);
break; break;
default: default:
...@@ -659,6 +662,13 @@ static void tbnet_login_work(struct work_struct *work) ...@@ -659,6 +662,13 @@ static void tbnet_login_work(struct work_struct *work)
} }
} }
static void tbnet_disconnect_work(struct work_struct *work)
{
struct tbnet *net = container_of(work, typeof(*net), disconnect_work);
tbnet_tear_down(net, false);
}
static bool tbnet_check_frame(struct tbnet *net, const struct tbnet_frame *tf, static bool tbnet_check_frame(struct tbnet *net, const struct tbnet_frame *tf,
const struct thunderbolt_ip_frame_header *hdr) const struct thunderbolt_ip_frame_header *hdr)
{ {
...@@ -881,6 +891,7 @@ static int tbnet_stop(struct net_device *dev) ...@@ -881,6 +891,7 @@ static int tbnet_stop(struct net_device *dev)
napi_disable(&net->napi); napi_disable(&net->napi);
cancel_work_sync(&net->disconnect_work);
tbnet_tear_down(net, true); tbnet_tear_down(net, true);
tb_ring_free(net->rx_ring.ring); tb_ring_free(net->rx_ring.ring);
...@@ -1195,6 +1206,7 @@ static int tbnet_probe(struct tb_service *svc, const struct tb_service_id *id) ...@@ -1195,6 +1206,7 @@ static int tbnet_probe(struct tb_service *svc, const struct tb_service_id *id)
net = netdev_priv(dev); net = netdev_priv(dev);
INIT_DELAYED_WORK(&net->login_work, tbnet_login_work); INIT_DELAYED_WORK(&net->login_work, tbnet_login_work);
INIT_WORK(&net->connected_work, tbnet_connected_work); INIT_WORK(&net->connected_work, tbnet_connected_work);
INIT_WORK(&net->disconnect_work, tbnet_disconnect_work);
mutex_init(&net->connection_lock); mutex_init(&net->connection_lock);
atomic_set(&net->command_id, 0); atomic_set(&net->command_id, 0);
atomic_set(&net->frame_id, 0); atomic_set(&net->frame_id, 0);
......
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