Received: by 10.213.65.68 with SMTP id h4csp99565imn; Thu, 15 Mar 2018 10:43:07 -0700 (PDT) X-Google-Smtp-Source: AG47ELuSb+NncF1n468t6WPi0CkpnQ27QkqIcAVcQtsSLaPv5WpmCFApKQpSy6tyVISYGyhDHKP1 X-Received: by 10.99.190.11 with SMTP id l11mr1422441pgf.269.1521135787705; Thu, 15 Mar 2018 10:43:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1521135787; cv=none; d=google.com; s=arc-20160816; b=rf5h5NnHOUtvMOE0DoErJmoPm5UZyHhtosp1XVYQdj1c4evoX2GYVB6u7QocpM7Bd8 LPoUtaTOUZ3k37/bZZvZDfY+OD6oX5NWly3a2C4os6WDOtPAhalDHUOgky2TPdXj2JKf fq5/hd9feTZv8hqWagab7Sjz9w2Xfy/41v7gSxHXyRkekCyp9uphxgGRAo+JOtkd02+y 2vOklUt3JcArd71ycgQnKwpdkDodFMscvfZZvj3BB9Hgc01MMUhLD/ZQ0Bv+VyOobtzE iNwUclkC5UH4cIP43rcHwwu+5lKV8oijQQkZ1PayMxHUN0Oh3AUAAXH7FT2TyMj3L8c7 gwfw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:subject:cc:to:from:date :dkim-signature:arc-authentication-results; bh=wtkQ8kJFeSC5NNNlwdAoyyc8THT/iXJtrZ6UYF6ftqI=; b=eNcMII0CHDq0ZNScHz7XbgJhErdqnd8qxpygC2bVZObKiNHH1JATbTwJicKOmzOAKi axYDPPKdTPZYpqbEc9fPb/KznE8zy0ImR7wcreWQoqhWiFNMbYqzHTree04dgXwm4rep Gee9ndj9aOUy/n42OcmNfsKWM9HHb0HI5RPTJf3E0sq83pGGNqN8m3APM5kQKSzAqkdG ZEleEkoMmwMmxJzIA6izNoWbt5WJPgfs424/fNxJ0IUQcLf8o4BdpJRA5EhAdU/zQ+NM peZ9fHDGxIa/Qw4rdclaXfO0yvuNpVBVMk1LR57gO4Yqb7kQ2nV6LhgfQZbPNXK9Ci7j jMCg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@networkplumber-org.20150623.gappssmtp.com header.s=20150623 header.b=Uo2Hdoea; 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 m4si3726959pgc.351.2018.03.15.10.42.28; Thu, 15 Mar 2018 10:43:07 -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; dkim=pass header.i=@networkplumber-org.20150623.gappssmtp.com header.s=20150623 header.b=Uo2Hdoea; 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 S932391AbeCORlC (ORCPT + 99 others); Thu, 15 Mar 2018 13:41:02 -0400 Received: from mail-pf0-f196.google.com ([209.85.192.196]:38583 "EHLO mail-pf0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751864AbeCORlA (ORCPT ); Thu, 15 Mar 2018 13:41:00 -0400 Received: by mail-pf0-f196.google.com with SMTP id d26so3098937pfn.5 for ; Thu, 15 Mar 2018 10:41:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=wtkQ8kJFeSC5NNNlwdAoyyc8THT/iXJtrZ6UYF6ftqI=; b=Uo2Hdoea9Eei3baMiKYrfN1gyhbCEwZSvsZZoa0oV4RcCJWEQCY7XtOdtwlS8zsYMH Vja/+FvuEd2nDdx1RiV7dmlIGpsLOcLwlBhuk60tGutaHTgfstueRGbc+90gYuPTWW64 LfKiAnnTun0BhfI7CBBOEMjCO8oGFAaipGfWfH5D2a6H+icWmHZfEy0mh3xvxOMnPH04 k5DFq9TKV5pRq5S8vwgBeGmFVw+x79Atbw9ggAiGPffs7KgeE1zsQXSX6q3hT0RzxQa7 hIl9vWdptJhEk7f9egmMyVxxuafswyzpV4w81Aie+AjcsWtnVZTM0zpcjW9gKjVnU3bf Q9tg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=wtkQ8kJFeSC5NNNlwdAoyyc8THT/iXJtrZ6UYF6ftqI=; b=XGV0BxoWEdwNDJ1uMByoNUAwDXT3Z4vUvz0T2lW33DannAp7NavXUpiDQKA5xu0i/l r45ehsadvkXN/kJCpPqL+FZSdMTnPpeJUe/+gig46p7/2UgckmfGZyoEwjEhQ+PTUpn4 M0EMSchuMxrpYJTX8njMLVdaGCRgtauuEdKcaz3VMxPE+Wm9IDDAnwF4Q3Nr7lMrGiJE z6Uj8MwG2M0mMytd1uOXz3KrnQ7C7YzDB16fxgJITAivOWrfb8dbn5JdNEIQUpcDq1w5 Gc/pdNcoe7LDSHxexVT5R/D6SvanjM1NHr6nWWN9O7OQGjBScQcumZsXnD6f0Fec38Ws ExRg== X-Gm-Message-State: AElRT7HD6aDi3tDJgT5rAg7kGkaqH4jWshd566qcSGw4oW53ro165UNv 7Vq5qIWxOEp29jFWuYLDmhYv7A== X-Received: by 10.99.105.202 with SMTP id e193mr7137888pgc.84.1521135659717; Thu, 15 Mar 2018 10:40:59 -0700 (PDT) Received: from xeon-e3 (204-195-71-95.wavecable.com. [204.195.71.95]) by smtp.gmail.com with ESMTPSA id w24sm11896504pfl.14.2018.03.15.10.40.59 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 15 Mar 2018 10:40:59 -0700 (PDT) Date: Thu, 15 Mar 2018 10:40:52 -0700 From: Stephen Hemminger To: Mohammed Gamal Cc: netdev@vger.kernel.org, sthemmin@microsoft.com, otubo@redhat.com, linux-kernel@vger.kernel.org, devel@linuxdriverproject.org, vkuznets@redhat.com, davem@davemloft.net Subject: Re: [PATCH] hv_netvsc: Make sure out channel is fully opened on send Message-ID: <20180315104052.14698f66@xeon-e3> In-Reply-To: <1521131053.30828.1.camel@redhat.com> References: <1520968010-20733-1-git-send-email-mgamal@redhat.com> <20180313123521.5b486da1@xeon-e3> <1521019321.8260.1.camel@redhat.com> <1521131053.30828.1.camel@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, 15 Mar 2018 17:24:13 +0100 Mohammed Gamal wrote: > On Wed, 2018-03-14 at 10:22 +0100, Mohammed Gamal wrote: > > On Tue, 2018-03-13 at 12:35 -0700, Stephen Hemminger wrote: =20 > > > On Tue, 13 Mar 2018 20:06:50 +0100 > > > Mohammed Gamal wrote: > > > =20 > > > > Dring high network traffic changes to network interface > > > > parameters > > > > such as number of channels or MTU can cause a kernel panic with a > > > > NULL > > > > pointer dereference. This is due to netvsc_device_remove() being > > > > called and deallocating the channel ring buffers, which can then > > > > be > > > > accessed by netvsc_send_pkt() before they're allocated on calling > > > > netvsc_device_add() > > > >=20 > > > > The patch fixes this problem by checking the channel state and > > > > returning > > > > ENODEV if not yet opened. We also move the call to > > > > hv_ringbuf_avail_percent() > > > > which may access the uninitialized ring buffer. > > > >=20 > > > > Signed-off-by: Mohammed Gamal > > > > --- > > > > =C2=A0drivers/net/hyperv/netvsc.c | 5 +++-- > > > > =C2=A01 file changed, 3 insertions(+), 2 deletions(-) > > > >=20 > > > > diff --git a/drivers/net/hyperv/netvsc.c > > > > b/drivers/net/hyperv/netvsc.c > > > > index 0265d70..44a8358 100644 > > > > --- a/drivers/net/hyperv/netvsc.c > > > > +++ b/drivers/net/hyperv/netvsc.c > > > > @@ -757,7 +757,7 @@ static inline int netvsc_send_pkt( > > > > =C2=A0 struct netdev_queue *txq =3D netdev_get_tx_queue(ndev, > > > > packet->q_idx); > > > > =C2=A0 u64 req_id; > > > > =C2=A0 int ret; > > > > - u32 ring_avail =3D hv_ringbuf_avail_percent(&out_channel- =20 > > > > > outbound); =20 > > > >=20 > > > > + u32 ring_avail; > > > > =C2=A0 > > > > =C2=A0 nvmsg.hdr.msg_type =3D NVSP_MSG1_TYPE_SEND_RNDIS_PKT; > > > > =C2=A0 if (skb) > > > > @@ -773,7 +773,7 @@ static inline int netvsc_send_pkt( > > > > =C2=A0 > > > > =C2=A0 req_id =3D (ulong)skb; > > > > =C2=A0 > > > > - if (out_channel->rescind) > > > > + if (out_channel->rescind || out_channel->state !=3D > > > > CHANNEL_OPENED_STATE) > > > > =C2=A0 return -ENODEV; > > > > =C2=A0 > > > > =C2=A0 if (packet->page_buf_cnt) { > > > > @@ -791,6 +791,7 @@ static inline int netvsc_send_pkt( > > > > =C2=A0 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0VMBUS_DATA_PACK= ET_FLAG_CO > > > > MP > > > > LETION_REQUESTED); > > > > =C2=A0 } > > > > =C2=A0 > > > > + ring_avail =3D hv_ringbuf_avail_percent(&out_channel- =20 > > > > > outbound); =20 > > > >=20 > > > > =C2=A0 if (ret =3D=3D 0) { > > > > =C2=A0 atomic_inc_return(&nvchan->queue_sends); > > > > =C2=A0 =20 > > >=20 > > > Thanks for your patch. Yes there are races with the current update > > > logic. The root cause goes higher up in the flow; the send queues > > > should > > > be stopped before netvsc_device_remove is called. Solving it where > > > you tried > > > to is racy and not going to work reliably. > > >=20 > > > Network patches should go to netdev@vger.kernel.org > > >=20 > > > You can't move the ring_avail check until after the > > > vmbus_sendpacket > > > because > > > that will break the flow control logic. > > > =20 > >=20 > > Why? I don't see ring_avail being used before that point. =20 >=20 > Ah, stupid me. vmbus_sendpacket() will write to the ring buffer and > that means that ring_avail value will be different than the expected. >=20 > > =20 > > > Instead, you should just move the avail_read check until just after > > > the existing rescind > > > check. > > >=20 > > > Also, you shouldn't need to check for OPENED_STATE, just rescind is > > > enough. =20 > >=20 > > That rarely mitigated the race. channel->rescind flag is set on vmbus > > exit - called on module unload - and when a rescind offer is received > > from the host, which AFAICT doesn't happen on every call to > > netvsc_device_remove, so it's quite possible that the ringbuffer is > > accessed before it's allocated again on channel open and hence the > > check for OPENED_STAT - which is only set after all vmbus data is > > initialized. > > =20 >=20 > Perhaps I haven't been clear enough. The NULL pointer dereference > happens in the call to hv_ringbuf_avail_percent() which is used to > calculate ring_avail.=C2=A0 >=20 > So we need to stop the queues before calling it if the channel's ring > buffers haven't been allocated yet, but OTOH we should only stop the > queues based upon the value of ring_avail, so this leads into a chicken > and egg situation.=C2=A0 >=20 > Is my observation here correct? Please correct me if I am wrong, > Stephen. This is a far more drastic work of the shutdown logic which I am still work= ing on. The current netvsc driver is not doing a good job of handling concurrent send/receives during ring changes. =46rom a22da18b41ad5024340dddcc989d918987836f6d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 6 Feb 2018 15:05:19 -0800 Subject: [PATCH] hv_netvsc: common detach logic Make common function for detaching internals of device during changes to MTU and RSS. Make sure no more packets are transmitted and all packets have been received before doing device teardown. Changes transmit enabling logic so that transmit queues are disabled during the period when lower device is being changed. And enabled only after sub channels are setup. This avoids issue where it could be that a packet was being sent while subchannel was not initialized. Signed-off-by: Stephen Hemminger --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc.c | 20 +-- drivers/net/hyperv/netvsc_drv.c | 268 +++++++++++++++++++++-------------= ---- drivers/net/hyperv/rndis_filter.c | 14 +- 4 files changed, 160 insertions(+), 143 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_ne= t.h index cd538d5a7986..32861036c3fc 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -212,7 +212,6 @@ void netvsc_channel_cb(void *context); int netvsc_poll(struct napi_struct *napi, int budget); =20 void rndis_set_subchannel(struct work_struct *w); -bool rndis_filter_opened(const 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 8c95a3797b2f..31b1c6c430bb 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -573,8 +573,6 @@ void netvsc_device_remove(struct hv_device *device) =3D rtnl_dereference(net_device_ctx->nvdev); int i; =20 - cancel_work_sync(&net_device->subchan_work); - netvsc_revoke_buf(device, net_device); =20 RCU_INIT_POINTER(net_device_ctx->nvdev, NULL); @@ -661,14 +659,18 @@ static void netvsc_send_tx_complete(struct netvsc_dev= ice *net_device, queue_sends =3D atomic_dec_return(&net_device->chan_table[q_idx].queue_sends); =20 - if (net_device->destroy && queue_sends =3D=3D 0) - wake_up(&net_device->wait_drain); + if (unlikely(net_device->destroy)) { + if (queue_sends =3D=3D 0) + wake_up(&net_device->wait_drain); + } else { + struct netdev_queue *txq =3D netdev_get_tx_queue(ndev, q_idx); =20 - if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) && - (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HI= WATER || - queue_sends < 1)) { - netif_tx_wake_queue(netdev_get_tx_queue(ndev, q_idx)); - ndev_ctx->eth_stats.wake_queue++; + if (netif_tx_queue_stopped(txq) && + (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_H= IWATER || + queue_sends < 1)) { + netif_tx_wake_queue(txq); + ndev_ctx->eth_stats.wake_queue++; + } } } =20 diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_dr= v.c index faea0be18924..721fac7cad81 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -46,7 +46,8 @@ =20 #include "hyperv_net.h" =20 -#define RING_SIZE_MIN 64 +#define RING_SIZE_MIN 64 +#define RETRY_MAX 2000 =20 #define LINKCHANGE_INT (2 * HZ) #define VF_TAKEOVER_INT (HZ / 10) @@ -123,10 +124,8 @@ static int netvsc_open(struct net_device *net) } =20 rdev =3D nvdev->extension; - if (!rdev->link_state) { + if (!rdev->link_state) netif_carrier_on(net); - netif_tx_wake_all_queues(net); - } =20 if (vf_netdev) { /* Setting synthetic device up transparently sets @@ -142,36 +141,25 @@ static int netvsc_open(struct net_device *net) return 0; } =20 -static int netvsc_close(struct net_device *net) +static int netvsc_wait_until_empty(struct netvsc_device *nvdev) { - struct net_device_context *net_device_ctx =3D netdev_priv(net); - struct net_device *vf_netdev - =3D rtnl_dereference(net_device_ctx->vf_netdev); - struct netvsc_device *nvdev =3D rtnl_dereference(net_device_ctx->nvdev); - int ret =3D 0; - u32 aread, i, msec =3D 10, retry =3D 0, retry_max =3D 20; - struct vmbus_channel *chn; - - netif_tx_disable(net); - - /* No need to close rndis filter if it is removed already */ - if (!nvdev) - goto out; - - ret =3D rndis_filter_close(nvdev); - if (ret !=3D 0) { - netdev_err(net, "unable to close device (ret %d).\n", ret); - return ret; - } + unsigned int retry =3D 0; + int i; =20 /* Ensure pending bytes in ring are read */ - while (true) { - aread =3D 0; + for (;;) { + u32 aread =3D 0; + for (i =3D 0; i < nvdev->num_chn; i++) { - chn =3D nvdev->chan_table[i].channel; + struct vmbus_channel *chn + =3D nvdev->chan_table[i].channel; + if (!chn) continue; =20 + /* make sure receive not running now */ + napi_synchronize(&nvdev->chan_table[i].napi); + aread =3D hv_get_bytes_to_read(&chn->inbound); if (aread) break; @@ -181,22 +169,40 @@ static int netvsc_close(struct net_device *net) break; } =20 - retry++; - if (retry > retry_max || aread =3D=3D 0) - break; + if (aread =3D=3D 0) + return 0; =20 - msleep(msec); + if (++retry > RETRY_MAX) + return -ETIMEDOUT; =20 - if (msec < 1000) - msec *=3D 2; + usleep_range(1000, 2000); } +} =20 - if (aread) { - netdev_err(net, "Ring buffer not empty after closing rndis\n"); - ret =3D -ETIMEDOUT; +static int netvsc_close(struct net_device *net) +{ + struct net_device_context *net_device_ctx =3D netdev_priv(net); + struct net_device *vf_netdev + =3D rtnl_dereference(net_device_ctx->vf_netdev); + struct netvsc_device *nvdev =3D rtnl_dereference(net_device_ctx->nvdev); + int ret; + + netif_tx_disable(net); + + /* No need to close rndis filter if it is removed already */ + if (!nvdev) + return 0; + + ret =3D rndis_filter_close(nvdev); + if (ret !=3D 0) { + netdev_err(net, "unable to close device (ret %d).\n", ret); + return ret; } =20 -out: + ret =3D netvsc_wait_until_empty(nvdev); + if (ret) + netdev_err(net, "Ring buffer not empty after closing rndis\n"); + if (vf_netdev) dev_close(vf_netdev); =20 @@ -845,16 +851,76 @@ static void netvsc_get_channels(struct net_device *ne= t, } } =20 +static int netvsc_detach(struct net_device *ndev, + struct netvsc_device *nvdev) +{ + struct net_device_context *ndev_ctx =3D netdev_priv(ndev); + struct hv_device *hdev =3D ndev_ctx->device_ctx; + int ret; + + /* Don't try continuing to try and setup sub channels */ + if (cancel_work_sync(&nvdev->subchan_work)) + nvdev->num_chn =3D 1; + + /* If device was up (receiving) then shutdown */ + if (netif_running(ndev)) { + netif_tx_disable(ndev); + + ret =3D rndis_filter_close(nvdev); + if (ret) { + netdev_err(ndev, + "unable to close device (ret %d).\n", ret); + return ret; + } + + ret =3D netvsc_wait_until_empty(nvdev); + if (ret) { + netdev_err(ndev, + "Ring buffer not empty after closing rndis\n"); + return ret; + } + } + + rndis_filter_device_remove(hdev, nvdev); + return 0; +} + +static int netvsc_attach(struct net_device *ndev, + struct netvsc_device_info *dev_info) +{ + struct net_device_context *ndev_ctx =3D netdev_priv(ndev); + struct hv_device *hdev =3D ndev_ctx->device_ctx; + struct netvsc_device *nvdev; + struct rndis_device *rdev; + int ret; + + nvdev =3D rndis_filter_device_add(hdev, dev_info); + if (IS_ERR(nvdev)) + return PTR_ERR(nvdev); + + netif_carrier_off(ndev); + + if (netif_running(ndev)) { + ret =3D rndis_filter_open(nvdev); + if (ret) + return ret; + + rdev =3D nvdev->extension; + if (!rdev->link_state) + netif_carrier_on(ndev); + } + + return 0; +} + static int netvsc_set_channels(struct net_device *net, struct ethtool_channels *channels) { struct net_device_context *net_device_ctx =3D netdev_priv(net); - struct hv_device *dev =3D net_device_ctx->device_ctx; struct netvsc_device *nvdev =3D rtnl_dereference(net_device_ctx->nvdev); unsigned int orig, count =3D channels->combined_count; struct netvsc_device_info device_info; - bool was_opened; - int ret =3D 0; + int ret; =20 /* We do not support separate count for rx, tx, or other */ if (count =3D=3D 0 || @@ -871,9 +937,6 @@ static int netvsc_set_channels(struct net_device *net, return -EINVAL; =20 orig =3D nvdev->num_chn; - was_opened =3D rndis_filter_opened(nvdev); - if (was_opened) - rndis_filter_close(nvdev); =20 memset(&device_info, 0, sizeof(device_info)); device_info.num_chn =3D count; @@ -882,28 +945,17 @@ static int netvsc_set_channels(struct net_device *net, device_info.recv_sections =3D nvdev->recv_section_cnt; device_info.recv_section_size =3D nvdev->recv_section_size; =20 - rndis_filter_device_remove(dev, nvdev); + ret =3D netvsc_detach(net, nvdev); + if (ret) + return ret; =20 - nvdev =3D rndis_filter_device_add(dev, &device_info); - if (IS_ERR(nvdev)) { - ret =3D PTR_ERR(nvdev); + ret =3D netvsc_attach(net, &device_info); + if (ret) { device_info.num_chn =3D orig; - nvdev =3D rndis_filter_device_add(dev, &device_info); - - if (IS_ERR(nvdev)) { - netdev_err(net, "restoring channel setting failed: %ld\n", - PTR_ERR(nvdev)); - return ret; - } + if (netvsc_attach(net, &device_info)) + netdev_err(net, "restoring channel setting failed\n"); } =20 - if (was_opened) - rndis_filter_open(nvdev); - - /* We may have missed link change notifications */ - net_device_ctx->last_reconfig =3D 0; - schedule_delayed_work(&net_device_ctx->dwork, 0); - return ret; } =20 @@ -969,10 +1021,8 @@ static int netvsc_change_mtu(struct net_device *ndev,= int mtu) struct net_device_context *ndevctx =3D netdev_priv(ndev); struct net_device *vf_netdev =3D rtnl_dereference(ndevctx->vf_netdev); struct netvsc_device *nvdev =3D rtnl_dereference(ndevctx->nvdev); - struct hv_device *hdev =3D ndevctx->device_ctx; int orig_mtu =3D ndev->mtu; struct netvsc_device_info device_info; - bool was_opened; int ret =3D 0; =20 if (!nvdev || nvdev->destroy) @@ -985,11 +1035,6 @@ static int netvsc_change_mtu(struct net_device *ndev,= int mtu) return ret; } =20 - netif_device_detach(ndev); - was_opened =3D rndis_filter_opened(nvdev); - if (was_opened) - rndis_filter_close(nvdev); - memset(&device_info, 0, sizeof(device_info)); device_info.num_chn =3D nvdev->num_chn; device_info.send_sections =3D nvdev->send_section_cnt; @@ -997,35 +1042,27 @@ static int netvsc_change_mtu(struct net_device *ndev= , int mtu) device_info.recv_sections =3D nvdev->recv_section_cnt; device_info.recv_section_size =3D nvdev->recv_section_size; =20 - rndis_filter_device_remove(hdev, nvdev); + ret =3D netvsc_detach(ndev, nvdev); + if (ret) + goto rollback_vf; =20 ndev->mtu =3D mtu; =20 - nvdev =3D rndis_filter_device_add(hdev, &device_info); - if (IS_ERR(nvdev)) { - ret =3D PTR_ERR(nvdev); - - /* Attempt rollback to original MTU */ - ndev->mtu =3D orig_mtu; - nvdev =3D rndis_filter_device_add(hdev, &device_info); - - if (vf_netdev) - dev_set_mtu(vf_netdev, orig_mtu); - - if (IS_ERR(nvdev)) { - netdev_err(ndev, "restoring mtu failed: %ld\n", - PTR_ERR(nvdev)); - return ret; - } - } + ret =3D netvsc_attach(ndev, &device_info); + if (ret) + goto rollback; =20 - if (was_opened) - rndis_filter_open(nvdev); + return 0; =20 - netif_device_attach(ndev); +rollback: + /* Attempt rollback to original MTU */ + ndev->mtu =3D orig_mtu; =20 - /* We may have missed link change notifications */ - schedule_delayed_work(&ndevctx->dwork, 0); + if (netvsc_attach(ndev, &device_info)) + netdev_err(ndev, "restoring mtu failed\n"); +rollback_vf: + if (vf_netdev) + dev_set_mtu(vf_netdev, orig_mtu); =20 return ret; } @@ -1531,11 +1568,9 @@ static int netvsc_set_ringparam(struct net_device *n= dev, { struct net_device_context *ndevctx =3D netdev_priv(ndev); struct netvsc_device *nvdev =3D rtnl_dereference(ndevctx->nvdev); - struct hv_device *hdev =3D ndevctx->device_ctx; struct netvsc_device_info device_info; struct ethtool_ringparam orig; u32 new_tx, new_rx; - bool was_opened; int ret =3D 0; =20 if (!nvdev || nvdev->destroy) @@ -1560,34 +1595,18 @@ static int netvsc_set_ringparam(struct net_device *= ndev, device_info.recv_sections =3D new_rx; device_info.recv_section_size =3D nvdev->recv_section_size; =20 - netif_device_detach(ndev); - was_opened =3D rndis_filter_opened(nvdev); - if (was_opened) - rndis_filter_close(nvdev); - - rndis_filter_device_remove(hdev, nvdev); - - nvdev =3D rndis_filter_device_add(hdev, &device_info); - if (IS_ERR(nvdev)) { - ret =3D PTR_ERR(nvdev); + ret =3D netvsc_detach(ndev, nvdev); + if (ret) + return ret; =20 + ret =3D netvsc_attach(ndev, &device_info); + if (ret) { device_info.send_sections =3D orig.tx_pending; device_info.recv_sections =3D orig.rx_pending; - nvdev =3D rndis_filter_device_add(hdev, &device_info); - if (IS_ERR(nvdev)) { - netdev_err(ndev, "restoring ringparam failed: %ld\n", - PTR_ERR(nvdev)); - return ret; - } - } - - if (was_opened) - rndis_filter_open(nvdev); - netif_device_attach(ndev); =20 - /* We may have missed link change notifications */ - ndevctx->last_reconfig =3D 0; - schedule_delayed_work(&ndevctx->dwork, 0); + if (netvsc_attach(ndev, &device_info)) + netdev_err(ndev, "restoring ringparam failed"); + } =20 return ret; } @@ -2072,8 +2091,8 @@ static int netvsc_probe(struct hv_device *dev, static int netvsc_remove(struct hv_device *dev) { struct net_device_context *ndev_ctx; - struct net_device *vf_netdev; - struct net_device *net; + struct net_device *vf_netdev, *net; + struct netvsc_device *nvdev; =20 net =3D hv_get_drvdata(dev); if (net =3D=3D NULL) { @@ -2083,8 +2102,6 @@ static int netvsc_remove(struct hv_device *dev) =20 ndev_ctx =3D netdev_priv(net); =20 - netif_device_detach(net); - cancel_delayed_work_sync(&ndev_ctx->dwork); =20 /* @@ -2092,14 +2109,19 @@ static int netvsc_remove(struct hv_device *dev) * removed. Also blocks mtu and channel changes. */ rtnl_lock(); + vf_netdev =3D rtnl_dereference(ndev_ctx->vf_netdev); if (vf_netdev) netvsc_unregister_vf(vf_netdev); =20 + nvdev =3D rtnl_dereference(ndev_ctx->nvdev); + if (nvdev) { + cancel_work_sync(&nvdev->subchan_work); + rndis_filter_device_remove(dev, nvdev); + } + unregister_netdevice(net); =20 - rndis_filter_device_remove(dev, - rtnl_dereference(ndev_ctx->nvdev)); rtnl_unlock(); =20 hv_set_drvdata(dev, NULL); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_f= ilter.c index a9746f9fbf4b..3ccb0c6263a8 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1122,6 +1122,7 @@ void rndis_set_subchannel(struct work_struct *w) for (i =3D 0; i < VRSS_SEND_TAB_SIZE; i++) ndev_ctx->tx_table[i] =3D i % nvdev->num_chn; =20 + netif_tx_wake_all_queues(ndev); rtnl_unlock(); return; =20 @@ -1132,6 +1133,7 @@ void rndis_set_subchannel(struct work_struct *w) =20 nvdev->max_chn =3D 1; nvdev->num_chn =3D 1; + netif_wake_queue(ndev); unlock: rtnl_unlock(); } @@ -1326,6 +1328,8 @@ struct netvsc_device *rndis_filter_device_add(struct = hv_device *dev, =20 if (net_device->num_chn > 1) schedule_work(&net_device->subchan_work); + else + netif_wake_queue(net); =20 out: /* if unavailable, just proceed with one queue */ @@ -1346,9 +1350,6 @@ void rndis_filter_device_remove(struct hv_device *dev, { struct rndis_device *rndis_dev =3D net_dev->extension; =20 - /* Don't try and setup sub channels if about to halt */ - cancel_work_sync(&net_dev->subchan_work); - /* Halt and release the rndis device */ rndis_filter_halt_device(net_dev, rndis_dev); =20 @@ -1372,10 +1373,3 @@ int rndis_filter_close(struct netvsc_device *nvdev) =20 return rndis_filter_close_device(nvdev->extension); } - -bool rndis_filter_opened(const struct netvsc_device *nvdev) -{ - const struct rndis_device *dev =3D nvdev->extension; - - return dev->state =3D=3D RNDIS_DEV_DATAINITIALIZED; -} --=20 2.16.1