Skip to content

Commit fb51879

Browse files
Michael Daltondavem330
authored andcommitted
virtio-net: use per-receive queue page frag alloc for mergeable bufs
The virtio-net driver currently uses netdev_alloc_frag() for GFP_ATOMIC mergeable rx buffer allocations. This commit migrates virtio-net to use per-receive queue page frags for GFP_ATOMIC allocation. This change unifies mergeable rx buffer memory allocation, which now will use skb_refill_frag() for both atomic and GFP-WAIT buffer allocations. To address fragmentation concerns, if after buffer allocation there is too little space left in the page frag to allocate a subsequent buffer, the remaining space is added to the current allocated buffer so that the remaining space can be used to store packet data. Acked-by: Michael S. Tsirkin <[email protected]> Signed-off-by: Michael Dalton <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 097b4f1 commit fb51879

File tree

1 file changed

+35
-34
lines changed

1 file changed

+35
-34
lines changed

drivers/net/virtio_net.c

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ struct receive_queue {
7575
/* Chain pages by the private ptr. */
7676
struct page *pages;
7777

78+
/* Page frag for packet buffer allocation. */
79+
struct page_frag alloc_frag;
80+
7881
/* RX: fragments + linear part + virtio header */
7982
struct scatterlist sg[MAX_SKB_FRAGS + 2];
8083

@@ -123,11 +126,6 @@ struct virtnet_info {
123126
/* Lock for config space updates */
124127
struct mutex config_lock;
125128

126-
/* Page_frag for GFP_KERNEL packet buffer allocation when we run
127-
* low on memory.
128-
*/
129-
struct page_frag alloc_frag;
130-
131129
/* Does the affinity hint is set for virtqueues? */
132130
bool affinity_hint_set;
133131

@@ -333,8 +331,8 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
333331
int num_buf = hdr->mhdr.num_buffers;
334332
struct page *page = virt_to_head_page(buf);
335333
int offset = buf - page_address(page);
336-
struct sk_buff *head_skb = page_to_skb(rq, page, offset, len,
337-
MERGE_BUFFER_LEN);
334+
unsigned int truesize = max_t(unsigned int, len, MERGE_BUFFER_LEN);
335+
struct sk_buff *head_skb = page_to_skb(rq, page, offset, len, truesize);
338336
struct sk_buff *curr_skb = head_skb;
339337

340338
if (unlikely(!curr_skb))
@@ -350,11 +348,6 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
350348
dev->stats.rx_length_errors++;
351349
goto err_buf;
352350
}
353-
if (unlikely(len > MERGE_BUFFER_LEN)) {
354-
pr_debug("%s: rx error: merge buffer too long\n",
355-
dev->name);
356-
len = MERGE_BUFFER_LEN;
357-
}
358351

359352
page = virt_to_head_page(buf);
360353

@@ -372,19 +365,20 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
372365
head_skb->truesize += nskb->truesize;
373366
num_skb_frags = 0;
374367
}
368+
truesize = max_t(unsigned int, len, MERGE_BUFFER_LEN);
375369
if (curr_skb != head_skb) {
376370
head_skb->data_len += len;
377371
head_skb->len += len;
378-
head_skb->truesize += MERGE_BUFFER_LEN;
372+
head_skb->truesize += truesize;
379373
}
380374
offset = buf - page_address(page);
381375
if (skb_can_coalesce(curr_skb, num_skb_frags, page, offset)) {
382376
put_page(page);
383377
skb_coalesce_rx_frag(curr_skb, num_skb_frags - 1,
384-
len, MERGE_BUFFER_LEN);
378+
len, truesize);
385379
} else {
386380
skb_add_rx_frag(curr_skb, num_skb_frags, page,
387-
offset, len, MERGE_BUFFER_LEN);
381+
offset, len, truesize);
388382
}
389383
}
390384

