summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaiyang Zhang <haiyangz@microsoft.com>2019-04-30 19:29:07 +0000
committerDavid S. Miller <davem@davemloft.net>2019-05-03 23:50:25 -0400
commit93aa4792c3908eac87ddd368ee0fe0564148232b (patch)
treef020f133b06ca0eaa129110bacbb41279f3bd7e5
parentea9866793d1e925b4d320eaea409263b2a568f38 (diff)
downloadlinux-93aa4792c3908eac87ddd368ee0fe0564148232b.tar.gz
linux-93aa4792c3908eac87ddd368ee0fe0564148232b.tar.bz2
linux-93aa4792c3908eac87ddd368ee0fe0564148232b.zip
hv_netvsc: fix race that may miss tx queue wakeup
When the ring buffer is almost full due to RX completion messages, a TX packet may reach the "low watermark" and cause the queue stopped. If the TX completion arrives earlier than queue stopping, the wakeup may be missed. This patch moves the check for the last pending packet to cover both EAGAIN and success cases, so the queue will be reliably waked up when necessary. Reported-and-tested-by: Stephan Klein <stephan.klein@wegfinder.at> Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/hyperv/netvsc.c15
1 files changed, 9 insertions, 6 deletions
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index e0dce373cdd9..3d4a166a49d5 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -875,12 +875,6 @@ 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 &&
- !net_device->tx_disable) {
- netif_tx_wake_queue(txq);
- ndev_ctx->eth_stats.wake_queue++;
- ret = -ENOSPC;
- }
} else {
netdev_err(ndev,
"Unable to send packet pages %u len %u, ret %d\n",
@@ -888,6 +882,15 @@ static inline int netvsc_send_pkt(
ret);
}
+ if (netif_tx_queue_stopped(txq) &&
+ atomic_read(&nvchan->queue_sends) < 1 &&
+ !net_device->tx_disable) {
+ netif_tx_wake_queue(txq);
+ ndev_ctx->eth_stats.wake_queue++;
+ if (ret == -EAGAIN)
+ ret = -ENOSPC;
+ }
+
return ret;
}