Skip to content

Commit e6d5dbd

Browse files
LorenzoBianconikuba-moo
authored andcommitted
xdp: add multi-buff support for xdp running in generic mode
Similar to native xdp, do not always linearize the skb in netif_receive_generic_xdp routine but create a non-linear xdp_buff to be processed by the eBPF program. This allow to add multi-buffer support for xdp running in generic mode. Acked-by: Jesper Dangaard Brouer <[email protected]> Reviewed-by: Toke Hoiland-Jorgensen <[email protected]> Signed-off-by: Lorenzo Bianconi <[email protected]> Link: https://lore.kernel.org/r/1044d6412b1c3e95b40d34993fd5f37cd2f319fd.1707729884.git.lorenzo@kernel.org Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 4d2bb0b commit e6d5dbd

File tree

3 files changed

+144
-19
lines changed

3 files changed

+144
-19
lines changed

include/linux/skbuff.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3446,6 +3446,8 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f)
34463446
__skb_frag_ref(&skb_shinfo(skb)->frags[f]);
34473447
}
34483448

3449+
int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb,
3450+
struct bpf_prog *prog);
34493451
bool napi_pp_put_page(struct page *page, bool napi_safe);
34503452

34513453
static inline void

net/core/dev.c

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4874,6 +4874,12 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
48744874
xdp_init_buff(xdp, frame_sz, &rxqueue->xdp_rxq);
48754875
xdp_prepare_buff(xdp, hard_start, skb_headroom(skb) - mac_len,
48764876
skb_headlen(skb) + mac_len, true);
4877+
if (skb_is_nonlinear(skb)) {
4878+
skb_shinfo(skb)->xdp_frags_size = skb->data_len;
4879+
xdp_buff_set_frags_flag(xdp);
4880+
} else {
4881+
xdp_buff_clear_frags_flag(xdp);
4882+
}
48774883

48784884
orig_data_end = xdp->data_end;
48794885
orig_data = xdp->data;
@@ -4903,6 +4909,14 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
49034909
skb->len += off; /* positive on grow, negative on shrink */
49044910
}
49054911

4912+
/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers
4913+
* (e.g. bpf_xdp_adjust_tail), we need to update data_len here.
4914+
*/
4915+
if (xdp_buff_has_frags(xdp))
4916+
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
4917+
else
4918+
skb->data_len = 0;
4919+
49064920
/* check if XDP changed eth hdr such SKB needs update */
49074921
eth = (struct ethhdr *)xdp->data;
49084922
if ((orig_eth_type != eth->h_proto) ||
@@ -4936,54 +4950,72 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
49364950
return act;
49374951
}
49384952

4953+
static int
4954+
netif_skb_check_for_xdp(struct sk_buff **pskb, struct bpf_prog *prog)
4955+
{
4956+
struct sk_buff *skb = *pskb;
4957+
int err, hroom, troom;
4958+
4959+
if (!skb_cow_data_for_xdp(this_cpu_read(system_page_pool), pskb, prog))
4960+
return 0;
4961+
4962+
/* In case we have to go down the path and also linearize,
4963+
* then lets do the pskb_expand_head() work just once here.
4964+
*/
4965+
hroom = XDP_PACKET_HEADROOM - skb_headroom(skb);
4966+
troom = skb->tail + skb->data_len - skb->end;
4967+
err = pskb_expand_head(skb,
4968+
hroom > 0 ? ALIGN(hroom, NET_SKB_PAD) : 0,
4969+
troom > 0 ? troom + 128 : 0, GFP_ATOMIC);
4970+
if (err)
4971+
return err;
4972+
4973+
return skb_linearize(skb);
4974+
}
4975+
49394976
static u32 netif_receive_generic_xdp(struct sk_buff **pskb,
49404977
struct xdp_buff *xdp,
49414978
struct bpf_prog *xdp_prog)
49424979
{
49434980
struct sk_buff *skb = *pskb;
4944-
u32 act = XDP_DROP;
4981+
u32 mac_len, act = XDP_DROP;
49454982

49464983
/* Reinjected packets coming from act_mirred or similar should
49474984
* not get XDP generic processing.
49484985
*/
49494986
if (skb_is_redirected(skb))
49504987
return XDP_PASS;
49514988

4952-
/* XDP packets must be linear and must have sufficient headroom
4953-
* of XDP_PACKET_HEADROOM bytes. This is the guarantee that also
4954-
* native XDP provides, thus we need to do it here as well.
4989+
/* XDP packets must have sufficient headroom of XDP_PACKET_HEADROOM
4990+
* bytes. This is the guarantee that also native XDP provides,
4991+
* thus we need to do it here as well.
49554992
*/
4993+
mac_len = skb->data - skb_mac_header(skb);
4994+
__skb_push(skb, mac_len);
4995+
49564996
if (skb_cloned(skb) || skb_is_nonlinear(skb) ||
49574997
skb_headroom(skb) < XDP_PACKET_HEADROOM) {
4958-
int hroom = XDP_PACKET_HEADROOM - skb_headroom(skb);
4959-
int troom = skb->tail + skb->data_len - skb->end;
4960-
4961-
/* In case we have to go down the path and also linearize,
4962-
* then lets do the pskb_expand_head() work just once here.
4963-
*/
4964-
if (pskb_expand_head(skb,
4965-
hroom > 0 ? ALIGN(hroom, NET_SKB_PAD) : 0,
4966-
troom > 0 ? troom + 128 : 0, GFP_ATOMIC))
4967-
goto do_drop;
4968-
if (skb_linearize(skb))
4998+
if (netif_skb_check_for_xdp(pskb, xdp_prog))
49694999
goto do_drop;
49705000
}
49715001

4972-
act = bpf_prog_run_generic_xdp(skb, xdp, xdp_prog);
5002+
__skb_pull(*pskb, mac_len);
5003+
5004+
act = bpf_prog_run_generic_xdp(*pskb, xdp, xdp_prog);
49735005
switch (act) {
49745006
case XDP_REDIRECT:
49755007
case XDP_TX:
49765008
case XDP_PASS:
49775009
break;
49785010
default:
4979-
bpf_warn_invalid_xdp_action(skb->dev, xdp_prog, act);
5011+
bpf_warn_invalid_xdp_action((*pskb)->dev, xdp_prog, act);
49805012
fallthrough;
49815013
case XDP_ABORTED:
4982-
trace_xdp_exception(skb->dev, xdp_prog, act);
5014+
trace_xdp_exception((*pskb)->dev, xdp_prog, act);
49835015
fallthrough;
49845016
case XDP_DROP:
49855017
do_drop:
4986-
kfree_skb(skb);
5018+
kfree_skb(*pskb);
49875019
break;
49885020
}
49895021

net/core/skbuff.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,97 @@ static bool is_pp_page(struct page *page)
895895
return (page->pp_magic & ~0x3UL) == PP_SIGNATURE;
896896
}
897897