@@ -573,25 +567,24 @@ static int add_recvbuf_big(struct receive_queue *rq, gfp_t gfp)
573567

574568
static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp)
575569
{
576-
struct virtnet_info *vi = rq->vq->vdev->priv;
577-
char *buf = NULL;
570+
struct page_frag *alloc_frag = &rq->alloc_frag;
571+
char *buf;
578572
int err;
573+
unsigned int len, hole;
579574

580-
if (gfp & __GFP_WAIT) {
581-
if (skb_page_frag_refill(MERGE_BUFFER_LEN, &vi->alloc_frag,
582-
gfp)) {
583-
buf = (char *)page_address(vi->alloc_frag.page) +
584-
vi->alloc_frag.offset;
585-
get_page(vi->alloc_frag.page);
586-
vi->alloc_frag.offset += MERGE_BUFFER_LEN;
587-
}
588-
} else {
589-
buf = netdev_alloc_frag(MERGE_BUFFER_LEN);
590-
}
591-
if (!buf)
575+
if (unlikely(!skb_page_frag_refill(MERGE_BUFFER_LEN, alloc_frag, gfp)))
592576
return -ENOMEM;
577+
buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset;
578+
get_page(alloc_frag->page);
579+
len = MERGE_BUFFER_LEN;
580+
alloc_frag->offset += len;
581+
hole = alloc_frag->size - alloc_frag->offset;
582+
if (hole < MERGE_BUFFER_LEN) {
583+
len += hole;
584+
alloc_frag->offset += hole;
585+
}
593586

594-
sg_init_one(rq->sg, buf, MERGE_BUFFER_LEN);
587+
sg_init_one(rq->sg, buf, len);
595588
err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, buf, gfp);
596589
if (err < 0)
597590
put_page(virt_to_head_page(buf));
@@ -612,6 +605,7 @@ static bool try_fill_recv(struct receive_queue *rq, gfp_t gfp)
612605
int err;
613606
bool oom;
614607

608+
gfp |= __GFP_COLD;
615609
do {
616610
if (vi->mergeable_rx_bufs)
617611
err = add_recvbuf_mergeable(rq, gfp);
@@ -1368,6 +1362,14 @@ static void free_receive_bufs(struct virtnet_info *vi)
13681362
}
13691363
}
13701364

1365+
static void free_receive_page_frags(struct virtnet_info *vi)
1366+
{
1367+
int i;
1368+
for (i = 0; i < vi->max_queue_pairs; i++)
1369+
if (vi->rq[i].alloc_frag.page)
1370+
put_page(vi->rq[i].alloc_frag.page);
1371+
}
1372+
13711373
static void free_unused_bufs(struct virtnet_info *vi)
13721374
{
13731375
void *buf;
@@ -1695,9 +1697,8 @@ static int virtnet_probe(struct virtio_device *vdev)
16951697
unregister_netdev(dev);
16961698
free_vqs:
16971699
cancel_delayed_work_sync(&vi->refill);
1700+
free_receive_page_frags(vi);
16981701
virtnet_del_vqs(vi);
1699-
if (vi->alloc_frag.page)
1700-
put_page(vi->alloc_frag.page);
17011702
free_stats:
17021703
free_percpu(vi->stats);
17031704
free:
@@ -1714,6 +1715,8 @@ static void remove_vq_common(struct virtnet_info *vi)
17141715

17151716
free_receive_bufs(vi);
17161717

1718+
free_receive_page_frags(vi);
1719+
17171720
virtnet_del_vqs(vi);
17181721
}
17191722

@@ -1731,8 +1734,6 @@ static void virtnet_remove(struct virtio_device *vdev)
17311734
unregister_netdev(vi->dev);
17321735

17331736
remove_vq_common(vi);
1734-
if (vi->alloc_frag.page)
1735-
put_page(vi->alloc_frag.page);
17361737

17371738
flush_work(&vi->config_work);
17381739

0 commit comments

Comments
 (0)