Commit b3e6b82a authored by KY Srinivasan's avatar KY Srinivasan Committed by David S. Miller

hv_netvsc: Wait for sub-channels to be processed during probe

The current code returns from probe without waiting for the proper handling
of subchannels that may be requested. If the netvsc driver were to be rapidly
loaded/unloaded, we can  trigger a panic as the unload will be tearing
down state that may not have been fully setup yet. We fix this issue by making
sure that we return from the probe call only after ensuring that the
sub-channel offers in flight are properly handled.

Reviewed-and-tested-by: Haiyang Zhang <haiyangz@microsoft.com
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 402b7645
...@@ -671,6 +671,8 @@ struct netvsc_device { ...@@ -671,6 +671,8 @@ struct netvsc_device {
u32 send_table[VRSS_SEND_TAB_SIZE]; u32 send_table[VRSS_SEND_TAB_SIZE];
u32 max_chn; u32 max_chn;
u32 num_chn; u32 num_chn;
spinlock_t sc_lock; /* Protects num_sc_offered variable */
u32 num_sc_offered;
atomic_t queue_sends[NR_CPUS]; atomic_t queue_sends[NR_CPUS];
/* Holds rndis device info */ /* Holds rndis device info */
......
...@@ -984,9 +984,16 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) ...@@ -984,9 +984,16 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc)
struct netvsc_device *nvscdev; struct netvsc_device *nvscdev;
u16 chn_index = new_sc->offermsg.offer.sub_channel_index; u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
int ret; int ret;
unsigned long flags;
nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj); nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj);
spin_lock_irqsave(&nvscdev->sc_lock, flags);
nvscdev->num_sc_offered--;
spin_unlock_irqrestore(&nvscdev->sc_lock, flags);
if (nvscdev->num_sc_offered == 0)
complete(&nvscdev->channel_init_wait);
if (chn_index >= nvscdev->num_chn) if (chn_index >= nvscdev->num_chn)
return; return;
...@@ -1015,8 +1022,10 @@ int rndis_filter_device_add(struct hv_device *dev, ...@@ -1015,8 +1022,10 @@ int rndis_filter_device_add(struct hv_device *dev,
u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
u32 mtu, size; u32 mtu, size;
u32 num_rss_qs; u32 num_rss_qs;
u32 sc_delta;
const struct cpumask *node_cpu_mask; const struct cpumask *node_cpu_mask;
u32 num_possible_rss_qs; u32 num_possible_rss_qs;
unsigned long flags;
rndis_device = get_rndis_device(); rndis_device = get_rndis_device();
if (!rndis_device) if (!rndis_device)
...@@ -1039,6 +1048,8 @@ int rndis_filter_device_add(struct hv_device *dev, ...@@ -1039,6 +1048,8 @@ 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;
spin_lock_init(&net_device->sc_lock);
net_device->extension = rndis_device; net_device->extension = rndis_device;
rndis_device->net_dev = net_device; rndis_device->net_dev = net_device;
...@@ -1116,6 +1127,9 @@ int rndis_filter_device_add(struct hv_device *dev, ...@@ -1116,6 +1127,9 @@ int rndis_filter_device_add(struct hv_device *dev,
num_possible_rss_qs = cpumask_weight(node_cpu_mask); num_possible_rss_qs = cpumask_weight(node_cpu_mask);
net_device->num_chn = min(num_possible_rss_qs, num_rss_qs); net_device->num_chn = min(num_possible_rss_qs, num_rss_qs);
num_rss_qs = net_device->num_chn - 1;
net_device->num_sc_offered = num_rss_qs;
if (net_device->num_chn == 1) if (net_device->num_chn == 1)
goto out; goto out;
...@@ -1157,11 +1171,25 @@ int rndis_filter_device_add(struct hv_device *dev, ...@@ -1157,11 +1171,25 @@ int rndis_filter_device_add(struct hv_device *dev,
ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn); ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn);
/*
* Wait for the host to send us the sub-channel offers.
*/
spin_lock_irqsave(&net_device->sc_lock, flags);
sc_delta = num_rss_qs - (net_device->num_chn - 1);
net_device->num_sc_offered -= sc_delta;
spin_unlock_irqrestore(&net_device->sc_lock, flags);
while (net_device->num_sc_offered != 0) {
t = wait_for_completion_timeout(&net_device->channel_init_wait, 10*HZ);
if (t == 0)
WARN(1, "Netvsc: Waiting for sub-channel processing");
}
out: out:
if (ret) { if (ret) {
net_device->max_chn = 1; net_device->max_chn = 1;
net_device->num_chn = 1; net_device->num_chn = 1;
} }
return 0; /* return 0 because primary channel can be used alone */ return 0; /* return 0 because primary channel can be used alone */
err_dev_remv: err_dev_remv:
......
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