Use per-cpu variables to maintain 64 bit statistics. Compile tested only. Signed-off-by: Stephen Hemminger <shemminger at vyatta.com> --- a/drivers/net/virtio_net.c 2011-06-14 15:18:46.448596355 -0400 +++ b/drivers/net/virtio_net.c 2011-06-15 09:54:22.914426067 -0400 @@ -40,6 +40,15 @@ module_param(gso, bool, 0444); #define VIRTNET_SEND_COMMAND_SG_MAX 2 +struct virtnet_stats { + struct u64_stats_sync syncp; + u64 tx_bytes; + u64 tx_packets; + + u64 rx_bytes; + u64 rx_packets; +}; + struct virtnet_info { struct virtio_device *vdev; struct virtqueue *rvq, *svq, *cvq; @@ -56,6 +65,9 @@ struct virtnet_info { /* Host will merge rx buffers for big packets (shake it! shake it!) */ bool mergeable_rx_bufs; + /* Active statistics */ + struct virtnet_stats __percpu *stats; + /* Work struct for refilling if we run low on memory. */ struct delayed_work refill; @@ -209,7 +221,6 @@ static int receive_mergeable(struct virt skb->dev->stats.rx_length_errors++; return -EINVAL; } - page = virtqueue_get_buf(vi->rvq, &len); if (!page) { pr_debug("%s: rx error: %d buffers missing\n", @@ -217,6 +228,7 @@ static int receive_mergeable(struct virt skb->dev->stats.rx_length_errors++; return -EINVAL; } + if (len > PAGE_SIZE) len = PAGE_SIZE; @@ -230,6 +242,7 @@ static int receive_mergeable(struct virt static void receive_buf(struct net_device *dev, void *buf, unsigned int len) { struct virtnet_info *vi = netdev_priv(dev); + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); struct sk_buff *skb; struct page *page; struct skb_vnet_hdr *hdr; @@ -265,8 +278,11 @@ static void receive_buf(struct net_devic hdr = skb_vnet_hdr(skb); skb->truesize += skb->data_len; - dev->stats.rx_bytes += skb->len; - dev->stats.rx_packets++; + + u64_stats_update_begin(&stats->syncp); + stats->rx_bytes += skb->len; + stats->rx_packets++; + u64_stats_update_begin(&stats->syncp); if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { pr_debug("Needs csum!\n"); @@ -515,11 +531,16 @@ static unsigned int free_old_xmit_skbs(s { struct sk_buff *skb; unsigned int len, tot_sgs = 0; + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) { pr_debug("Sent skb %p\n", skb); - vi->dev->stats.tx_bytes += skb->len; - vi->dev->stats.tx_packets++; + + u64_stats_update_begin(&stats->syncp); + stats->tx_bytes += skb->len; + stats->tx_packets++; + u64_stats_update_begin(&stats->syncp); + tot_sgs += skb_vnet_hdr(skb)->num_sg; dev_kfree_skb_any(skb); } @@ -641,6 +662,40 @@ static int virtnet_set_mac_address(struc return 0; } +static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev, + struct rtnl_link_stats64 *tot) +{ + struct virtnet_info *vi = netdev_priv(dev); + int cpu; + unsigned int start; + + for_each_possible_cpu(cpu) { + struct virtnet_stats __percpu *stats + = per_cpu_ptr(vi->stats, cpu); + u64 tpackets, tbytes, rpackets, rbytes; + + do { + start = u64_stats_fetch_begin(&stats->syncp); + tpackets = stats->tx_packets; + tbytes = stats->tx_bytes; + rpackets = stats->rx_packets; + rbytes = stats->rx_bytes; + } while (u64_stats_fetch_retry(&stats->syncp, start)); + + tot->rx_packets += rpackets; + tot->tx_packets += tpackets; + tot->rx_bytes += rbytes; + tot->tx_bytes += tbytes; + } + + tot->tx_dropped = dev->stats.tx_dropped; + tot->rx_dropped = dev->stats.rx_dropped; + tot->rx_length_errors = dev->stats.rx_length_errors; + tot->rx_frame_errors = dev->stats.rx_frame_errors; + + return tot; +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void virtnet_netpoll(struct net_device *dev) { @@ -650,6 +705,14 @@ static void virtnet_netpoll(struct net_d } #endif +static void virtnet_free(struct net_device *dev) +{ + struct virtnet_info *vi = netdev_priv(dev); + + free_percpu(vi->stats); + free_netdev(dev); +} + static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); @@ -835,6 +898,7 @@ static const struct net_device_ops virtn .ndo_set_mac_address = virtnet_set_mac_address, .ndo_set_rx_mode = virtnet_set_rx_mode, .ndo_change_mtu = virtnet_change_mtu, + .ndo_get_stats64 = virtnet_stats, .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -895,6 +959,8 @@ static int virtnet_probe(struct virtio_d /* Set up network device as normal. */ dev->netdev_ops = &virtnet_netdev; dev->features = NETIF_F_HIGHDMA; + dev->destructor = virtnet_free; + SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops); SET_NETDEV_DEV(dev, &vdev->dev); @@ -939,6 +1005,11 @@ static int virtnet_probe(struct virtio_d vi->vdev = vdev; vdev->priv = vi; vi->pages = NULL; + vi->stats = alloc_percpu(struct virtnet_stats); + err = -ENOMEM; + if (vi->stats == NULL) + goto free; + INIT_DELAYED_WORK(&vi->refill, refill_work); sg_init_table(vi->rx_sg, ARRAY_SIZE(vi->rx_sg)); sg_init_table(vi->tx_sg, ARRAY_SIZE(vi->tx_sg)); @@ -958,7 +1029,7 @@ static int virtnet_probe(struct virtio_d err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); if (err) - goto free; + goto free_stats; vi->rvq = vqs[0]; vi->svq = vqs[1]; @@ -1003,6 +1074,8 @@ unregister: cancel_delayed_work_sync(&vi->refill); free_vqs: vdev->config->del_vqs(vdev); +free_stats: + free_percpu(vi->stats); free: free_netdev(dev); return err;
Le mercredi 15 juin 2011 ? 11:43 -0400, Stephen Hemminger a ?crit :> Use per-cpu variables to maintain 64 bit statistics. > Compile tested only. > > Signed-off-by: Stephen Hemminger <shemminger at vyatta.com> > > --- a/drivers/net/virtio_net.c 2011-06-14 15:18:46.448596355 -0400 > +++ b/drivers/net/virtio_net.c 2011-06-15 09:54:22.914426067 -0400 > @@ -40,6 +40,15 @@ module_param(gso, bool, 0444); > > #define VIRTNET_SEND_COMMAND_SG_MAX 2 > > +struct virtnet_stats { > + struct u64_stats_sync syncp; > + u64 tx_bytes; > + u64 tx_packets; > + > + u64 rx_bytes; > + u64 rx_packets; > +}; > + > struct virtnet_info { > struct virtio_device *vdev; > struct virtqueue *rvq, *svq, *cvq; > @@ -56,6 +65,9 @@ struct virtnet_info { > /* Host will merge rx buffers for big packets (shake it! shake it!) */ > bool mergeable_rx_bufs; > > + /* Active statistics */ > + struct virtnet_stats __percpu *stats; > + > /* Work struct for refilling if we run low on memory. */ > struct delayed_work refill; > > @@ -209,7 +221,6 @@ static int receive_mergeable(struct virt > skb->dev->stats.rx_length_errors++; > return -EINVAL; > } > - > page = virtqueue_get_buf(vi->rvq, &len); > if (!page) { > pr_debug("%s: rx error: %d buffers missing\n", > @@ -217,6 +228,7 @@ static int receive_mergeable(struct virt > skb->dev->stats.rx_length_errors++; > return -EINVAL; > } > + > if (len > PAGE_SIZE) > len = PAGE_SIZE; > > @@ -230,6 +242,7 @@ static int receive_mergeable(struct virt > static void receive_buf(struct net_device *dev, void *buf, unsigned int len) > { > struct virtnet_info *vi = netdev_priv(dev); > + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); > struct sk_buff *skb; > struct page *page; > struct skb_vnet_hdr *hdr; > @@ -265,8 +278,11 @@ static void receive_buf(struct net_devic > > hdr = skb_vnet_hdr(skb); > skb->truesize += skb->data_len; > - dev->stats.rx_bytes += skb->len; > - dev->stats.rx_packets++; > + > + u64_stats_update_begin(&stats->syncp); > + stats->rx_bytes += skb->len; > + stats->rx_packets++; > + u64_stats_update_begin(&stats->syncp);u64_stats_update_end(&stats->syncp);> > if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { > pr_debug("Needs csum!\n"); > @@ -515,11 +531,16 @@ static unsigned int free_old_xmit_skbs(s > { > struct sk_buff *skb; > unsigned int len, tot_sgs = 0; > + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); > > while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) { > pr_debug("Sent skb %p\n", skb); > - vi->dev->stats.tx_bytes += skb->len; > - vi->dev->stats.tx_packets++; > + > + u64_stats_update_begin(&stats->syncp); > + stats->tx_bytes += skb->len; > + stats->tx_packets++; > + u64_stats_update_begin(&stats->syncp);u64_stats_update_end(&stats->syncp);> + > tot_sgs += skb_vnet_hdr(skb)->num_sg; > dev_kfree_skb_any(skb); > } > @@ -641,6 +662,40 @@ static int virtnet_set_mac_address(struc > return 0; > } >
Use per-cpu variables to maintain 64 bit statistics. Signed-off-by: Stephen Hemminger <shemminger at vyatta.com> --- a/drivers/net/virtio_net.c 2011-06-14 15:18:46.448596355 -0400 +++ b/drivers/net/virtio_net.c 2011-06-15 12:02:54.860667443 -0400 @@ -40,6 +40,15 @@ module_param(gso, bool, 0444); #define VIRTNET_SEND_COMMAND_SG_MAX 2 +struct virtnet_stats { + struct u64_stats_sync syncp; + u64 tx_bytes; + u64 tx_packets; + + u64 rx_bytes; + u64 rx_packets; +}; + struct virtnet_info { struct virtio_device *vdev; struct virtqueue *rvq, *svq, *cvq; @@ -56,6 +65,9 @@ struct virtnet_info { /* Host will merge rx buffers for big packets (shake it! shake it!) */ bool mergeable_rx_bufs; + /* Active statistics */ + struct virtnet_stats __percpu *stats; + /* Work struct for refilling if we run low on memory. */ struct delayed_work refill; @@ -209,7 +221,6 @@ static int receive_mergeable(struct virt skb->dev->stats.rx_length_errors++; return -EINVAL; } - page = virtqueue_get_buf(vi->rvq, &len); if (!page) { pr_debug("%s: rx error: %d buffers missing\n", @@ -217,6 +228,7 @@ static int receive_mergeable(struct virt skb->dev->stats.rx_length_errors++; return -EINVAL; } + if (len > PAGE_SIZE) len = PAGE_SIZE; @@ -230,6 +242,7 @@ static int receive_mergeable(struct virt static void receive_buf(struct net_device *dev, void *buf, unsigned int len) { struct virtnet_info *vi = netdev_priv(dev); + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); struct sk_buff *skb; struct page *page; struct skb_vnet_hdr *hdr; @@ -265,8 +278,11 @@ static void receive_buf(struct net_devic hdr = skb_vnet_hdr(skb); skb->truesize += skb->data_len; - dev->stats.rx_bytes += skb->len; - dev->stats.rx_packets++; + + u64_stats_update_begin(&stats->syncp); + stats->rx_bytes += skb->len; + stats->rx_packets++; + u64_stats_update_end(&stats->syncp); if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { pr_debug("Needs csum!\n"); @@ -515,11 +531,16 @@ static unsigned int free_old_xmit_skbs(s { struct sk_buff *skb; unsigned int len, tot_sgs = 0; + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) { pr_debug("Sent skb %p\n", skb); - vi->dev->stats.tx_bytes += skb->len; - vi->dev->stats.tx_packets++; + + u64_stats_update_begin(&stats->syncp); + stats->tx_bytes += skb->len; + stats->tx_packets++; + u64_stats_update_end(&stats->syncp); + tot_sgs += skb_vnet_hdr(skb)->num_sg; dev_kfree_skb_any(skb); } @@ -641,6 +662,40 @@ static int virtnet_set_mac_address(struc return 0; } +static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev, + struct rtnl_link_stats64 *tot) +{ + struct virtnet_info *vi = netdev_priv(dev); + int cpu; + unsigned int start; + + for_each_possible_cpu(cpu) { + struct virtnet_stats __percpu *stats + = per_cpu_ptr(vi->stats, cpu); + u64 tpackets, tbytes, rpackets, rbytes; + + do { + start = u64_stats_fetch_begin(&stats->syncp); + tpackets = stats->tx_packets; + tbytes = stats->tx_bytes; + rpackets = stats->rx_packets; + rbytes = stats->rx_bytes; + } while (u64_stats_fetch_retry(&stats->syncp, start)); + + tot->rx_packets += rpackets; + tot->tx_packets += tpackets; + tot->rx_bytes += rbytes; + tot->tx_bytes += tbytes; + } + + tot->tx_dropped = dev->stats.tx_dropped; + tot->rx_dropped = dev->stats.rx_dropped; + tot->rx_length_errors = dev->stats.rx_length_errors; + tot->rx_frame_errors = dev->stats.rx_frame_errors; + + return tot; +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void virtnet_netpoll(struct net_device *dev) { @@ -650,6 +705,14 @@ static void virtnet_netpoll(struct net_d } #endif +static void virtnet_free(struct net_device *dev) +{ + struct virtnet_info *vi = netdev_priv(dev); + + free_percpu(vi->stats); + free_netdev(dev); +} + static int virtnet_open(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); @@ -835,6 +898,7 @@ static const struct net_device_ops virtn .ndo_set_mac_address = virtnet_set_mac_address, .ndo_set_rx_mode = virtnet_set_rx_mode, .ndo_change_mtu = virtnet_change_mtu, + .ndo_get_stats64 = virtnet_stats, .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -895,6 +959,8 @@ static int virtnet_probe(struct virtio_d /* Set up network device as normal. */ dev->netdev_ops = &virtnet_netdev; dev->features = NETIF_F_HIGHDMA; + dev->destructor = virtnet_free; + SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops); SET_NETDEV_DEV(dev, &vdev->dev); @@ -939,6 +1005,11 @@ static int virtnet_probe(struct virtio_d vi->vdev = vdev; vdev->priv = vi; vi->pages = NULL; + vi->stats = alloc_percpu(struct virtnet_stats); + err = -ENOMEM; + if (vi->stats == NULL) + goto free; + INIT_DELAYED_WORK(&vi->refill, refill_work); sg_init_table(vi->rx_sg, ARRAY_SIZE(vi->rx_sg)); sg_init_table(vi->tx_sg, ARRAY_SIZE(vi->tx_sg)); @@ -958,7 +1029,7 @@ static int virtnet_probe(struct virtio_d err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); if (err) - goto free; + goto free_stats; vi->rvq = vqs[0]; vi->svq = vqs[1]; @@ -1003,6 +1074,8 @@ unregister: cancel_delayed_work_sync(&vi->refill); free_vqs: vdev->config->del_vqs(vdev); +free_stats: + free_percpu(vi->stats); free: free_netdev(dev); return err;
On Wed, Jun 15, 2011 at 11:43:37AM -0400, Stephen Hemminger wrote:> Use per-cpu variables to maintain 64 bit statistics. > Compile tested only. > > Signed-off-by: Stephen Hemminger <shemminger at vyatta.com>Interesting. Does this help speed at all?> --- a/drivers/net/virtio_net.c 2011-06-14 15:18:46.448596355 -0400 > +++ b/drivers/net/virtio_net.c 2011-06-15 09:54:22.914426067 -0400 > @@ -40,6 +40,15 @@ module_param(gso, bool, 0444); > > #define VIRTNET_SEND_COMMAND_SG_MAX 2 > > +struct virtnet_stats { > + struct u64_stats_sync syncp; > + u64 tx_bytes; > + u64 tx_packets; > + > + u64 rx_bytes; > + u64 rx_packets; > +}; > + > struct virtnet_info { > struct virtio_device *vdev; > struct virtqueue *rvq, *svq, *cvq; > @@ -56,6 +65,9 @@ struct virtnet_info { > /* Host will merge rx buffers for big packets (shake it! shake it!) */ > bool mergeable_rx_bufs; > > + /* Active statistics */ > + struct virtnet_stats __percpu *stats; > + > /* Work struct for refilling if we run low on memory. */ > struct delayed_work refill; > > @@ -209,7 +221,6 @@ static int receive_mergeable(struct virt > skb->dev->stats.rx_length_errors++; > return -EINVAL; > } > - > page = virtqueue_get_buf(vi->rvq, &len); > if (!page) { > pr_debug("%s: rx error: %d buffers missing\n", > @@ -217,6 +228,7 @@ static int receive_mergeable(struct virt > skb->dev->stats.rx_length_errors++; > return -EINVAL; > } > + > if (len > PAGE_SIZE) > len = PAGE_SIZE; >Let's not tweak whitespace unnecessarily.> @@ -230,6 +242,7 @@ static int receive_mergeable(struct virt > static void receive_buf(struct net_device *dev, void *buf, unsigned int len) > { > struct virtnet_info *vi = netdev_priv(dev); > + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); > struct sk_buff *skb; > struct page *page; > struct skb_vnet_hdr *hdr; > @@ -265,8 +278,11 @@ static void receive_buf(struct net_devic > > hdr = skb_vnet_hdr(skb); > skb->truesize += skb->data_len; > - dev->stats.rx_bytes += skb->len; > - dev->stats.rx_packets++; > + > + u64_stats_update_begin(&stats->syncp); > + stats->rx_bytes += skb->len; > + stats->rx_packets++; > + u64_stats_update_begin(&stats->syncp); > > if (hdr->hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { > pr_debug("Needs csum!\n"); > @@ -515,11 +531,16 @@ static unsigned int free_old_xmit_skbs(s > { > struct sk_buff *skb; > unsigned int len, tot_sgs = 0; > + struct virtnet_stats __percpu *stats = this_cpu_ptr(vi->stats); > > while ((skb = virtqueue_get_buf(vi->svq, &len)) != NULL) { > pr_debug("Sent skb %p\n", skb); > - vi->dev->stats.tx_bytes += skb->len; > - vi->dev->stats.tx_packets++; > + > + u64_stats_update_begin(&stats->syncp); > + stats->tx_bytes += skb->len; > + stats->tx_packets++; > + u64_stats_update_begin(&stats->syncp); > + > tot_sgs += skb_vnet_hdr(skb)->num_sg; > dev_kfree_skb_any(skb); > } > @@ -641,6 +662,40 @@ static int virtnet_set_mac_address(struc > return 0; > } > > +static struct rtnl_link_stats64 *virtnet_stats(struct net_device *dev, > + struct rtnl_link_stats64 *tot) > +{ > + struct virtnet_info *vi = netdev_priv(dev); > + int cpu; > + unsigned int start; > + > + for_each_possible_cpu(cpu) { > + struct virtnet_stats __percpu *stats > + = per_cpu_ptr(vi->stats, cpu); > + u64 tpackets, tbytes, rpackets, rbytes; > + > + do { > + start = u64_stats_fetch_begin(&stats->syncp); > + tpackets = stats->tx_packets; > + tbytes = stats->tx_bytes; > + rpackets = stats->rx_packets; > + rbytes = stats->rx_bytes; > + } while (u64_stats_fetch_retry(&stats->syncp, start)); > + > + tot->rx_packets += rpackets; > + tot->tx_packets += tpackets; > + tot->rx_bytes += rbytes; > + tot->tx_bytes += tbytes; > + } > + > + tot->tx_dropped = dev->stats.tx_dropped; > + tot->rx_dropped = dev->stats.rx_dropped; > + tot->rx_length_errors = dev->stats.rx_length_errors; > + tot->rx_frame_errors = dev->stats.rx_frame_errors; > + > + return tot; > +} > + > #ifdef CONFIG_NET_POLL_CONTROLLER > static void virtnet_netpoll(struct net_device *dev) > { > @@ -650,6 +705,14 @@ static void virtnet_netpoll(struct net_d > } > #endif > > +static void virtnet_free(struct net_device *dev) > +{ > + struct virtnet_info *vi = netdev_priv(dev); > + > + free_percpu(vi->stats); > + free_netdev(dev); > +} > + > static int virtnet_open(struct net_device *dev) > { > struct virtnet_info *vi = netdev_priv(dev); > @@ -835,6 +898,7 @@ static const struct net_device_ops virtn > .ndo_set_mac_address = virtnet_set_mac_address, > .ndo_set_rx_mode = virtnet_set_rx_mode, > .ndo_change_mtu = virtnet_change_mtu, > + .ndo_get_stats64 = virtnet_stats, > .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid, > .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid, > #ifdef CONFIG_NET_POLL_CONTROLLER > @@ -895,6 +959,8 @@ static int virtnet_probe(struct virtio_d > /* Set up network device as normal. */ > dev->netdev_ops = &virtnet_netdev; > dev->features = NETIF_F_HIGHDMA; > + dev->destructor = virtnet_free; > + > SET_ETHTOOL_OPS(dev, &virtnet_ethtool_ops); > SET_NETDEV_DEV(dev, &vdev->dev); > > @@ -939,6 +1005,11 @@ static int virtnet_probe(struct virtio_d > vi->vdev = vdev; > vdev->priv = vi; > vi->pages = NULL; > + vi->stats = alloc_percpu(struct virtnet_stats); > + err = -ENOMEM; > + if (vi->stats == NULL) > + goto free; > + > INIT_DELAYED_WORK(&vi->refill, refill_work); > sg_init_table(vi->rx_sg, ARRAY_SIZE(vi->rx_sg)); > sg_init_table(vi->tx_sg, ARRAY_SIZE(vi->tx_sg)); > @@ -958,7 +1029,7 @@ static int virtnet_probe(struct virtio_d > > err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); > if (err) > - goto free; > + goto free_stats; > > vi->rvq = vqs[0]; > vi->svq = vqs[1]; > @@ -1003,6 +1074,8 @@ unregister: > cancel_delayed_work_sync(&vi->refill); > free_vqs: > vdev->config->del_vqs(vdev); > +free_stats: > + free_percpu(vi->stats); > free: > free_netdev(dev); > return err;
Possibly Parallel Threads
- [PATCH] virtio-net: per cpu 64 bit stats
- [PATCH] virtio-net: fix a race on 32bit arches
- [PATCH] virtio-net: fix a race on 32bit arches
- [V2 RFC net-next PATCH 1/2] virtio_net: convert the statistics into array
- [V2 RFC net-next PATCH 1/2] virtio_net: convert the statistics into array