virtio_net: Fix incosistent received bytes counter
authorToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Mon, 23 Jul 2018 14:36:04 +0000 (23:36 +0900)
committerDavid S. Miller <davem@davemloft.net>
Wed, 25 Jul 2018 19:58:25 +0000 (12:58 -0700)
When received packets are dropped in virtio_net driver, received packets
counter is incremented but bytes counter is not.
As a result, for instance if we drop all packets by XDP, only received
is counted and bytes stays 0, which looks inconsistent.
IMHO received packets/bytes should be counted if packets are produced by
the hypervisor, like what common NICs on physical machines are doing.
So fix the bytes counter.

Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/virtio_net.c

index 53085c6..2b6ec92 100644 (file)
@@ -586,7 +586,8 @@ static struct sk_buff *receive_small(struct net_device *dev,
                                     struct receive_queue *rq,
                                     void *buf, void *ctx,
                                     unsigned int len,
-                                    unsigned int *xdp_xmit)
+                                    unsigned int *xdp_xmit,
+                                    unsigned int *rbytes)
 {
        struct sk_buff *skb;
        struct bpf_prog *xdp_prog;
@@ -601,6 +602,7 @@ static struct sk_buff *receive_small(struct net_device *dev,
        int err;
 
        len -= vi->hdr_len;
+       *rbytes += len;
 
        rcu_read_lock();
        xdp_prog = rcu_dereference(rq->xdp_prog);
@@ -705,11 +707,13 @@ static struct sk_buff *receive_big(struct net_device *dev,
                                   struct virtnet_info *vi,
                                   struct receive_queue *rq,
                                   void *buf,
-                                  unsigned int len)
+                                  unsigned int len,
+                                  unsigned int *rbytes)
 {
        struct page *page = buf;
        struct sk_buff *skb = page_to_skb(vi, rq, page, 0, len, PAGE_SIZE);
 
+       *rbytes += len - vi->hdr_len;
        if (unlikely(!skb))
                goto err;
 
@@ -727,7 +731,8 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
                                         void *buf,
                                         void *ctx,
                                         unsigned int len,
-                                        unsigned int *xdp_xmit)
+                                        unsigned int *xdp_xmit,
+                                        unsigned int *rbytes)
 {
        struct virtio_net_hdr_mrg_rxbuf *hdr = buf;
        u16 num_buf = virtio16_to_cpu(vi->vdev, hdr->num_buffers);
@@ -740,6 +745,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
        int err;
 
        head_skb = NULL;
+       *rbytes += len - vi->hdr_len;
 
        rcu_read_lock();
        xdp_prog = rcu_dereference(rq->xdp_prog);
@@ -877,6 +883,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
                        goto err_buf;
                }
 
+               *rbytes += len;
                page = virt_to_head_page(buf);
 
                truesize = mergeable_ctx_to_truesize(ctx);
@@ -932,6 +939,7 @@ err_skb:
                        dev->stats.rx_length_errors++;
                        break;
                }
+               *rbytes += len;
                page = virt_to_head_page(buf);
                put_page(page);
        }
@@ -942,14 +950,13 @@ xdp_xmit:
        return NULL;
 }
 
-static int receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
-                      void *buf, unsigned int len, void **ctx,
-                      unsigned int *xdp_xmit)
+static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
+                       void *buf, unsigned int len, void **ctx,
+                       unsigned int *xdp_xmit, unsigned int *rbytes)
 {
        struct net_device *dev = vi->dev;
        struct sk_buff *skb;
        struct virtio_net_hdr_mrg_rxbuf *hdr;
-       int ret;
 
        if (unlikely(len < vi->hdr_len + ETH_HLEN)) {
                pr_debug("%s: short packet %i\n", dev->name, len);
@@ -961,23 +968,22 @@ static int receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
                } else {
                        put_page(virt_to_head_page(buf));
                }
-               return 0;
+               return;
        }
 
        if (vi->mergeable_rx_bufs)
-               skb = receive_mergeable(dev, vi, rq, buf, ctx, len, xdp_xmit);
+               skb = receive_mergeable(dev, vi, rq, buf, ctx, len, xdp_xmit,
+                                       rbytes);
        else if (vi->big_packets)
-               skb = receive_big(dev, vi, rq, buf, len);
+               skb = receive_big(dev, vi, rq, buf, len, rbytes);
        else
-               skb = receive_small(dev, vi, rq, buf, ctx, len, xdp_xmit);
+               skb = receive_small(dev, vi, rq, buf, ctx, len, xdp_xmit, rbytes);
 
        if (unlikely(!skb))
-               return 0;
+               return;
 
        hdr = skb_vnet_hdr(skb);
 
-       ret = skb->len;
-
        if (hdr->hdr.flags & VIRTIO_NET_HDR_F_DATA_VALID)
                skb->ip_summed = CHECKSUM_UNNECESSARY;
 
@@ -994,12 +1000,11 @@ static int receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
                 ntohs(skb->protocol), skb->len, skb->pkt_type);
 
        napi_gro_receive(&rq->napi, skb);
-       return ret;
+       return;
 
 frame_err:
        dev->stats.rx_frame_errors++;
        dev_kfree_skb(skb);
-       return 0;
 }
 
 /* Unlike mergeable buffers, all buffers are allocated to the
@@ -1249,13 +1254,13 @@ static int virtnet_receive(struct receive_queue *rq, int budget,
 
                while (received < budget &&
                       (buf = virtqueue_get_buf_ctx(rq->vq, &len, &ctx))) {
-                       bytes += receive_buf(vi, rq, buf, len, ctx, xdp_xmit);
+                       receive_buf(vi, rq, buf, len, ctx, xdp_xmit, &bytes);
                        received++;
                }
        } else {
                while (received < budget &&
                       (buf = virtqueue_get_buf(rq->vq, &len)) != NULL) {
-                       bytes += receive_buf(vi, rq, buf, len, NULL, xdp_xmit);
+                       receive_buf(vi, rq, buf, len, NULL, xdp_xmit, &bytes);
                        received++;
                }
        }