Skip to content

Commit 68ddc8a

Browse files
alobakinkuba-moo
authored andcommitted
xdp: add generic xdp_buff_add_frag()
The code piece which would attach a frag to &xdp_buff is almost identical across the drivers supporting XDP multi-buffer on Rx. Make it a generic elegant "oneliner". Also, I see lots of drivers calculating frags_truesize as `xdp->frame_sz * nr_frags`. I can't say this is fully correct, since frags might be backed by chunks of different sizes, especially with stuff like the header split. Even page_pool_alloc() can give you two different truesizes on two subsequent requests to allocate the same buffer size. Add a field to &skb_shared_info (unionized as there's no free slot currently on x86_64) to track the "true" truesize. It can be used later when updating the skb. Reviewed-by: Maciej Fijalkowski <[email protected]> Signed-off-by: Alexander Lobakin <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent a19d023 commit 68ddc8a

File tree

3 files changed

+118
-5
lines changed

3 files changed

+118
-5
lines changed

include/linux/skbuff.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -608,11 +608,19 @@ struct skb_shared_info {
608608
* Warning : all fields before dataref are cleared in __alloc_skb()
609609
*/
610610
atomic_t dataref;
611-
unsigned int xdp_frags_size;
612611

613-
/* Intermediate layers must ensure that destructor_arg
614-
* remains valid until skb destructor */
615-
void * destructor_arg;
612+
union {
613+
struct {
614+
u32 xdp_frags_size;
615+
u32 xdp_frags_truesize;
616+
};
617+
618+
/*
619+
* Intermediate layers must ensure that destructor_arg
620+
* remains valid until skb destructor.
621+
*/
622+
void *destructor_arg;
623+
};
616624

617625
/* must be last field, see pskb_expand_head() */
618626
skb_frag_t frags[MAX_SKB_FRAGS];

include/net/xdp.h

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,93 @@ xdp_get_buff_len(const struct xdp_buff *xdp)
167167
return len;
168168
}
169169

170+
void xdp_return_frag(netmem_ref netmem, const struct xdp_buff *xdp);
171+
172+
/**
173+
* __xdp_buff_add_frag - attach frag to &xdp_buff
174+
* @xdp: XDP buffer to attach the frag to
175+
* @netmem: network memory containing the frag
176+
* @offset: offset at which the frag starts
177+
* @size: size of the frag
178+
* @truesize: total memory size occupied by the frag
179+
* @try_coalesce: whether to try coalescing the frags (not valid for XSk)
180+
*
181+
* Attach frag to the XDP buffer. If it currently has no frags attached,
182+
* initialize the related fields, otherwise check that the frag number
183+
* didn't reach the limit of ``MAX_SKB_FRAGS``. If possible, try coalescing
184+
* the frag with the previous one.
185+
* The function doesn't check/update the pfmemalloc bit. Please use the
186+
* non-underscored wrapper in drivers.
187+
*
188+
* Return: true on success, false if there's no space for the frag in
189+
* the shared info struct.
190+
*/
191+
static inline bool __xdp_buff_add_frag(struct xdp_buff *xdp, netmem_ref netmem,
192+
u32 offset, u32 size, u32 truesize,
193+
bool try_coalesce)
194+
{
195+
struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
196+
skb_frag_t *prev;
197+
u32 nr_frags;
198+
199+
if (!xdp_buff_has_frags(xdp)) {
200+
xdp_buff_set_frags_flag(xdp);
201+
202+
nr_frags = 0;
203+
sinfo->xdp_frags_size = 0;
204+
sinfo->xdp_frags_truesize = 0;
205+
206+
goto fill;
207+
}
208+
209+
nr_frags = sinfo->nr_frags;
210+
prev = &sinfo->frags[nr_frags - 1];
211+
212+
if (try_coalesce && netmem == skb_frag_netmem(prev) &&
213+
offset == skb_frag_off(prev) + skb_frag_size(prev)) {
214+
skb_frag_size_add(prev, size);
215+
/* Guaranteed to only decrement the refcount */
216+
xdp_return_frag(netmem, xdp);
217+
} else if (unlikely(nr_frags == MAX_SKB_FRAGS)) {
218+
return false;
219+
} else {
220+
fill:
221+
__skb_fill_netmem_desc_noacc(sinfo, nr_frags++, netmem,
222+
offset, size);
223+
}
224+
225+
sinfo->nr_frags = nr_frags;
226+
sinfo->xdp_frags_size += size;
227+
sinfo->xdp_frags_truesize += truesize;
228+
229+
return true;
230+
}
231+
232+
/**
233+
* xdp_buff_add_frag - attach frag to &xdp_buff
234+
* @xdp: XDP buffer to attach the frag to
235+
* @netmem: network memory containing the frag
236+
* @offset: offset at which the frag starts
237+
* @size: size of the frag
238+
* @truesize: total memory size occupied by the frag
239+
*
240+
* Version of __xdp_buff_add_frag() which takes care of the pfmemalloc bit.
241+
*
242+
* Return: true on success, false if there's no space for the frag in
243+
* the shared info struct.
244+
*/
245+
static inline bool xdp_buff_add_frag(struct xdp_buff *xdp, netmem_ref netmem,
246+
u32 offset, u32 size, u32 truesize)
247+
{
248+
if (!__xdp_buff_add_frag(xdp, netmem, offset, size, truesize, true))
249+
return false;
250+
251+
if (unlikely(netmem_is_pfmemalloc(netmem)))
252+
xdp_buff_set_frag_pfmemalloc(xdp);
253+
254+
return true;
255+
}
256+
170257
struct xdp_frame {
171258
void *data;
172259
u32 len;
@@ -230,7 +317,14 @@ xdp_update_skb_shared_info(struct sk_buff *skb, u8 nr_frags,
230317
unsigned int size, unsigned int truesize,
231318
bool pfmemalloc)
232319
{
233-
skb_shinfo(skb)->nr_frags = nr_frags;
320+
struct skb_shared_info *sinfo = skb_shinfo(skb);
321+
322+
sinfo->nr_frags = nr_frags;
323+
/*
324+
* ``destructor_arg`` is unionized with ``xdp_frags_{,true}size``,
325+
* reset it after that these fields aren't used anymore.
326+
*/
327+
sinfo->destructor_arg = NULL;
234328

235329
skb->len += size;
236330
skb->data_len += size;

net/core/xdp.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,17 @@ void xdp_return_frame_bulk(struct xdp_frame *xdpf,
535535
}
536536
EXPORT_SYMBOL_GPL(xdp_return_frame_bulk);
537537

538+
/**
539+
* xdp_return_frag -- free one XDP frag or decrement its refcount
540+
* @netmem: network memory reference to release
541+
* @xdp: &xdp_buff to release the frag for
542+
*/
543+
void xdp_return_frag(netmem_ref netmem, const struct xdp_buff *xdp)
544+
{
545+
__xdp_return(netmem, xdp->rxq->mem.type, true, NULL);
546+
}
547+
EXPORT_SYMBOL_GPL(xdp_return_frag);
548+
538549
void xdp_return_buff(struct xdp_buff *xdp)
539550
{
540551
struct skb_shared_info *sinfo;

0 commit comments

Comments
 (0)