Commit 9079ce69 authored by K. Y. Srinivasan's avatar K. Y. Srinivasan Committed by Greg Kroah-Hartman

Staging: hv: netvsc: Fix a bug in accounting transmit slots

The transmit slots were manipulated without proper locking. Fix this bug by
making the variable tracking the transmit slots atomic.

This patch should be ported to prior stable kernels 2.6.32 and later.
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarHank Janssen <hjanssen@microsoft.com>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 46d2eb6d
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h> #include <linux/init.h>
#include <linux/atomic.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -45,7 +46,7 @@ ...@@ -45,7 +46,7 @@
struct net_device_context { struct net_device_context {
/* point back to our device context */ /* point back to our device context */
struct hv_device *device_ctx; struct hv_device *device_ctx;
unsigned long avail; atomic_t avail;
struct delayed_work dwork; struct delayed_work dwork;
}; };
...@@ -118,8 +119,9 @@ static void netvsc_xmit_completion(void *context) ...@@ -118,8 +119,9 @@ static void netvsc_xmit_completion(void *context)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
net_device_ctx->avail += num_pages; atomic_add(num_pages, &net_device_ctx->avail);
if (net_device_ctx->avail >= PACKET_PAGES_HIWATER) if (atomic_read(&net_device_ctx->avail) >=
PACKET_PAGES_HIWATER)
netif_wake_queue(net); netif_wake_queue(net);
} }
} }
...@@ -133,7 +135,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -133,7 +135,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
/* Add 1 for skb->data and additional one for RNDIS */ /* Add 1 for skb->data and additional one for RNDIS */
num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; num_pages = skb_shinfo(skb)->nr_frags + 1 + 1;
if (num_pages > net_device_ctx->avail) if (num_pages > atomic_read(&net_device_ctx->avail))
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
/* Allocate a netvsc packet based on # of frags. */ /* Allocate a netvsc packet based on # of frags. */
...@@ -185,8 +187,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) ...@@ -185,8 +187,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
net->stats.tx_bytes += skb->len; net->stats.tx_bytes += skb->len;
net->stats.tx_packets++; net->stats.tx_packets++;
net_device_ctx->avail -= num_pages; atomic_sub(num_pages, &net_device_ctx->avail);
if (net_device_ctx->avail < PACKET_PAGES_LOWATER) if (atomic_read(&net_device_ctx->avail) < PACKET_PAGES_LOWATER)
netif_stop_queue(net); netif_stop_queue(net);
} else { } else {
/* we are shutting down or bus overloaded, just drop packet */ /* we are shutting down or bus overloaded, just drop packet */
...@@ -345,7 +347,7 @@ static int netvsc_probe(struct hv_device *dev) ...@@ -345,7 +347,7 @@ static int netvsc_probe(struct hv_device *dev)
net_device_ctx = netdev_priv(net); net_device_ctx = netdev_priv(net);
net_device_ctx->device_ctx = dev; net_device_ctx->device_ctx = dev;
net_device_ctx->avail = ring_size; atomic_set(&net_device_ctx->avail, ring_size);
dev_set_drvdata(&dev->device, net); dev_set_drvdata(&dev->device, net);
INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp); INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_send_garp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment