Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net, veth: Alloc skb in bulk for ndo_xdp_xmit

Split ndo_xdp_xmit and ndo_start_xmit use cases in veth_xdp_rcv routine
in order to alloc skbs in bulk for XDP_PASS verdict.

Introduce xdp_alloc_skb_bulk utility routine to alloc skb bulk list.
The proposed approach has been tested in the following scenario:

eth (ixgbe) --> XDP_REDIRECT --> veth0 --> (remote-ns) veth1 --> XDP_PASS

XDP_REDIRECT: xdp_redirect_map bpf sample
XDP_PASS: xdp_rxq_info bpf sample

traffic generator: pkt_gen sending udp traffic on a remote device

bpf-next master: ~3.64Mpps
bpf-next + skb bulking allocation: ~3.79Mpps

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Toshiaki Makita <toshiaki.makita1@gmail.com>
Acked-by: Jesper Dangaard Brouer <brouer@redhat.com>
Link: https://lore.kernel.org/bpf/a14a30d3c06fff24e13f836c733d80efc0bd6eb5.1611957532.git.lorenzo@kernel.org

authored by

Lorenzo Bianconi and committed by
Daniel Borkmann
65e6dcf7 060fd103

+70 -20
+58 -20
drivers/net/veth.c
··· 35 35 #define VETH_XDP_HEADROOM (XDP_PACKET_HEADROOM + NET_IP_ALIGN) 36 36 37 37 #define VETH_XDP_TX_BULK_SIZE 16 38 + #define VETH_XDP_BATCH 16 38 39 39 40 struct veth_stats { 40 41 u64 rx_drops; ··· 563 562 return 0; 564 563 } 565 564 566 - static struct sk_buff *veth_xdp_rcv_one(struct veth_rq *rq, 567 - struct xdp_frame *frame, 568 - struct veth_xdp_tx_bq *bq, 569 - struct veth_stats *stats) 565 + static struct xdp_frame *veth_xdp_rcv_one(struct veth_rq *rq, 566 + struct xdp_frame *frame, 567 + struct veth_xdp_tx_bq *bq, 568 + struct veth_stats *stats) 570 569 { 571 570 struct xdp_frame orig_frame; 572 571 struct bpf_prog *xdp_prog; 573 - struct sk_buff *skb; 574 572 575 573 rcu_read_lock(); 576 574 xdp_prog = rcu_dereference(rq->xdp_prog); ··· 623 623 } 624 624 rcu_read_unlock(); 625 625 626 - skb = xdp_build_skb_from_frame(frame, rq->dev); 627 - if (!skb) { 628 - xdp_return_frame(frame); 629 - stats->rx_drops++; 630 - } 631 - 632 - return skb; 626 + return frame; 633 627 err_xdp: 634 628 rcu_read_unlock(); 635 629 xdp_return_frame(frame); 636 630 xdp_xmit: 637 631 return NULL; 632 + } 633 + 634 + /* frames array contains VETH_XDP_BATCH at most */ 635 + static void veth_xdp_rcv_bulk_skb(struct veth_rq *rq, void **frames, 636 + int n_xdpf, struct veth_xdp_tx_bq *bq, 637 + struct veth_stats *stats) 638 + { 639 + void *skbs[VETH_XDP_BATCH]; 640 + int i; 641 + 642 + if (xdp_alloc_skb_bulk(skbs, n_xdpf, 643 + GFP_ATOMIC | __GFP_ZERO) < 0) { 644 + for (i = 0; i < n_xdpf; i++) 645 + xdp_return_frame(frames[i]); 646 + stats->rx_drops += n_xdpf; 647 + 648 + return; 649 + } 650 + 651 + for (i = 0; i < n_xdpf; i++) { 652 + struct sk_buff *skb = skbs[i]; 653 + 654 + skb = __xdp_build_skb_from_frame(frames[i], skb, 655 + rq->dev); 656 + if (!skb) { 657 + xdp_return_frame(frames[i]); 658 + stats->rx_drops++; 659 + continue; 660 + } 661 + napi_gro_receive(&rq->xdp_napi, skb); 662 + } 638 663 } 639 664 640 665 static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq, ··· 809 784 struct veth_xdp_tx_bq *bq, 810 785 struct veth_stats *stats) 811 786 { 812 - int i, done = 0; 787 + int i, done = 0, n_xdpf = 0; 788 + void *xdpf[VETH_XDP_BATCH]; 813 789 814 790 for (i = 0; i < budget; i++) { 815 791 void *ptr = __ptr_ring_consume(&rq->xdp_ring); 816 - struct sk_buff *skb; 817 792 818 793 if (!ptr) 819 794 break; 820 795 821 796 if (veth_is_xdp_frame(ptr)) { 797 + /* ndo_xdp_xmit */ 822 798 struct xdp_frame *frame = veth_ptr_to_xdp(ptr); 823 799 824 800 stats->xdp_bytes += frame->len; 825 - skb = veth_xdp_rcv_one(rq, frame, bq, stats); 801 + frame = veth_xdp_rcv_one(rq, frame, bq, stats); 802 + if (frame) { 803 + /* XDP_PASS */ 804 + xdpf[n_xdpf++] = frame; 805 + if (n_xdpf == VETH_XDP_BATCH) { 806 + veth_xdp_rcv_bulk_skb(rq, xdpf, n_xdpf, 807 + bq, stats); 808 + n_xdpf = 0; 809 + } 810 + } 826 811 } else { 827 - skb = ptr; 812 + /* ndo_start_xmit */ 813 + struct sk_buff *skb = ptr; 814 + 828 815 stats->xdp_bytes += skb->len; 829 816 skb = veth_xdp_rcv_skb(rq, skb, bq, stats); 817 + if (skb) 818 + napi_gro_receive(&rq->xdp_napi, skb); 830 819 } 831 - 832 - if (skb) 833 - napi_gro_receive(&rq->xdp_napi, skb); 834 - 835 820 done++; 836 821 } 822 + 823 + if (n_xdpf) 824 + veth_xdp_rcv_bulk_skb(rq, xdpf, n_xdpf, bq, stats); 837 825 838 826 u64_stats_update_begin(&rq->stats.syncp); 839 827 rq->stats.vs.xdp_redirect += stats->xdp_redirect;
+1
include/net/xdp.h
··· 169 169 struct net_device *dev); 170 170 struct sk_buff *xdp_build_skb_from_frame(struct xdp_frame *xdpf, 171 171 struct net_device *dev); 172 + int xdp_alloc_skb_bulk(void **skbs, int n_skb, gfp_t gfp); 172 173 173 174 static inline 174 175 void xdp_convert_frame_to_buff(struct xdp_frame *frame, struct xdp_buff *xdp)
+11
net/core/xdp.c
··· 514 514 }; 515 515 EXPORT_SYMBOL_GPL(xdp_warn); 516 516 517 + int xdp_alloc_skb_bulk(void **skbs, int n_skb, gfp_t gfp) 518 + { 519 + n_skb = kmem_cache_alloc_bulk(skbuff_head_cache, gfp, 520 + n_skb, skbs); 521 + if (unlikely(!n_skb)) 522 + return -ENOMEM; 523 + 524 + return 0; 525 + } 526 + EXPORT_SYMBOL_GPL(xdp_alloc_skb_bulk); 527 + 517 528 struct sk_buff *__xdp_build_skb_from_frame(struct xdp_frame *xdpf, 518 529 struct sk_buff *skb, 519 530 struct net_device *dev)