forked from mirrors/linux
		
	virtio-net: per cpu 64 bit stats (v2)
Use per-cpu variables to maintain 64 bit statistics. Signed-off-by: Stephen Hemminger <shemminger@vyatta.com> Signed-off-by: David S. Miller <davem@conan.davemloft.net>
This commit is contained in:
		
							parent
							
								
									f984cec64a
								
							
						
					
					
						commit
						3fa2a1df90
					
				
					 1 changed files with 79 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -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 virtnet_info *vi, struct sk_buff *skb)
 | 
			
		|||
			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 virtnet_info *vi, struct sk_buff *skb)
 | 
			
		|||
			skb->dev->stats.rx_length_errors++;
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (len > PAGE_SIZE)
 | 
			
		||||
			len = PAGE_SIZE;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -230,6 +242,7 @@ static int receive_mergeable(struct virtnet_info *vi, struct sk_buff *skb)
 | 
			
		|||
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_device *dev, void *buf, unsigned int len)
 | 
			
		|||
 | 
			
		||||
	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(struct virtnet_info *vi)
 | 
			
		|||
{
 | 
			
		||||
	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(struct net_device *dev, void *p)
 | 
			
		|||
	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_device *dev)
 | 
			
		|||
}
 | 
			
		||||
#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 virtnet_netdev = {
 | 
			
		|||
	.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_device *vdev)
 | 
			
		|||
	/* 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_device *vdev)
 | 
			
		|||
	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_device *vdev)
 | 
			
		|||
 | 
			
		||||
	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 @@ static int virtnet_probe(struct virtio_device *vdev)
 | 
			
		|||
	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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue