Dor Laor
2007-Dec-21 07:25 UTC
[kvm-devel] [Virtio-for-kvm] [PATCH 8/13] [Mostly resend] virtio additions
From aad0cb3074950a49714cf98562a0602d11ebb3d1 Mon Sep 17 00:00:00 2001 From: Anthony Liguori <aliguori@us.ibm.com> Date: Mon, 12 Nov 2007 21:30:26 -0600 Subject: [PATCH] virtio: add debug/performance stats to network driver ifconfig down or remove the module to get the statistics dump. --- drivers/net/virtio_net.c | 120 +++++++++++++++++++++++++++++++++++++++------- 1 files changed, 103 insertions(+), 17 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index c4f970e..538cc37 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -49,6 +49,32 @@ struct virtnet_info /* Receive & send queues. */ struct sk_buff_head recv; struct sk_buff_head send; + + struct { + unsigned int free_old_xmit_skbs_manual; + unsigned int free_old_xmit_skbs_tasklet; + unsigned int hrtimer_starts; + unsigned int hrtimer_fires; + unsigned int hrtimer_cancels; + unsigned int sendq_kicks; + unsigned int sendq_packets; + unsigned int sendq_partial_csum; + unsigned int sendq_gso; + unsigned int sendq_sglen; + unsigned int sendq_full; + unsigned int sendq_restarted; + unsigned int sendq_restart_failed; + unsigned int sendq_cancelled; + unsigned int recvq_packets; + unsigned int recvq_refills; + unsigned int recvq_restarted; + unsigned int recvq_restart_failed; + unsigned int recvq_reschedule_failed; + unsigned int recvq_partial_csum; + unsigned int recvq_gso; + unsigned int recvq_kicks; + unsigned int poll; + } stats; }; static inline struct virtio_net_hdr *skb_vnet_hdr(struct sk_buff *skb) @@ -84,6 +110,7 @@ static void xmit_free(unsigned long data) netif_tx_lock(vi->dev); free_old_xmit_skbs(vi); netif_tx_unlock(vi->dev); + vi->stats.free_old_xmit_skbs_tasklet++; /* In case we were waiting for output buffers. */ netif_wake_queue(vi->dev); @@ -97,28 +124,31 @@ static bool skb_xmit_done(struct virtqueue *rvq) return false; } -static void receive_skb(struct net_device *dev, struct sk_buff *skb, +static void receive_skb(struct virtnet_info *vi, struct sk_buff *skb, unsigned len) { struct virtio_net_hdr *hdr = skb_vnet_hdr(skb); + vi->stats.recvq_packets++; + if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) { - pr_debug("%s: short packet %i\n", dev->name, len); - dev->stats.rx_length_errors++; + pr_debug("%s: short packet %i\n", vi->dev->name, len); + vi->dev->stats.rx_length_errors++; goto drop; } len -= sizeof(struct virtio_net_hdr); BUG_ON(len > MAX_PACKET_LEN); skb_trim(skb, len); - skb->protocol = eth_type_trans(skb, dev); + skb->protocol = eth_type_trans(skb, vi->dev); pr_debug("Receiving skb proto 0x%04x len %i type %i\n", ntohs(skb->protocol), skb->len, skb->pkt_type); - dev->stats.rx_bytes += skb->len; - dev->stats.rx_packets++; + vi->dev->stats.rx_bytes += skb->len; + vi->dev->stats.rx_packets++; if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { pr_debug("Needs csum!\n"); + vi->stats.recvq_partial_csum++; skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = hdr->csum_start; skb->csum_offset = hdr->csum_offset; @@ -126,7 +156,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, || skb->csum_offset > skb->len - 2) { if (net_ratelimit()) printk(KERN_WARNING "%s: csum=%u/%u len=%u\n", - dev->name, skb->csum_start, + vi->dev->name, skb->csum_start, skb->csum_offset, skb->len); goto frame_err; } @@ -134,6 +164,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { pr_debug("GSO!\n"); + vi->stats.recvq_gso++; switch (hdr->gso_type) { case VIRTIO_NET_HDR_GSO_TCPV4: skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; @@ -150,7 +181,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, default: if (net_ratelimit()) printk(KERN_WARNING "%s: bad gso type %u.\n", - dev->name, hdr->gso_type); + vi->dev->name, hdr->gso_type); goto frame_err; } @@ -158,7 +189,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, if (skb_shinfo(skb)->gso_size == 0) { if (net_ratelimit()) printk(KERN_WARNING "%s: zero gso size.\n", - dev->name); + vi->dev->name); goto frame_err; } @@ -171,7 +202,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb, return; frame_err: - dev->stats.rx_frame_errors++; + vi->dev->stats.rx_frame_errors++; drop: dev_kfree_skb(skb); } @@ -203,6 +234,7 @@ static void try_fill_recv(struct virtnet_info *vi) } if (unlikely(vi->num > vi->max)) vi->max = vi->num; + vi->stats.recvq_kicks++; vi->rvq->vq_ops->kick(vi->rvq); } @@ -220,26 +252,33 @@ static int virtnet_poll(struct napi_struct *napi, int budget) struct sk_buff *skb = NULL; unsigned int len, received = 0; + vi->stats.poll++; again: while (received < budget && (skb = vi->rvq->vq_ops->get_buf(vi->rvq, &len)) != NULL) { __skb_unlink(skb, &vi->recv); - receive_skb(vi->dev, skb, len); + receive_skb(vi, skb, len); vi->num--; received++; } /* FIXME: If we oom and completely run out of inbufs, we need * to start a timer trying to fill more. */ - if (vi->num < vi->max / 2) + if (vi->num < vi->max / 2) { + vi->stats.recvq_refills++; try_fill_recv(vi); + } /* Out of packets? */ if (received < budget) { netif_rx_complete(vi->dev, napi); - if (unlikely(!vi->rvq->vq_ops->restart(vi->rvq)) - && netif_rx_reschedule(vi->dev, napi)) - goto again; + vi->stats.recvq_restarted++; + if (unlikely(!vi->rvq->vq_ops->restart(vi->rvq))) { + vi->stats.recvq_restart_failed++; + if (netif_rx_reschedule(vi->dev, napi)) + goto again; + vi->stats.recvq_reschedule_failed++; + } } return received; @@ -249,9 +288,11 @@ static enum hrtimer_restart kick_xmit(struct hrtimer *t) { struct virtnet_info *vi = container_of(t,struct virtnet_info,tx_timer); + vi->stats.hrtimer_fires++; BUG_ON(!in_softirq()); BUG_ON(in_irq()); netif_tx_lock(vi->dev); + vi->stats.sendq_kicks++; vi->svq->vq_ops->kick(vi->svq); vi->out_num = 0; netif_tx_unlock(vi->dev); @@ -272,12 +313,14 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev) pr_debug("%s: xmit %p %s\n", dev->name, skb, print_mac(mac, dest)); + vi->stats.sendq_packets++; /* Encode metadata header at front. */ hdr = skb_vnet_hdr(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = skb->csum_start - skb_headroom(skb); hdr->csum_offset = skb->csum_offset; + vi->stats.sendq_partial_csum++; } else { hdr->flags = 0; hdr->csum_offset = hdr->csum_start = 0; @@ -285,6 +328,7 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb_is_gso(skb)) { hdr->gso_size = skb_shinfo(skb)->gso_size; + vi->stats.sendq_gso++; if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN) hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4_ECN; else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) @@ -303,18 +347,23 @@ static int start_xmit(struct sk_buff *skb, struct net_device *dev) vnet_hdr_to_sg(sg, skb); num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; __skb_queue_head(&vi->send, skb); + vi->stats.sendq_sglen += num; again: err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb); if (err) { + vi->stats.sendq_full++; + /* Can we free any used skbs? */ + vi->stats.free_old_xmit_skbs_manual++; if (free_old_xmit_skbs(vi)) goto again; /* Activate callback for using skbs: if this fails it * means some were used in the meantime. */ + vi->stats.sendq_restarted++; if (unlikely(!vi->svq->vq_ops->restart(vi->svq))) { - printk("Unlikely: restart svq failed\n"); + vi->stats.sendq_restart_failed++; goto again; } @@ -326,20 +375,28 @@ again: dev->name, vi->out_max, vi->out_num); vi->out_max = vi->out_num; vi->out_num = 0; + vi->stats.sendq_cancelled++; + /* Kick off send immediately. */ + vi->stats.hrtimer_cancels++; hrtimer_cancel(&vi->tx_timer); + vi->stats.sendq_kicks++; vi->svq->vq_ops->kick(vi->svq); netif_stop_queue(dev); return NETDEV_TX_BUSY; } if (++vi->out_num == vi->out_max) { + vi->stats.hrtimer_cancels++; hrtimer_cancel(&vi->tx_timer); + vi->stats.sendq_kicks++; vi->svq->vq_ops->kick(vi->svq); vi->out_num = 0; - } else + } else { + vi->stats.hrtimer_starts++; hrtimer_start(&vi->tx_timer, ktime_set(0,500000), HRTIMER_MODE_REL); + } return 0; } @@ -376,6 +433,35 @@ static int virtnet_close(struct net_device *dev) kfree_skb(skb); BUG_ON(vi->num != 0); + + printk("Stats for %s\n", dev->name); + printk("free_old_xmit_skbs_manual = %u\n", + vi->stats.free_old_xmit_skbs_manual); + printk("free_old_xmit_skbs_tasklet = %u\n", + vi->stats.free_old_xmit_skbs_tasklet); + printk("hrtimer_starts = %u\n", vi->stats.hrtimer_starts); + printk("hrtimer_fires = %u\n", vi->stats.hrtimer_fires); + printk("hrtimer_cancels = %u\n", vi->stats.hrtimer_cancels); + printk("sendq_kicks = %u\n", vi->stats.sendq_kicks); + printk("sendq_packets = %u\n", vi->stats.sendq_packets); + printk("sendq_partial_csum = %u\n", vi->stats.sendq_partial_csum); + printk("sendq_gso = %u\n", vi->stats.sendq_gso); + printk("sendq_sglen = %u\n", vi->stats.sendq_sglen); + printk("sendq_full = %u\n", vi->stats.sendq_full); + printk("sendq_restarted = %u\n", vi->stats.sendq_restarted); + printk("sendq_restart_failed = %u\n", vi->stats.sendq_restart_failed); + printk("sendq_cancelled = %u\n", vi->stats.sendq_cancelled); + printk("recvq_packets = %u\n", vi->stats.recvq_packets); + printk("recvq_refills = %u\n", vi->stats.recvq_refills); + printk("recvq_restarted = %u\n", vi->stats.recvq_restarted); + printk("recvq_restart_failed = %u\n", vi->stats.recvq_restart_failed); + printk("recvq_reschedule_failed = %u\n", + vi->stats.recvq_reschedule_failed); + printk("recvq_partial_csum = %u\n", vi->stats.recvq_partial_csum); + printk("recvq_gso = %u\n", vi->stats.recvq_gso); + printk("recvq_kicks = %u\n", vi->stats.recvq_kicks); + printk("poll = %u\n", vi->stats.poll); + return 0; } -- 1.5.3.3