On 12.02.24 11:50, Lorenzo Bianconi wrote: > 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 <hawk@xxxxxxxxxx> > Reviewed-by: Toke Hoiland-Jorgensen <toke@xxxxxxxxxx> > Signed-off-by: Lorenzo Bianconi <lorenzo@xxxxxxxxxx> > --- > include/linux/skbuff.h | 2 + > net/core/dev.c | 70 +++++++++++++++++++++++--------- > net/core/skbuff.c | 91 ++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 144 insertions(+), 19 deletions(-) > [...] > diff --git a/net/core/skbuff.c b/net/core/skbuff.c > index 9e5eb47b4025..bdb94749f05d 100644 > --- a/net/core/skbuff.c > +++ b/net/core/skbuff.c > @@ -895,6 +895,97 @@ static bool is_pp_page(struct page *page) > return (page->pp_magic & ~0x3UL) == PP_SIGNATURE; > } > > +static int skb_pp_cow_data(struct page_pool *pool, struct sk_buff **pskb, > + unsigned int headroom) > +{ > +#if IS_ENABLED(CONFIG_PAGE_POOL) > + u32 size, truesize, len, max_head_size, off; > + struct sk_buff *skb = *pskb, *nskb; > + int err, i, head_off; > + void *data; > + > + /* XDP does not support fraglist so we need to linearize > + * the skb. > + */ > + if (skb_has_frag_list(skb)) > + return -EOPNOTSUPP; > + > + max_head_size = SKB_WITH_OVERHEAD(PAGE_SIZE - headroom); > + if (skb->len > max_head_size + MAX_SKB_FRAGS * PAGE_SIZE) > + return -ENOMEM; > + > + size = min_t(u32, skb->len, max_head_size); > + truesize = SKB_HEAD_ALIGN(size) + headroom; > + data = page_pool_dev_alloc_va(pool, &truesize); > + if (!data) > + return -ENOMEM; > + > + nskb = napi_build_skb(data, truesize); > + if (!nskb) { > + page_pool_free_va(pool, data, true); > + return -ENOMEM; > + } > + > + skb_reserve(nskb, headroom); > + skb_copy_header(nskb, skb); > + skb_mark_for_recycle(nskb); > + > + err = skb_copy_bits(skb, 0, nskb->data, size); > + if (err) { > + consume_skb(nskb); > + return err; > + } > + skb_put(nskb, size); > + > + head_off = skb_headroom(nskb) - skb_headroom(skb); > + skb_headers_offset_update(nskb, head_off); > + > + off = size; > + len = skb->len - off; > + for (i = 0; i < MAX_SKB_FRAGS && off < skb->len; i++) { > + struct page *page; > + u32 page_off; > + > + size = min_t(u32, len, PAGE_SIZE); > + truesize = size; > + > + page = page_pool_dev_alloc(pool, &page_off, &truesize); > + if (!data) { > + consume_skb(nskb); > + return -ENOMEM; > + } > + This should check for !page instead, no? (picked up as CID 1583654 by the coverity scan for linux-next)