898+
static int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb,
899+
unsigned int headroom)
900+
{
901+
#if IS_ENABLED(CONFIG_PAGE_POOL)
902+
u32 size, truesize, len, max_head_size, off;
903+
struct sk_buff *skb = *pskb, *nskb;
904+
int err, i, head_off;
905+
void *data;
906+
907+
/* XDP does not support fraglist so we need to linearize
908+
* the skb.
909+
*/
910+
if (skb_has_frag_list(skb))
911+
return -EOPNOTSUPP;
912+
913+
max_head_size = SKB_WITH_OVERHEAD(PAGE_SIZE - headroom);
914+
if (skb->len > max_head_size + MAX_SKB_FRAGS * PAGE_SIZE)
915+
return -ENOMEM;
916+
917+
size = min_t(u32, skb->len, max_head_size);
918+
truesize = SKB_HEAD_ALIGN(size) + headroom;
919+
data = page_pool_dev_alloc_va(pool, &truesize);
920+
if (!data)
921+
return -ENOMEM;
922+
923+
nskb = napi_build_skb(data, truesize);
924+
if (!nskb) {
925+
page_pool_free_va(pool, data, true);
926+
return -ENOMEM;
927+
}
928+
929+
skb_reserve(nskb, headroom);
930+
skb_copy_header(nskb, skb);
931+
skb_mark_for_recycle(nskb);
932+
933+
err = skb_copy_bits(skb, 0, nskb->data, size);
934+
if (err) {
935+
consume_skb(nskb);
936+
return err;
937+
}
938+
skb_put(nskb, size);
939+
940+
head_off = skb_headroom(nskb) - skb_headroom(skb);
941+
skb_headers_offset_update(nskb, head_off);
942+
943+
off = size;
944+
len = skb->len - off;
945+
for (i = 0; i < MAX_SKB_FRAGS && off < skb->len; i++) {
946+
struct page *page;
947+
u32 page_off;
948+
949+
size = min_t(u32, len, PAGE_SIZE);
950+
truesize = size;
951+
952+
page = page_pool_dev_alloc(pool, &page_off, &truesize);
953+
if (!data) {
954+
consume_skb(nskb);
955+
return -ENOMEM;
956+
}
957+
958+
skb_add_rx_frag(nskb, i, page, page_off, size, truesize);
959+
err = skb_copy_bits(skb, off, page_address(page) + page_off,
960+
size);
961+
if (err) {
962+
consume_skb(nskb);
963+
return err;
964+
}
965+
966+
len -= size;
967+
off += size;
968+
}
969+
970+
consume_skb(skb);
971+
*pskb = nskb;
972+
973+
return 0;
974+
#else
975+
return -EOPNOTSUPP;
976+
#endif
977+
}
978+
979+
int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb,
980+
struct bpf_prog *prog)
981+
{
982+
if (!prog->aux->xdp_has_frags)
983+
return -EINVAL;
984+
985+
return skb_pp_cow_data(pool, pskb, XDP_PACKET_HEADROOM);
986+
}
987+
EXPORT_SYMBOL(skb_cow_data_for_xdp);
988+
898989
#if IS_ENABLED(CONFIG_PAGE_POOL)
899990
bool napi_pp_put_page(struct page *page, bool napi_safe)
900991
{

0 commit comments

Comments
 (0)