Commit 732e4985 authored by stephen hemminger's avatar stephen hemminger Committed by David S. Miller

netvsc: fix race on sub channel creation

The existing sub channel code did not wait for all the sub-channels
to completely initialize. This could lead to race causing crash
in napi_netif_del() from bad list. The existing code would send
an init message, then wait only for the initial response that
the init message was received. It thought it was waiting for
sub channels but really the init response did the wakeup.

The new code keeps track of the number of open channels and
waits until that many are open.

Other issues here were:
  * host might return less sub-channels than was requested.
  * the new init status is not valid until after init was completed.

Fixes: b3e6b82a ("hv_netvsc: Wait for sub-channels to be processed during probe")
Signed-off-by: default avatarStephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2c460621
...@@ -765,7 +765,8 @@ struct netvsc_device { ...@@ -765,7 +765,8 @@ struct netvsc_device {
u32 max_chn; u32 max_chn;
u32 num_chn; u32 num_chn;
refcount_t sc_offered; atomic_t open_chn;
wait_queue_head_t subchan_open;
struct rndis_device *extension; struct rndis_device *extension;
......
...@@ -78,6 +78,7 @@ static struct netvsc_device *alloc_net_device(void) ...@@ -78,6 +78,7 @@ static struct netvsc_device *alloc_net_device(void)
net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT; net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT; net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
init_completion(&net_device->channel_init_wait); init_completion(&net_device->channel_init_wait);
init_waitqueue_head(&net_device->subchan_open);
return net_device; return net_device;
} }
......
...@@ -1048,8 +1048,8 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) ...@@ -1048,8 +1048,8 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc)
else else
netif_napi_del(&nvchan->napi); netif_napi_del(&nvchan->napi);
if (refcount_dec_and_test(&nvscdev->sc_offered)) atomic_inc(&nvscdev->open_chn);
complete(&nvscdev->channel_init_wait); wake_up(&nvscdev->subchan_open);
} }
int rndis_filter_device_add(struct hv_device *dev, int rndis_filter_device_add(struct hv_device *dev,
...@@ -1090,8 +1090,6 @@ int rndis_filter_device_add(struct hv_device *dev, ...@@ -1090,8 +1090,6 @@ int rndis_filter_device_add(struct hv_device *dev,
net_device->max_chn = 1; net_device->max_chn = 1;
net_device->num_chn = 1; net_device->num_chn = 1;
refcount_set(&net_device->sc_offered, 0);
net_device->extension = rndis_device; net_device->extension = rndis_device;
rndis_device->ndev = net; rndis_device->ndev = net;
...@@ -1221,11 +1219,11 @@ int rndis_filter_device_add(struct hv_device *dev, ...@@ -1221,11 +1219,11 @@ int rndis_filter_device_add(struct hv_device *dev,
rndis_device->ind_table[i] = ethtool_rxfh_indir_default(i, rndis_device->ind_table[i] = ethtool_rxfh_indir_default(i,
net_device->num_chn); net_device->num_chn);
atomic_set(&net_device->open_chn, 1);
num_rss_qs = net_device->num_chn - 1; num_rss_qs = net_device->num_chn - 1;
if (num_rss_qs == 0) if (num_rss_qs == 0)
return 0; return 0;
refcount_set(&net_device->sc_offered, num_rss_qs);
vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
init_packet = &net_device->channel_init_pkt; init_packet = &net_device->channel_init_pkt;
...@@ -1242,15 +1240,19 @@ int rndis_filter_device_add(struct hv_device *dev, ...@@ -1242,15 +1240,19 @@ int rndis_filter_device_add(struct hv_device *dev,
if (ret) if (ret)
goto out; goto out;
wait_for_completion(&net_device->channel_init_wait);
if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) { if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
} }
wait_for_completion(&net_device->channel_init_wait);
net_device->num_chn = 1 + net_device->num_chn = 1 +
init_packet->msg.v5_msg.subchn_comp.num_subchannels; init_packet->msg.v5_msg.subchn_comp.num_subchannels;
/* wait for all sub channels to open */
wait_event(net_device->subchan_open,
atomic_read(&net_device->open_chn) == net_device->num_chn);
/* ignore failues from setting rss parameters, still have channels */ /* ignore failues from setting rss parameters, still have channels */
rndis_filter_set_rss_param(rndis_device, netvsc_hash_key, rndis_filter_set_rss_param(rndis_device, netvsc_hash_key,
net_device->num_chn); net_device->num_chn);
......
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