Skip to content

Commit ef75cb5

Browse files
Heng Qidavem330
authored andcommitted
virtio-net: build xdp_buff with multi buffers
Support xdp for multi buffer packets in mergeable mode. Putting the first buffer as the linear part for xdp_buff, and the rest of the buffers as non-linear fragments to struct skb_shared_info in the tailroom belonging to xdp_buff. Let 'truesize' return to its literal meaning, that is, when xdp is set, it includes the length of headroom and tailroom. Signed-off-by: Heng Qi <hengqi@linux.alibaba.com> Reviewed-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> Acked-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 50bd14b commit ef75cb5

File tree

1 file changed

+100
-8
lines changed

1 file changed

+100
-8
lines changed

drivers/net/virtio_net.c

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,91 @@ static struct sk_buff *receive_big(struct net_device *dev,
938938
return NULL;
939939
}
940940

941+
/* TODO: build xdp in big mode */
942+
static int virtnet_build_xdp_buff_mrg(struct net_device *dev,
943+
struct virtnet_info *vi,
944+
struct receive_queue *rq,
945+
struct xdp_buff *xdp,
946+
void *buf,
947+
unsigned int len,
948+
unsigned int frame_sz,
949+
u16 *num_buf,
950+
unsigned int *xdp_frags_truesize,
951+
struct virtnet_rq_stats *stats)
952+
{
953+
struct virtio_net_hdr_mrg_rxbuf *hdr = buf;
954+
unsigned int headroom, tailroom, room;
955+
unsigned int truesize, cur_frag_size;
956+
struct skb_shared_info *shinfo;
957+
unsigned int xdp_frags_truesz = 0;
958+
struct page *page;
959+
skb_frag_t *frag;
960+
int offset;
961+
void *ctx;
962+
963+
xdp_init_buff(xdp, frame_sz, &rq->xdp_rxq);
964+
xdp_prepare_buff(xdp, buf - VIRTIO_XDP_HEADROOM,
965+
VIRTIO_XDP_HEADROOM + vi->hdr_len, len - vi->hdr_len, true);
966+
967+
if (*num_buf > 1) {
968+
/* If we want to build multi-buffer xdp, we need
969+
* to specify that the flags of xdp_buff have the
970+
* XDP_FLAGS_HAS_FRAG bit.
971+
*/
972+
if (!xdp_buff_has_frags(xdp))
973+
xdp_buff_set_frags_flag(xdp);
974+
975+
shinfo = xdp_get_shared_info_from_buff(xdp);
976+
shinfo->nr_frags = 0;
977+
shinfo->xdp_frags_size = 0;
978+
}
979+
980+
if ((*num_buf - 1) > MAX_SKB_FRAGS)
981+
return -EINVAL;
982+
983+
while ((--*num_buf) >= 1) {
984+
buf = virtqueue_get_buf_ctx(rq->vq, &len, &ctx);
985+
if (unlikely(!buf)) {
986+
pr_debug("%s: rx error: %d buffers out of %d missing\n",
987+
dev->name, *num_buf,
988+
virtio16_to_cpu(vi->vdev, hdr->num_buffers));
989+
dev->stats.rx_length_errors++;
990+
return -EINVAL;
991+
}
992+
993+
stats->bytes += len;
994+
page = virt_to_head_page(buf);
995+
offset = buf - page_address(page);
996+
997+
truesize = mergeable_ctx_to_truesize(ctx);
998+
headroom = mergeable_ctx_to_headroom(ctx);
999+
tailroom = headroom ? sizeof(struct skb_shared_info) : 0;
1000+
room = SKB_DATA_ALIGN(headroom + tailroom);
1001+
1002+
cur_frag_size = truesize;
1003+
xdp_frags_truesz += cur_frag_size;
1004+
if (unlikely(len > truesize - room || cur_frag_size > PAGE_SIZE)) {
1005+
put_page(page);
1006+
pr_debug("%s: rx error: len %u exceeds truesize %lu\n",
1007+
dev->name, len, (unsigned long)(truesize - room));
1008+
dev->stats.rx_length_errors++;
1009+
return -EINVAL;
1010+
}
1011+
1012+
frag = &shinfo->frags[shinfo->nr_frags++];
1013+
__skb_frag_set_page(frag, page);
1014+
skb_frag_off_set(frag, offset);
1015+
skb_frag_size_set(frag, len);
1016+
if (page_is_pfmemalloc(page))
1017+
xdp_buff_set_frag_pfmemalloc(xdp);
1018+
1019+
shinfo->xdp_frags_size += len;
1020+
}
1021+
1022+
*xdp_frags_truesize = xdp_frags_truesz;
1023+
return 0;
1024+
}
1025+
9411026
static struct sk_buff *receive_mergeable(struct net_device *dev,
9421027
struct virtnet_info *vi,
9431028
struct receive_queue *rq,
@@ -956,15 +1041,17 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
9561041
unsigned int truesize = mergeable_ctx_to_truesize(ctx);
9571042
unsigned int headroom = mergeable_ctx_to_headroom(ctx);
9581043
unsigned int metasize = 0;
1044+
unsigned int tailroom = headroom ? sizeof(struct skb_shared_info) : 0;
1045+
unsigned int room = SKB_DATA_ALIGN(headroom + tailroom);
9591046
unsigned int frame_sz;
9601047
int err;
9611048

9621049
head_skb = NULL;
9631050
stats->bytes += len - vi->hdr_len;
9641051

965-
if (unlikely(len > truesize)) {
1052+
if (unlikely(len > truesize - room)) {
9661053
pr_debug("%s: rx error: len %u exceeds truesize %lu\n",
967-
dev->name, len, (unsigned long)ctx);
1054+
dev->name, len, (unsigned long)(truesize - room));
9681055
dev->stats.rx_length_errors++;
9691056
goto err_skb;
9701057
}
@@ -990,10 +1077,12 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
9901077
if (unlikely(hdr->hdr.gso_type))
9911078
goto err_xdp;
9921079

993-
/* Buffers with headroom use PAGE_SIZE as alloc size,
994-
* see add_recvbuf_mergeable() + get_mergeable_buf_len()
1080+
/* Now XDP core assumes frag size is PAGE_SIZE, but buffers
1081+
* with headroom may add hole in truesize, which
1082+
* make their length exceed PAGE_SIZE. So we disabled the
1083+
* hole mechanism for xdp. See add_recvbuf_mergeable().
9951084
*/
996-
frame_sz = headroom ? PAGE_SIZE : truesize;
1085+
frame_sz = truesize;
9971086

9981087
/* This happens when rx buffer size is underestimated
9991088
* or headroom is not enough because of the buffer
@@ -1146,9 +1235,12 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
11461235
page = virt_to_head_page(buf);
11471236

11481237
truesize = mergeable_ctx_to_truesize(ctx);
1149-
if (unlikely(len > truesize)) {
1238+
headroom = mergeable_ctx_to_headroom(ctx);
1239+
tailroom = headroom ? sizeof(struct skb_shared_info) : 0;
1240+
room = SKB_DATA_ALIGN(headroom + tailroom);
1241+
if (unlikely(len > truesize - room)) {
11501242
pr_debug("%s: rx error: len %u exceeds truesize %lu\n",
1151-
dev->name, len, (unsigned long)ctx);
1243+
dev->name, len, (unsigned long)(truesize - room));
11521244
dev->stats.rx_length_errors++;
11531245
goto err_skb;
11541246
}
@@ -1435,7 +1527,7 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi,
14351527
}
14361528

14371529
sg_init_one(rq->sg, buf, len);
1438-
ctx = mergeable_len_to_ctx(len, headroom);
1530+
ctx = mergeable_len_to_ctx(len + room, headroom);
14391531
err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp);
14401532
if (err < 0)
14411533
put_page(virt_to_head_page(buf));

0 commit comments

Comments
 (0)