Skip to content

Commit 5061de3

Browse files
Michael Daltondavem330
authored andcommitted
virtio-net: mergeable buffer size should include virtio-net header
Commit 2613af0 ("virtio_net: migrate mergeable rx buffers to page frag allocators") changed the mergeable receive buffer size from PAGE_SIZE to MTU-size. However, the merge buffer size does not take into account the size of the virtio-net header. Consequently, packets that are MTU-size will take two buffers intead of one (to store the virtio-net header), substantially decreasing the throughput of MTU-size traffic due to TCP window / SKB truesize effects. This commit changes the mergeable buffer size to include the virtio-net header. The buffer size is cacheline-aligned because skb_page_frag_refill will not automatically align the requested size. Benchmarks taken from an average of 5 netperf 30-second TCP_STREAM runs between two QEMU VMs on a single physical machine. Each VM has two VCPUs and vhost enabled. All VMs and vhost threads run in a single 4 CPU cgroup cpuset, using cgroups to ensure that other processes in the system will not be scheduled on the benchmark CPUs. Transmit offloads and mergeable receive buffers are enabled, but guest_tso4 / guest_csum are explicitly disabled to force MTU-sized packets on the receiver. next-net trunk before 2613af0 (PAGE_SIZE buf): 3861.08Gb/s net-next trunk (MTU 1500- packet uses two buf due to size bug): 4076.62Gb/s net-next trunk (MTU 1480- packet fits in one buf): 6301.34Gb/s net-next trunk w/ size fix (MTU 1500 - packet fits in one buf): 6445.44Gb/s Suggested-by: Eric Northup <digitaleric@google.com> Signed-off-by: Michael Dalton <mwdalton@google.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 1ca1a4c commit 5061de3

File tree

1 file changed

+16
-14
lines changed

1 file changed

+16
-14
lines changed

drivers/net/virtio_net.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ module_param(csum, bool, 0444);
3636
module_param(gso, bool, 0444);
3737

3838
/* FIXME: MTU in config. */
39-
#define MAX_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN)
39+
#define GOOD_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN)
40+
#define MERGE_BUFFER_LEN (ALIGN(GOOD_PACKET_LEN + \
41+
sizeof(struct virtio_net_hdr_mrg_rxbuf), \
42+
L1_CACHE_BYTES))
4043
#define GOOD_COPY_LEN 128
4144

4245
#define VIRTNET_DRIVER_VERSION "1.0.0"
@@ -314,10 +317,10 @@ static int receive_mergeable(struct receive_queue *rq, struct sk_buff *head_skb)
314317
head_skb->dev->stats.rx_length_errors++;
315318
return -EINVAL;
316319
}
317-
if (unlikely(len > MAX_PACKET_LEN)) {
320+
if (unlikely(len > MERGE_BUFFER_LEN)) {
318321
pr_debug("%s: rx error: merge buffer too long\n",
319322
head_skb->dev->name);
320-
len = MAX_PACKET_LEN;
323+
len = MERGE_BUFFER_LEN;
321324
}
322325
if (unlikely(num_skb_frags == MAX_SKB_FRAGS)) {
323326
struct sk_buff *nskb = alloc_skb(0, GFP_ATOMIC);
@@ -336,18 +339,17 @@ static int receive_mergeable(struct receive_queue *rq, struct sk_buff *head_skb)
336339
if (curr_skb != head_skb) {
337340
head_skb->data_len += len;
338341
head_skb->len += len;
339-
head_skb->truesize += MAX_PACKET_LEN;
342+
head_skb->truesize += MERGE_BUFFER_LEN;
340343
}
341344
page = virt_to_head_page(buf);
342345
offset = buf - (char *)page_address(page);
343346
if (skb_can_coalesce(curr_skb, num_skb_frags, page, offset)) {
344347
put_page(page);
345348
skb_coalesce_rx_frag(curr_skb, num_skb_frags - 1,
346-
len, MAX_PACKET_LEN);
349+
len, MERGE_BUFFER_LEN);
347350
} else {
348351
skb_add_rx_frag(curr_skb, num_skb_frags, page,
349-
offset, len,
350-
MAX_PACKET_LEN);
352+
offset, len, MERGE_BUFFER_LEN);
351353
}
352354
--rq->num;
353355
}
@@ -383,7 +385,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
383385
struct page *page = virt_to_head_page(buf);
384386
skb = page_to_skb(rq, page,
385387
(char *)buf - (char *)page_address(page),
386-
len, MAX_PACKET_LEN);
388+
len, MERGE_BUFFER_LEN);
387389
if (unlikely(!skb)) {
388390
dev->stats.rx_dropped++;
389391
put_page(page);
@@ -471,11 +473,11 @@ static int add_recvbuf_small(struct receive_queue *rq, gfp_t gfp)
471473
struct skb_vnet_hdr *hdr;
472474
int err;
473475

474-
skb = __netdev_alloc_skb_ip_align(vi->dev, MAX_PACKET_LEN, gfp);
476+
skb = __netdev_alloc_skb_ip_align(vi->dev, GOOD_PACKET_LEN, gfp);
475477
if (unlikely(!skb))
476478
return -ENOMEM;
477479

478-
skb_put(skb, MAX_PACKET_LEN);
480+
skb_put(skb, GOOD_PACKET_LEN);
479481

480482
hdr = skb_vnet_hdr(skb);
481483
sg_set_buf(rq->sg, &hdr->hdr, sizeof hdr->hdr);
@@ -542,20 +544,20 @@ static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp)
542544
int err;
543545

544546
if (gfp & __GFP_WAIT) {
545-
if (skb_page_frag_refill(MAX_PACKET_LEN, &vi->alloc_frag,
547+
if (skb_page_frag_refill(MERGE_BUFFER_LEN, &vi->alloc_frag,
546548
gfp)) {
547549
buf = (char *)page_address(vi->alloc_frag.page) +
548550
vi->alloc_frag.offset;
549551
get_page(vi->alloc_frag.page);
550-
vi->alloc_frag.offset += MAX_PACKET_LEN;
552+
vi->alloc_frag.offset += MERGE_BUFFER_LEN;
551553
}
552554
} else {
553-
buf = netdev_alloc_frag(MAX_PACKET_LEN);
555+
buf = netdev_alloc_frag(MERGE_BUFFER_LEN);
554556
}
555557
if (!buf)
556558
return -ENOMEM;
557559

558-
sg_init_one(rq->sg, buf, MAX_PACKET_LEN);
560+
sg_init_one(rq->sg, buf, MERGE_BUFFER_LEN);
559561
err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, buf, gfp);
560562
if (err < 0)
561563
put_page(virt_to_head_page(buf));

0 commit comments

Comments
 (0)