Commit 83a27052 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

virtio-net: fix a race on 32bit arches

commit 3fa2a1df (virtio-net: per cpu 64 bit stats (v2)) added a race
on 32bit arches.

We must use separate syncp for rx and tx path as they can be run at the
same time on different cpus. Thus one sequence increment can be lost and
readers spin forever.
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Acked-by: default avatarRusty Russell <rusty@rustcorp.com.au>
Acked-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7dbb4918
...@@ -42,7 +42,8 @@ module_param(gso, bool, 0444); ...@@ -42,7 +42,8 @@ module_param(gso, bool, 0444);
#define VIRTNET_DRIVER_VERSION "1.0.0" #define VIRTNET_DRIVER_VERSION "1.0.0"
struct virtnet_stats { struct virtnet_stats {
struct u64_stats_sync syncp; struct u64_stats_sync tx_syncp;
struct u64_stats_sync rx_syncp;
u64 tx_bytes; u64 tx_bytes;
u64 tx_packets; u64 tx_packets;
...@@ -300,10 +301,10 @@ static void receive_buf(struct net_device *dev, void *buf, unsigned int len) ...@@ -300,10 +301,10 @@ static void receive_buf(struct net_device *dev, void *buf, unsigned int len)
hdr = skb_vnet_hdr(skb); hdr = skb_vnet_hdr(skb);
u64_stats_update_begin(&stats->syncp); u64_stats_update_begin(&stats->rx_syncp);
stats->rx_bytes += skb->len; stats->rx_bytes += skb->len;
stats->rx_packets++; stats->rx_packets++;
u64_stats_update_end(&stats->syncp); u64_stats_update_end(&stats->rx_syncp);
if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
pr_debug("Needs csum!\n"); pr_debug("Needs csum!\n");
...@@ -565,10 +566,10 @@ static unsigned int free_old_xmit_skbs(struct virtnet_info *vi) ...@@ -565,10 +566,10 @@ static unsigned int free_old_xmit_skbs(struct virtnet_info *vi)
while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) { while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) {
pr_debug("Sent skb %p\n", skb); pr_debug("Sent skb %p\n", skb);
u64_stats_update_begin(&stats->syncp); u64_stats_update_begin(&stats->tx_syncp);
stats->tx_bytes += skb->len; stats->tx_bytes += skb->len;
stats->tx_packets++; stats->tx_packets++;
u64_stats_update_end(&stats->syncp); u64_stats_update_end(&stats->tx_syncp);
tot_sgs += skb_vnet_hdr(skb)->num_sg; tot_sgs += skb_vnet_hdr(skb)->num_sg;
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -703,12 +704,16 @@ static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev, ...@@ -703,12 +704,16 @@ static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev,
u64 tpackets, tbytes, rpackets, rbytes; u64 tpackets, tbytes, rpackets, rbytes;
do { do {
start = u64_stats_fetch_begin(&stats->syncp); start = u64_stats_fetch_begin(&stats->tx_syncp);
tpackets = stats->tx_packets; tpackets = stats->tx_packets;
tbytes = stats->tx_bytes; tbytes = stats->tx_bytes;
} while (u64_stats_fetch_retry(&stats->tx_syncp, start));
do {
start = u64_stats_fetch_begin(&stats->rx_syncp);
rpackets = stats->rx_packets; rpackets = stats->rx_packets;
rbytes = stats->rx_bytes; rbytes = stats->rx_bytes;
} while (u64_stats_fetch_retry(&stats->syncp, start)); } while (u64_stats_fetch_retry(&stats->rx_syncp, start));
tot->rx_packets += rpackets; tot->rx_packets += rpackets;
tot->tx_packets += tpackets; tot->tx_packets += tpackets;
......
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