From d66ab51442211158b677c2f12310c314d9587f74 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 4 Mar 2016 15:07:43 -0800 Subject: hv_netvsc: Move subchannel waiting to rndis_filter_device_remove() During hot add, vmbus_device_register() is called from vmbus_onoffer(), on the same workqueue as the subchannel offer message work-queue, so subchannel offer won't be processed until the vmbus_device_register()/... /netvsc_probe() is done. Also, vmbus_device_register() is called with channel_mutex locked, which prevents subchannel processing too. So the "waiting for sub-channel processing" will not success in hot add case. But, in usual module loading, the netvsc_probe() is called from different code path, and doesn't fail. This patch resolves the deadlock during NIC hot-add, and speeds up NIC loading time. Signed-off-by: Haiyang Zhang Reviewed-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/rndis_filter.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index a37bbda..47d07c5 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1175,22 +1175,18 @@ int rndis_filter_device_add(struct hv_device *dev, ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn); /* - * Wait for the host to send us the sub-channel offers. + * Set the number of sub-channels to be received. */ 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: if (ret) { net_device->max_chn = 1; net_device->num_chn = 1; + net_device->num_sc_offered = 0; } return 0; /* return 0 because primary channel can be used alone */ @@ -1204,6 +1200,17 @@ void rndis_filter_device_remove(struct hv_device *dev) { struct netvsc_device *net_dev = hv_get_drvdata(dev); struct rndis_device *rndis_dev = net_dev->extension; + unsigned long t; + + /* If not all subchannel offers are complete, wait for them until + * completion to avoid race. + */ + while (net_dev->num_sc_offered > 0) { + t = wait_for_completion_timeout(&net_dev->channel_init_wait, + 10 * HZ); + if (t == 0) + WARN(1, "Netvsc: Waiting for sub-channel processing"); + } /* Halt and release the rndis device */ rndis_filter_halt_device(rndis_dev); -- cgit v1.1