Received: by 2002:ac0:a581:0:0:0:0:0 with SMTP id m1-v6csp1249462imm; Fri, 29 Jun 2018 14:21:04 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJL76N6H//ZMn2XQ6JvOhEHBbwIVw5rePJfZT2Y1DwOqHucSntVgbiCQXtEF91ccUgtw8kS X-Received: by 2002:a17:902:1127:: with SMTP id d36-v6mr16654765pla.267.1530307264295; Fri, 29 Jun 2018 14:21:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1530307264; cv=none; d=google.com; s=arc-20160816; b=IuURtr+o9RxGPMjRZfJz63T9H7rfJjBTMexluIx9Zj5Oql8QHwxC1W7ZvnapVnJ6M2 H84XUmxChgxI7yhjsw1V0AIbqlEVIffIAGY4WlE4YYjsd5mOSeZ+psEEESub4ZUwHZWM xGkm281P/HHnvpEAOL6i6ureigIb+ULOBWHKcfD2/Z4bX1y97qgC0cF5/FNH7EPvTER7 S51/Ljj0PgK992SgBQy15ZluIgS1iaIJPkqnePzA7jWmsfz/KvzqmCOSwJ4U3iSlQzRj T6YmNUnep5Do+C1NcM201XTsz98ADKyWmNSSMrUSaVkJM59IT81Jvbb3aQDNwhF6s7CT KxXA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:reply-to:message-id:date:subject:cc:to :from:arc-authentication-results; bh=6lxheF5eJy35UJVNLYiFX0yCJAaBiGGEYk8TKXRTcgE=; b=rUhQFyZz27Txb8lnVll7hRbK+/kjrrSUbQ5+YMzjCFTcupXdCb9ux0aI+otoebN3tw udMDboh82irN+L8V+fvQCv5maqvLReiqhyUsAh5P83eFzOvEThgKhAU//e2drjkyxhgL +Kuv/YTfDeSwf5rkRiliDLvDVPDer9Kz1M4EqzwmcNz6iqk0BX8STvMvEmCyiDtfxPTB I7RaEfuo7d3VkwItosMmtBCzKSh8fz4eiUxgOleeblvhi+SYGa/7j2IsiUp2jVGfdRCf /xmbxHIREN9LUPqQfsd8ltSOOdqziGXQ+MWOjIz1my6RadfB9OIP6hHC3GTmnbr2qBAT PuWQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id g9-v6si2441326pgu.489.2018.06.29.14.20.50; Fri, 29 Jun 2018 14:21:04 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965786AbeF2VI2 (ORCPT + 99 others); Fri, 29 Jun 2018 17:08:28 -0400 Received: from a2nlsmtp01-05.prod.iad2.secureserver.net ([198.71.225.49]:50832 "EHLO a2nlsmtp01-05.prod.iad2.secureserver.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932577AbeF2VI0 (ORCPT ); Fri, 29 Jun 2018 17:08:26 -0400 Received: from linuxonhyperv2.linuxonhyperv.com ([107.180.71.197]) by : HOSTING RELAY : with SMTP id Z0c0fA3Dm0KO8Z0c0fwe4v; Fri, 29 Jun 2018 14:07:25 -0700 x-originating-ip: 107.180.71.197 Received: from haiyangz by linuxonhyperv2.linuxonhyperv.com with local (Exim 4.91) (envelope-from ) id 1fZ0c0-0001mM-NX; Fri, 29 Jun 2018 14:07:24 -0700 From: Haiyang Zhang To: davem@davemloft.net, netdev@vger.kernel.org Cc: haiyangz@microsoft.com, kys@microsoft.com, sthemmin@microsoft.com, olaf@aepfle.de, vkuznets@redhat.com, devel@linuxdriverproject.org, linux-kernel@vger.kernel.org Subject: [PATCH net] hv_netvsc: split sub-channel setup into async and sync Date: Fri, 29 Jun 2018 14:07:16 -0700 Message-Id: <20180629210716.6798-1-haiyangz@linuxonhyperv.com> X-Mailer: git-send-email 2.17.1 Reply-To: haiyangz@microsoft.com X-CMAE-Envelope: MS4wfNYrlplXXz88xMWwH/YTEl8RRQXTjCSmZxeXXI3gt6cfHdy9uDbQcl2iVVuVbYCoPuEZQoWvIVbAHZwiODFspxp0jnSNHnE10h9yffRFD0LOPpXFYQQI KEyjc20GghwsTHvuXnle0tdEDkpDgDLGAyXINYRRmf0wiT5ZZYT1jQoGrXg99bJf2R0cqVxlpZ/t6MSI57mFBGXYGQ2VWxtmB75OpDL3ElW76KvTXgMWn0FJ bax0FzAeSe7qN1lNm9+a6gjA+Yjxl+MA2AHuVB8Mvq6lkKTwQ/05nm2VLlMQTUzji1p5/ZNv0w2h27w6PA88URy7jF82rLDxY5FdItVMKW/vMMrrdYVLSfCg xekjQAVCROPvzro0XqxFPFzXUopsMSw9wjpJ5ulZrfQQJ0HeL40fhjzSTryc0Ia3Apy0XiC+Hqzs2qHOf86f8Y4jwfmL+CbqDVEPYoiFztfAw3okTWbtuWtt +e+gHQaxBhRljYNRUJTAjreSzzHl5Ul4kH0Syg== Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Stephen Hemminger When doing device hotplug the sub channel must be async to avoid deadlock issues because device is discovered in softirq context. When doing changes to MTU and number of channels, the setup must be synchronous to avoid races such as when MTU and device settings are done in a single ip command. Reported-by: Thomas Walker Fixes: 8195b1396ec8 ("hv_netvsc: fix deadlock on hotplug") Fixes: 732e49850c5e ("netvsc: fix race on sub channel creation") Signed-off-by: Stephen Hemminger Signed-off-by: Haiyang Zhang --- drivers/net/hyperv/hyperv_net.h | 2 +- drivers/net/hyperv/netvsc.c | 37 ++++++++++++++++++- drivers/net/hyperv/netvsc_drv.c | 17 ++++++++- drivers/net/hyperv/rndis_filter.c | 61 ++++++------------------------- 4 files changed, 65 insertions(+), 52 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 1a924b867b07..4b6e308199d2 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -210,7 +210,7 @@ int netvsc_recv_callback(struct net_device *net, void netvsc_channel_cb(void *context); int netvsc_poll(struct napi_struct *napi, int budget); -void rndis_set_subchannel(struct work_struct *w); +int rndis_set_subchannel(struct net_device *ndev, struct netvsc_device *nvdev); int rndis_filter_open(struct netvsc_device *nvdev); int rndis_filter_close(struct netvsc_device *nvdev); struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 5d5bd513847f..8e9d0ee1572b 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -65,6 +65,41 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf) VM_PKT_DATA_INBAND, 0); } +/* Worker to setup sub channels on initial setup + * Initial hotplug event occurs in softirq context + * and can't wait for channels. + */ +static void netvsc_subchan_work(struct work_struct *w) +{ + struct netvsc_device *nvdev = + container_of(w, struct netvsc_device, subchan_work); + struct rndis_device *rdev; + int i, ret; + + /* Avoid deadlock with device removal already under RTNL */ + if (!rtnl_trylock()) { + schedule_work(w); + return; + } + + rdev = nvdev->extension; + if (rdev) { + ret = rndis_set_subchannel(rdev->ndev, nvdev); + if (ret == 0) { + netif_device_attach(rdev->ndev); + } else { + /* fallback to only primary channel */ + for (i = 1; i < nvdev->num_chn; i++) + netif_napi_del(&nvdev->chan_table[i].napi); + + nvdev->max_chn = 1; + nvdev->num_chn = 1; + } + } + + rtnl_unlock(); +} + static struct netvsc_device *alloc_net_device(void) { struct netvsc_device *net_device; @@ -81,7 +116,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); + INIT_WORK(&net_device->subchan_work, netvsc_subchan_work); return net_device; } diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index fe2256bf1d13..dd1d6e115145 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -905,8 +905,20 @@ static int netvsc_attach(struct net_device *ndev, if (IS_ERR(nvdev)) return PTR_ERR(nvdev); - /* Note: enable and attach happen when sub-channels setup */ + if (nvdev->num_chn > 1) { + ret = rndis_set_subchannel(ndev, nvdev); + + /* if unavailable, just proceed with one queue */ + if (ret) { + nvdev->max_chn = 1; + nvdev->num_chn = 1; + } + } + + /* In any case device is now ready */ + netif_device_attach(ndev); + /* Note: enable and attach happen when sub-channels setup */ netif_carrier_off(ndev); if (netif_running(ndev)) { @@ -2089,6 +2101,9 @@ static int netvsc_probe(struct hv_device *dev, memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); + if (nvdev->num_chn > 1) + schedule_work(&nvdev->subchan_work); + /* hw_features computed in rndis_netdev_set_hwcaps() */ net->features = net->hw_features | NETIF_F_HIGHDMA | NETIF_F_SG | diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 5428bb261102..9b4e3c3787e5 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1062,29 +1062,15 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) * This breaks overlap of processing the host message for the * new primary channel with the initialization of sub-channels. */ -void rndis_set_subchannel(struct work_struct *w) +int rndis_set_subchannel(struct net_device *ndev, struct netvsc_device *nvdev) { - struct netvsc_device *nvdev - = container_of(w, struct netvsc_device, subchan_work); struct nvsp_message *init_packet = &nvdev->channel_init_pkt; - struct net_device_context *ndev_ctx; - struct rndis_device *rdev; - struct net_device *ndev; - struct hv_device *hv_dev; + struct net_device_context *ndev_ctx = netdev_priv(ndev); + struct hv_device *hv_dev = ndev_ctx->device_ctx; + struct rndis_device *rdev = nvdev->extension; int i, ret; - if (!rtnl_trylock()) { - schedule_work(w); - return; - } - - rdev = nvdev->extension; - if (!rdev) - goto unlock; /* device was removed */ - - ndev = rdev->ndev; - ndev_ctx = netdev_priv(ndev); - hv_dev = ndev_ctx->device_ctx; + ASSERT_RTNL(); memset(init_packet, 0, sizeof(struct nvsp_message)); init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL; @@ -1100,13 +1086,13 @@ void rndis_set_subchannel(struct work_struct *w) VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret) { netdev_err(ndev, "sub channel allocate send failed: %d\n", ret); - goto failed; + return ret; } wait_for_completion(&nvdev->channel_init_wait); if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) { netdev_err(ndev, "sub channel request failed\n"); - goto failed; + return -EIO; } nvdev->num_chn = 1 + @@ -1125,21 +1111,7 @@ void rndis_set_subchannel(struct work_struct *w) for (i = 0; i < VRSS_SEND_TAB_SIZE; i++) ndev_ctx->tx_table[i] = i % nvdev->num_chn; - netif_device_attach(ndev); - rtnl_unlock(); - return; - -failed: - /* fallback to only primary channel */ - for (i = 1; i < nvdev->num_chn; i++) - netif_napi_del(&nvdev->chan_table[i].napi); - - nvdev->max_chn = 1; - nvdev->num_chn = 1; - - netif_device_attach(ndev); -unlock: - rtnl_unlock(); + return 0; } static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device, @@ -1360,21 +1332,12 @@ struct netvsc_device *rndis_filter_device_add(struct hv_device *dev, netif_napi_add(net, &net_device->chan_table[i].napi, netvsc_poll, NAPI_POLL_WEIGHT); - if (net_device->num_chn > 1) - schedule_work(&net_device->subchan_work); + return net_device; out: - /* if unavailable, just proceed with one queue */ - if (ret) { - net_device->max_chn = 1; - net_device->num_chn = 1; - } - - /* No sub channels, device is ready */ - if (net_device->num_chn == 1) - netif_device_attach(net); - - return net_device; + /* setting up multiple channels failed */ + net_device->max_chn = 1; + net_device->num_chn = 1; err_dev_remv: rndis_filter_device_remove(dev, net_device); -- 2.17.1