diff options
author | Stephen Hemminger <stephen@networkplumber.org> | 2017-09-06 22:53:05 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-09-11 23:21:30 +0200 |
commit | 8195b1396ec86dddbba443c74b2188b423556c74 (patch) | |
tree | a09b122fb16f158c00385cfc07bcea9e88722d59 /drivers/net/hyperv/netvsc.c | |
parent | mlxsw: spectrum: Fix EEPROM access in case of SFP/SFP+ (diff) | |
download | linux-8195b1396ec86dddbba443c74b2188b423556c74.tar.xz linux-8195b1396ec86dddbba443c74b2188b423556c74.zip |
hv_netvsc: fix deadlock on hotplug
When a virtual device is added dynamically (via host console), then
the vmbus sends an offer message for the primary channel. The processing
of this message for networking causes the network device to then
initialize the sub channels.
The problem is that setting up the sub channels needs to wait until
the subsequent subchannel offers have been processed. These offers
come in on the same ring buffer and work queue as where the primary
offer is being processed; leading to a deadlock.
This did not happen in older kernels, because the sub channel waiting
logic was broken (it wasn't really waiting).
The solution is to do the sub channel setup in its own work queue
context that is scheduled by the primary channel setup; and then
happens later.
Fixes: 732e49850c5e ("netvsc: fix race on sub channel creation")
Reported-by: Dexuan Cui <decui@microsoft.com>
Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/hyperv/netvsc.c')
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0062b802676f..a5511b7326af 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -81,6 +81,7 @@ static struct netvsc_device *alloc_net_device(void) init_completion(&net_device->channel_init_wait); init_waitqueue_head(&net_device->subchan_open); + INIT_WORK(&net_device->subchan_work, rndis_set_subchannel); return net_device; } @@ -557,6 +558,8 @@ void netvsc_device_remove(struct hv_device *device) = rtnl_dereference(net_device_ctx->nvdev); int i; + cancel_work_sync(&net_device->subchan_work); + netvsc_disconnect_vsp(device); RCU_INIT_POINTER(net_device_ctx->nvdev, NULL); |