linux-hyperv.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable
@ 2019-03-28 17:48 Haiyang Zhang
  2019-03-28 18:38 ` Stephen Hemminger
  2019-03-28 19:21 ` David Miller
  0 siblings, 2 replies; 6+ messages in thread
From: Haiyang Zhang @ 2019-03-28 17:48 UTC (permalink / raw)
  To: sashal, linux-hyperv
  Cc: haiyangz, kys, sthemmin, olaf, vkuznets, davem, netdev,
	linux-kernel

From: Haiyang Zhang <haiyangz@microsoft.com>

After queue stopped, the wakeup mechanism may wake it up again
when ring buffer usage is lower than a threshold. This may cause
send path panic on NULL pointer when we stopped all tx queues in
netvsc_detach and start removing the netvsc device.

This patch fix it by adding a tx_disable flag to prevent unwanted
queue wakeup.

Fixes: 7b2ee50c0cd5 ("hv_netvsc: common detach logic")
Reported-by: Mohammed Gamal <mgamal@redhat.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
---
 drivers/net/hyperv/hyperv_net.h |  1 +
 drivers/net/hyperv/netvsc.c     |  6 ++++--
 drivers/net/hyperv/netvsc_drv.c | 32 ++++++++++++++++++++++++++------
 3 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index e859ae2..49f41b6 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -987,6 +987,7 @@ struct netvsc_device {
 
 	wait_queue_head_t wait_drain;
 	bool destroy;
+	bool tx_disable; /* if true, do not wake up queue again */
 
 	/* Receive buffer allocated by us but manages by NetVSP */
 	void *recv_buf;
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 813d195..e0dce37 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -110,6 +110,7 @@ static struct netvsc_device *alloc_net_device(void)
 
 	init_waitqueue_head(&net_device->wait_drain);
 	net_device->destroy = false;
+	net_device->tx_disable = false;
 
 	net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
 	net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;
@@ -719,7 +720,7 @@ static void netvsc_send_tx_complete(struct net_device *ndev,
 	} else {
 		struct netdev_queue *txq = netdev_get_tx_queue(ndev, q_idx);
 
-		if (netif_tx_queue_stopped(txq) &&
+		if (netif_tx_queue_stopped(txq) && !net_device->tx_disable &&
 		    (hv_get_avail_to_write_percent(&channel->outbound) >
 		     RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) {
 			netif_tx_wake_queue(txq);
@@ -874,7 +875,8 @@ static inline int netvsc_send_pkt(
 	} else if (ret == -EAGAIN) {
 		netif_tx_stop_queue(txq);
 		ndev_ctx->eth_stats.stop_queue++;
-		if (atomic_read(&nvchan->queue_sends) < 1) {
+		if (atomic_read(&nvchan->queue_sends) < 1 &&
+		    !net_device->tx_disable) {
 			netif_tx_wake_queue(txq);
 			ndev_ctx->eth_stats.wake_queue++;
 			ret = -ENOSPC;
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 1a08679..0824155 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -109,6 +109,15 @@ static void netvsc_set_rx_mode(struct net_device *net)
 	rcu_read_unlock();
 }
 
+static inline void netvsc_tx_enable(struct netvsc_device *nvscdev,
+				    struct net_device *ndev)
+{
+	nvscdev->tx_disable = false;
+	mb(); /* ensure queue wake up mechanism is on */
+
+	netif_tx_wake_all_queues(ndev);
+}
+
 static int netvsc_open(struct net_device *net)
 {
 	struct net_device_context *ndev_ctx = netdev_priv(net);
@@ -129,7 +138,7 @@ static int netvsc_open(struct net_device *net)
 	rdev = nvdev->extension;
 	if (!rdev->link_state) {
 		netif_carrier_on(net);
-		netif_tx_wake_all_queues(net);
+		netvsc_tx_enable(nvdev, net);
 	}
 
 	if (vf_netdev) {
@@ -184,6 +193,17 @@ static int netvsc_wait_until_empty(struct netvsc_device *nvdev)
 	}
 }
 
+static inline void netvsc_tx_disable(struct netvsc_device *nvscdev,
+				     struct net_device *ndev)
+{
+	if (nvscdev) {
+		nvscdev->tx_disable = true;
+		mb(); /* ensure txq will not wake up after stop */
+	}
+
+	netif_tx_disable(ndev);
+}
+
 static int netvsc_close(struct net_device *net)
 {
 	struct net_device_context *net_device_ctx = netdev_priv(net);
@@ -192,7 +212,7 @@ static int netvsc_close(struct net_device *net)
 	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
 	int ret;
 
-	netif_tx_disable(net);
+	netvsc_tx_disable(nvdev, net);
 
 	/* No need to close rndis filter if it is removed already */
 	if (!nvdev)
@@ -918,7 +938,7 @@ static int netvsc_detach(struct net_device *ndev,
 
 	/* If device was up (receiving) then shutdown */
 	if (netif_running(ndev)) {
-		netif_tx_disable(ndev);
+		netvsc_tx_disable(nvdev, ndev);
 
 		ret = rndis_filter_close(nvdev);
 		if (ret) {
@@ -1906,7 +1926,7 @@ static void netvsc_link_change(struct work_struct *w)
 		if (rdev->link_state) {
 			rdev->link_state = false;
 			netif_carrier_on(net);
-			netif_tx_wake_all_queues(net);
+			netvsc_tx_enable(net_device, net);
 		} else {
 			notify = true;
 		}
@@ -1916,7 +1936,7 @@ static void netvsc_link_change(struct work_struct *w)
 		if (!rdev->link_state) {
 			rdev->link_state = true;
 			netif_carrier_off(net);
-			netif_tx_stop_all_queues(net);
+			netvsc_tx_disable(net_device, net);
 		}
 		kfree(event);
 		break;
@@ -1925,7 +1945,7 @@ static void netvsc_link_change(struct work_struct *w)
 		if (!rdev->link_state) {
 			rdev->link_state = true;
 			netif_carrier_off(net);
-			netif_tx_stop_all_queues(net);
+			netvsc_tx_disable(net_device, net);
 			event->event = RNDIS_STATUS_MEDIA_CONNECT;
 			spin_lock_irqsave(&ndev_ctx->lock, flags);
 			list_add(&event->list, &ndev_ctx->reconfig_events);
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable
  2019-03-28 17:48 [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable Haiyang Zhang
@ 2019-03-28 18:38 ` Stephen Hemminger
  2019-03-28 19:00   ` Haiyang Zhang
  2019-03-28 19:21 ` David Miller
  1 sibling, 1 reply; 6+ messages in thread
From: Stephen Hemminger @ 2019-03-28 18:38 UTC (permalink / raw)
  To: Haiyang Zhang
  Cc: sashal, linux-hyperv, haiyangz, kys, sthemmin, olaf, vkuznets,
	davem, netdev, linux-kernel

On Thu, 28 Mar 2019 17:48:45 +0000
Haiyang Zhang <haiyangz@linuxonhyperv.com> wrote:

> +static inline void netvsc_tx_enable(struct netvsc_device *nvscdev,
> +				    struct net_device *ndev)
> +{
> +	nvscdev->tx_disable = false;
> +	mb(); /* ensure queue wake up mechanism is on */
> +
> +	netif_tx_wake_all_queues(ndev);
> +}

You don't need a full mb(). virt_wmb() should be sufficient.

Could I suggest an alternative approach.
You don't need to introduce a local tx_disable flag, the only place where a wakeup
could cause problems is after a send_completion was processed during detach state.

Instead, just avoid wakeup in that place.

--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -720,6 +720,7 @@ static void netvsc_send_tx_complete(struct net_device *ndev,
                struct netdev_queue *txq = netdev_get_tx_queue(ndev, q_idx);
 
                if (netif_tx_queue_stopped(txq) &&
+                   netif_device_present(ndev) &&
                    (hv_get_avail_to_write_percent(&channel->outbound) >
                     RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) {
                        netif_tx_wake_queue(txq);

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable
  2019-03-28 18:38 ` Stephen Hemminger
@ 2019-03-28 19:00   ` Haiyang Zhang
  2019-03-28 19:42     ` Stephen Hemminger
  0 siblings, 1 reply; 6+ messages in thread
From: Haiyang Zhang @ 2019-03-28 19:00 UTC (permalink / raw)
  To: Stephen Hemminger, Haiyang Zhang
  Cc: sashal@kernel.org, linux-hyperv@vger.kernel.org, KY Srinivasan,
	Stephen Hemminger, olaf@aepfle.de, vkuznets, davem@davemloft.net,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org



> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Thursday, March 28, 2019 2:38 PM
> To: Haiyang Zhang <haiyangz@linuxonhyperv.com>
> Cc: sashal@kernel.org; linux-hyperv@vger.kernel.org; Haiyang Zhang
> <haiyangz@microsoft.com>; KY Srinivasan <kys@microsoft.com>; Stephen
> Hemminger <sthemmin@microsoft.com>; olaf@aepfle.de; vkuznets
> <vkuznets@redhat.com>; davem@davemloft.net; netdev@vger.kernel.org;
> linux-kernel@vger.kernel.org
> Subject: Re: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after
> tx_disable
> 
> On Thu, 28 Mar 2019 17:48:45 +0000
> Haiyang Zhang <haiyangz@linuxonhyperv.com> wrote:
> 
> > +static inline void netvsc_tx_enable(struct netvsc_device *nvscdev,
> > +				    struct net_device *ndev)
> > +{
> > +	nvscdev->tx_disable = false;
> > +	mb(); /* ensure queue wake up mechanism is on */
> > +
> > +	netif_tx_wake_all_queues(ndev);
> > +}
> 
> You don't need a full mb(). virt_wmb() should be sufficient.

I will make this change. 

> Could I suggest an alternative approach.
> You don't need to introduce a local tx_disable flag, the only place where a
> wakeup could cause problems is after a send_completion was processed
> during detach state.
> 
> Instead, just avoid wakeup in that place.

In netvsc_detach(), after netif_tx_disable(), we call  netvsc_wait_until_empty(nvdev);
TX patch should not be waken up again while waiting for in/out ring to becomes empty.

In my tests before this patch, there are wakeup happens before netif_device_detach(), 
so netif_device_present(ndev) is still true at that time.

In other places, like netvsc_close(), link_change(), we also don't want wakeup after tx_disable.

Thanks.
- Haiyang

> 
> --- a/drivers/net/hyperv/netvsc.c
> +++ b/drivers/net/hyperv/netvsc.c
> @@ -720,6 +720,7 @@ static void netvsc_send_tx_complete(struct
> net_device *ndev,
>                 struct netdev_queue *txq = netdev_get_tx_queue(ndev, q_idx);
> 
>                 if (netif_tx_queue_stopped(txq) &&
> +                   netif_device_present(ndev) &&
>                     (hv_get_avail_to_write_percent(&channel->outbound) >
>                      RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) {
>                         netif_tx_wake_queue(txq);

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable
  2019-03-28 17:48 [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable Haiyang Zhang
  2019-03-28 18:38 ` Stephen Hemminger
@ 2019-03-28 19:21 ` David Miller
  1 sibling, 0 replies; 6+ messages in thread
From: David Miller @ 2019-03-28 19:21 UTC (permalink / raw)
  To: haiyangz
  Cc: sashal, linux-hyperv, haiyangz, kys, sthemmin, olaf, vkuznets,
	netdev, linux-kernel

From: Haiyang Zhang <haiyangz@linuxonhyperv.com>
Date: Thu, 28 Mar 2019 17:48:45 +0000

> diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
> index 1a08679..0824155 100644
> --- a/drivers/net/hyperv/netvsc_drv.c
> +++ b/drivers/net/hyperv/netvsc_drv.c
> @@ -109,6 +109,15 @@ static void netvsc_set_rx_mode(struct net_device *net)
>  	rcu_read_unlock();
>  }
>  
> +static inline void netvsc_tx_enable(struct netvsc_device *nvscdev,
> +				    struct net_device *ndev)

Do not use inline in foo.c files, let the compiler decide.

> @@ -184,6 +193,17 @@ static int netvsc_wait_until_empty(struct netvsc_device *nvdev)
>  	}
>  }
>  
> +static inline void netvsc_tx_disable(struct netvsc_device *nvscdev,
> +				     struct net_device *ndev)
> +{

Likewise.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable
  2019-03-28 19:00   ` Haiyang Zhang
@ 2019-03-28 19:42     ` Stephen Hemminger
  2019-03-28 19:59       ` Haiyang Zhang
  0 siblings, 1 reply; 6+ messages in thread
From: Stephen Hemminger @ 2019-03-28 19:42 UTC (permalink / raw)
  To: Haiyang Zhang
  Cc: Haiyang Zhang, sashal@kernel.org, linux-hyperv@vger.kernel.org,
	KY Srinivasan, Stephen Hemminger, olaf@aepfle.de, vkuznets,
	davem@davemloft.net, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org

On Thu, 28 Mar 2019 19:00:18 +0000
Haiyang Zhang <haiyangz@microsoft.com> wrote:

> > -----Original Message-----
> > From: Stephen Hemminger <stephen@networkplumber.org>
> > Sent: Thursday, March 28, 2019 2:38 PM
> > To: Haiyang Zhang <haiyangz@linuxonhyperv.com>
> > Cc: sashal@kernel.org; linux-hyperv@vger.kernel.org; Haiyang Zhang
> > <haiyangz@microsoft.com>; KY Srinivasan <kys@microsoft.com>; Stephen
> > Hemminger <sthemmin@microsoft.com>; olaf@aepfle.de; vkuznets
> > <vkuznets@redhat.com>; davem@davemloft.net; netdev@vger.kernel.org;
> > linux-kernel@vger.kernel.org
> > Subject: Re: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after
> > tx_disable
> > 
> > On Thu, 28 Mar 2019 17:48:45 +0000
> > Haiyang Zhang <haiyangz@linuxonhyperv.com> wrote:
> >   
> > > +static inline void netvsc_tx_enable(struct netvsc_device *nvscdev,
> > > +				    struct net_device *ndev)
> > > +{
> > > +	nvscdev->tx_disable = false;
> > > +	mb(); /* ensure queue wake up mechanism is on */
> > > +
> > > +	netif_tx_wake_all_queues(ndev);
> > > +}  
> > 
> > You don't need a full mb(). virt_wmb() should be sufficient.  
> 
> I will make this change. 
> 
> > Could I suggest an alternative approach.
> > You don't need to introduce a local tx_disable flag, the only place where a
> > wakeup could cause problems is after a send_completion was processed
> > during detach state.
> > 
> > Instead, just avoid wakeup in that place.  
> 
> In netvsc_detach(), after netif_tx_disable(), we call  netvsc_wait_until_empty(nvdev);
> TX patch should not be waken up again while waiting for in/out ring to becomes empty.
> 
> In my tests before this patch, there are wakeup happens before netif_device_detach(), 
> so netif_device_present(ndev) is still true at that time.
> 
> In other places, like netvsc_close(), link_change(), we also don't want wakeup after tx_disable.
> 
> Thanks.
> - Haiyang
> 
> > 
> > --- a/drivers/net/hyperv/netvsc.c
> > +++ b/drivers/net/hyperv/netvsc.c
> > @@ -720,6 +720,7 @@ static void netvsc_send_tx_complete(struct
> > net_device *ndev,
> >                 struct netdev_queue *txq = netdev_get_tx_queue(ndev, q_idx);
> > 
> >                 if (netif_tx_queue_stopped(txq) &&
> > +                   netif_device_present(ndev) &&
> >                     (hv_get_avail_to_write_percent(&channel->outbound) >
> >                      RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) {
> >                         netif_tx_wake_queue(txq);  


Then what about doing netif_detach earlier in netvsc_detach.

The state management is already (too) complex in netvsc and adding another
boolean flag just makes it harder to understand.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable
  2019-03-28 19:42     ` Stephen Hemminger
@ 2019-03-28 19:59       ` Haiyang Zhang
  0 siblings, 0 replies; 6+ messages in thread
From: Haiyang Zhang @ 2019-03-28 19:59 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: sashal@kernel.org, linux-hyperv@vger.kernel.org, KY Srinivasan,
	Stephen Hemminger, olaf@aepfle.de, vkuznets, davem@davemloft.net,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org



> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Thursday, March 28, 2019 3:42 PM
> To: Haiyang Zhang <haiyangz@microsoft.com>
> Cc: Haiyang Zhang <haiyangz@linuxonhyperv.com>; sashal@kernel.org;
> linux-hyperv@vger.kernel.org; KY Srinivasan <kys@microsoft.com>; Stephen
> Hemminger <sthemmin@microsoft.com>; olaf@aepfle.de; vkuznets
> <vkuznets@redhat.com>; davem@davemloft.net; netdev@vger.kernel.org;
> linux-kernel@vger.kernel.org
> Subject: Re: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after
> tx_disable
> 
> On Thu, 28 Mar 2019 19:00:18 +0000
> Haiyang Zhang <haiyangz@microsoft.com> wrote:
> 
> > > -----Original Message-----
> > > From: Stephen Hemminger <stephen@networkplumber.org>
> > > Sent: Thursday, March 28, 2019 2:38 PM
> > > To: Haiyang Zhang <haiyangz@linuxonhyperv.com>
> > > Cc: sashal@kernel.org; linux-hyperv@vger.kernel.org; Haiyang Zhang
> > > <haiyangz@microsoft.com>; KY Srinivasan <kys@microsoft.com>;
> Stephen
> > > Hemminger <sthemmin@microsoft.com>; olaf@aepfle.de; vkuznets
> > > <vkuznets@redhat.com>; davem@davemloft.net;
> netdev@vger.kernel.org;
> > > linux-kernel@vger.kernel.org
> > > Subject: Re: [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup
> > > after tx_disable
> > >
> > > On Thu, 28 Mar 2019 17:48:45 +0000
> > > Haiyang Zhang <haiyangz@linuxonhyperv.com> wrote:
> > >
> > > > +static inline void netvsc_tx_enable(struct netvsc_device *nvscdev,
> > > > +				    struct net_device *ndev)
> > > > +{
> > > > +	nvscdev->tx_disable = false;
> > > > +	mb(); /* ensure queue wake up mechanism is on */
> > > > +
> > > > +	netif_tx_wake_all_queues(ndev);
> > > > +}
> > >
> > > You don't need a full mb(). virt_wmb() should be sufficient.
> >
> > I will make this change.
> >
> > > Could I suggest an alternative approach.
> > > You don't need to introduce a local tx_disable flag, the only place
> > > where a wakeup could cause problems is after a send_completion was
> > > processed during detach state.
> > >
> > > Instead, just avoid wakeup in that place.
> >
> > In netvsc_detach(), after netif_tx_disable(), we call
> > netvsc_wait_until_empty(nvdev); TX patch should not be waken up again
> while waiting for in/out ring to becomes empty.
> >
> > In my tests before this patch, there are wakeup happens before
> > netif_device_detach(), so netif_device_present(ndev) is still true at that
> time.
> >
> > In other places, like netvsc_close(), link_change(), we also don't want
> wakeup after tx_disable.
> >
> > Thanks.
> > - Haiyang
> >
> > >
> > > --- a/drivers/net/hyperv/netvsc.c
> > > +++ b/drivers/net/hyperv/netvsc.c
> > > @@ -720,6 +720,7 @@ static void netvsc_send_tx_complete(struct
> > > net_device *ndev,
> > >                 struct netdev_queue *txq = netdev_get_tx_queue(ndev,
> > > q_idx);
> > >
> > >                 if (netif_tx_queue_stopped(txq) &&
> > > +                   netif_device_present(ndev) &&
> > >                     (hv_get_avail_to_write_percent(&channel->outbound) >
> > >                      RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) {
> > >                         netif_tx_wake_queue(txq);
> 
> 
> Then what about doing netif_detach earlier in netvsc_detach.
> 
> The state management is already (too) complex in netvsc and adding another
> boolean flag just makes it harder to understand.

If we move netif_device_detach() before the netvsc_wait_until_empty(), the remaining
Packets in the receive buffer will be passed to a detached device.

Also, in case of netvsc_close() and link_change(), we don't call netif_device_detach().

Thanks,
- Haiyang

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-03-28 19:59 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-03-28 17:48 [PATCH hyperv-fixes] hv_netvsc: Fix unwanted wakeup after tx_disable Haiyang Zhang
2019-03-28 18:38 ` Stephen Hemminger
2019-03-28 19:00   ` Haiyang Zhang
2019-03-28 19:42     ` Stephen Hemminger
2019-03-28 19:59       ` Haiyang Zhang
2019-03-28 19:21 ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).