Quelle xdp.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2025 Intel Corporation */
#define DEFAULT_SYMBOL_NAMESPACE "LIBETH_XDP"
#include <linux/export.h>
#include <net/libeth/xdp.h>
#include "priv.h"
/* XDPSQ sharing */
DEFINE_STATIC_KEY_FALSE(libeth_xdpsq_share);
EXPORT_SYMBOL_GPL(libeth_xdpsq_share);
void __libeth_xdpsq_get(struct libeth_xdpsq_lock *lock,
const struct net_device *dev)
{
bool warn;
spin_lock_init(&lock->lock);
lock->share = true ;
warn = !static_key_enabled(&libeth_xdpsq_share);
static_branch_inc(&libeth_xdpsq_share);
if (warn && net_ratelimit())
netdev_warn(dev, "XDPSQ sharing enabled, possible XDP Tx slowdown\n" );
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_get);
void __libeth_xdpsq_put(struct libeth_xdpsq_lock *lock,
const struct net_device *dev)
{
static_branch_dec(&libeth_xdpsq_share);
if (!static_key_enabled(&libeth_xdpsq_share) && net_ratelimit())
netdev_notice(dev, "XDPSQ sharing disabled\n" );
lock->share = false ;
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_put);
void __acquires(&lock->lock)
__libeth_xdpsq_lock(struct libeth_xdpsq_lock *lock)
{
spin_lock(&lock->lock);
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_lock);
void __releases(&lock->lock)
__libeth_xdpsq_unlock(struct libeth_xdpsq_lock *lock)
{
spin_unlock(&lock->lock);
}
EXPORT_SYMBOL_GPL(__libeth_xdpsq_unlock);
/* XDPSQ clean-up timers */
/**
* libeth_xdpsq_init_timer - initialize an XDPSQ clean - up timer
* @ timer : timer to initialize
* @ xdpsq : queue this timer belongs to
* @ lock : corresponding XDPSQ lock
* @ poll : queue polling / completion function
*
* XDPSQ clean - up timers must be set up before using at the queue configuration
* time . Set the required pointers and the cleaning callback .
*/
void libeth_xdpsq_init_timer(struct libeth_xdpsq_timer *timer, void *xdpsq,
struct libeth_xdpsq_lock *lock,
void (*poll)(struct work_struct *work))
{
timer->xdpsq = xdpsq;
timer->lock = lock;
INIT_DELAYED_WORK(&timer->dwork, poll);
}
EXPORT_SYMBOL_GPL(libeth_xdpsq_init_timer);
/* ``XDP_TX`` bulking */
static void __cold
libeth_xdp_tx_return_one(const struct libeth_xdp_tx_frame *frm)
{
if (frm->len_fl & LIBETH_XDP_TX_MULTI)
libeth_xdp_return_frags(frm->data + frm->soff, true );
libeth_xdp_return_va(frm->data, true );
}
static void __cold
libeth_xdp_tx_return_bulk(const struct libeth_xdp_tx_frame *bq, u32 count)
{
for (u32 i = 0 ; i < count; i++) {
const struct libeth_xdp_tx_frame *frm = &bq[i];
if (!(frm->len_fl & LIBETH_XDP_TX_FIRST))
continue ;
libeth_xdp_tx_return_one(frm);
}
}
static void __cold libeth_trace_xdp_exception(const struct net_device *dev,
const struct bpf_prog *prog,
u32 act)
{
trace_xdp_exception(dev, prog, act);
}
/**
* libeth_xdp_tx_exception - handle Tx exceptions of XDP frames
* @ bq : XDP Tx frame bulk
* @ sent : number of frames sent successfully ( from this bulk )
* @ flags : internal libeth_xdp flags ( XSk , . ndo_xdp_xmit etc . )
*
* Cold helper used by _ _ libeth_xdp_tx_flush_bulk ( ) , do not call directly .
* Reports XDP Tx exceptions , frees the frames that won ' t be sent or adjust
* the Tx bulk to try again later .
*/
void __cold libeth_xdp_tx_exception(struct libeth_xdp_tx_bulk *bq, u32 sent,
u32 flags)
{
const struct libeth_xdp_tx_frame *pos = &bq->bulk[sent];
u32 left = bq->count - sent;
if (!(flags & LIBETH_XDP_TX_NDO))
libeth_trace_xdp_exception(bq->dev, bq->prog, XDP_TX);
if (!(flags & LIBETH_XDP_TX_DROP)) {
memmove(bq->bulk, pos, left * sizeof (*bq->bulk));
bq->count = left;
return ;
}
if (flags & LIBETH_XDP_TX_XSK)
libeth_xsk_tx_return_bulk(pos, left);
else if (!(flags & LIBETH_XDP_TX_NDO))
libeth_xdp_tx_return_bulk(pos, left);
else
libeth_xdp_xmit_return_bulk(pos, left, bq->dev);
bq->count = 0 ;
}
EXPORT_SYMBOL_GPL(libeth_xdp_tx_exception);
/* .ndo_xdp_xmit() implementation */
u32 __cold libeth_xdp_xmit_return_bulk(const struct libeth_xdp_tx_frame *bq,
u32 count, const struct net_device *dev)
{
u32 n = 0 ;
for (u32 i = 0 ; i < count; i++) {
const struct libeth_xdp_tx_frame *frm = &bq[i];
dma_addr_t dma;
if (frm->flags & LIBETH_XDP_TX_FIRST)
dma = *libeth_xdp_xmit_frame_dma(frm->xdpf);
else
dma = dma_unmap_addr(frm, dma);
dma_unmap_page(dev->dev.parent, dma, dma_unmap_len(frm, len),
DMA_TO_DEVICE);
/* Actual xdp_frames are freed by the core */
n += !!(frm->flags & LIBETH_XDP_TX_FIRST);
}
return n;
}
EXPORT_SYMBOL_GPL(libeth_xdp_xmit_return_bulk);
/* Rx polling path */
/**
* libeth_xdp_load_stash - recreate an & xdp_buff from libeth_xdp buffer stash
* @ dst : target & libeth_xdp_buff to initialize
* @ src : source stash
*
* External helper used by libeth_xdp_init_buff ( ) , do not call directly .
* Recreate an onstack & libeth_xdp_buff using the stash saved earlier .
* The only field untouched ( rxq ) is initialized later in the
* abovementioned function .
*/
void libeth_xdp_load_stash(struct libeth_xdp_buff *dst,
const struct libeth_xdp_buff_stash *src)
{
dst->data = src->data;
dst->base.data_end = src->data + src->len;
dst->base.data_meta = src->data;
dst->base.data_hard_start = src->data - src->headroom;
dst->base.frame_sz = src->frame_sz;
dst->base.flags = src->flags;
}
EXPORT_SYMBOL_GPL(libeth_xdp_load_stash);
/**
* libeth_xdp_save_stash - convert & xdp_buff to a libeth_xdp buffer stash
* @ dst : target & libeth_xdp_buff_stash to initialize
* @ src : source XDP buffer
*
* External helper used by libeth_xdp_save_buff ( ) , do not call directly .
* Use the fields from the passed XDP buffer to initialize the stash on the
* queue , so that a partially received frame can be finished later during
* the next NAPI poll .
*/
void libeth_xdp_save_stash(struct libeth_xdp_buff_stash *dst,
const struct libeth_xdp_buff *src)
{
dst->data = src->data;
dst->headroom = src->data - src->base.data_hard_start;
dst->len = src->base.data_end - src->data;
dst->frame_sz = src->base.frame_sz;
dst->flags = src->base.flags;
WARN_ON_ONCE(dst->flags != src->base.flags);
}
EXPORT_SYMBOL_GPL(libeth_xdp_save_stash);
void __libeth_xdp_return_stash(struct libeth_xdp_buff_stash *stash)
{
LIBETH_XDP_ONSTACK_BUFF(xdp);
libeth_xdp_load_stash(xdp, stash);
libeth_xdp_return_buff_slow(xdp);
stash->data = NULL;
}
EXPORT_SYMBOL_GPL(__libeth_xdp_return_stash);
/**
* libeth_xdp_return_buff_slow - free & libeth_xdp_buff
* @ xdp : buffer to free / return
*
* Slowpath version of libeth_xdp_return_buff ( ) to be called on exceptions ,
* queue clean - ups etc . , without unwanted inlining .
*/
void __cold libeth_xdp_return_buff_slow(struct libeth_xdp_buff *xdp)
{
__libeth_xdp_return_buff(xdp, false );
}
EXPORT_SYMBOL_GPL(libeth_xdp_return_buff_slow);
/**
* libeth_xdp_buff_add_frag - add frag to XDP buffer
* @ xdp : head XDP buffer
* @ fqe : Rx buffer containing the frag
* @ len : frag length reported by HW
*
* External helper used by libeth_xdp_process_buff ( ) , do not call directly .
* Frees both head and frag buffers on error .
*
* Return : true success , false on error ( no space for a new frag ) .
*/
bool libeth_xdp_buff_add_frag(struct libeth_xdp_buff *xdp,
const struct libeth_fqe *fqe,
u32 len)
{
netmem_ref netmem = fqe->netmem;
if (!xdp_buff_add_frag(&xdp->base, netmem,
fqe->offset + netmem_get_pp(netmem)->p.offset,
len, fqe->truesize))
goto recycle;
return true ;
recycle:
libeth_rx_recycle_slow(netmem);
libeth_xdp_return_buff_slow(xdp);
return false ;
}
EXPORT_SYMBOL_GPL(libeth_xdp_buff_add_frag);
/**
* libeth_xdp_prog_exception - handle XDP prog exceptions
* @ bq : XDP Tx bulk
* @ xdp : buffer to process
* @ act : original XDP prog verdict
* @ ret : error code if redirect failed
*
* External helper used by _ _ libeth_xdp_run_prog ( ) and
* _ _ libeth_xsk_run_prog_slow ( ) , do not call directly .
* Reports invalid @ act , XDP exception trace event and frees the buffer .
*
* Return : libeth_xdp XDP prog verdict .
*/
u32 __cold libeth_xdp_prog_exception(const struct libeth_xdp_tx_bulk *bq,
struct libeth_xdp_buff *xdp,
enum xdp_action act, int ret)
{
if (act > XDP_REDIRECT)
bpf_warn_invalid_xdp_action(bq->dev, bq->prog, act);
libeth_trace_xdp_exception(bq->dev, bq->prog, act);
if (xdp->base.rxq->mem.type == MEM_TYPE_XSK_BUFF_POOL)
return libeth_xsk_prog_exception(xdp, act, ret);
libeth_xdp_return_buff_slow(xdp);
return LIBETH_XDP_DROP;
}
EXPORT_SYMBOL_GPL(libeth_xdp_prog_exception);
/* Tx buffer completion */
static void libeth_xdp_put_netmem_bulk(netmem_ref netmem,
struct xdp_frame_bulk *bq)
{
if (unlikely(bq->count == XDP_BULK_QUEUE_SIZE))
xdp_flush_frame_bulk(bq);
bq->q[bq->count++] = netmem;
}
/**
* libeth_xdp_return_buff_bulk - free & xdp_buff as part of a bulk
* @ sinfo : shared info corresponding to the buffer
* @ bq : XDP frame bulk to store the buffer
* @ frags : whether the buffer has frags
*
* Same as xdp_return_frame_bulk ( ) , but for & libeth_xdp_buff , speeds up Tx
* completion of ` ` XDP_TX ` ` buffers and allows to free them in same bulks
* with & xdp_frame buffers .
*/
void libeth_xdp_return_buff_bulk(const struct skb_shared_info *sinfo,
struct xdp_frame_bulk *bq, bool frags)
{
if (!frags)
goto head;
for (u32 i = 0 ; i < sinfo->nr_frags; i++)
libeth_xdp_put_netmem_bulk(skb_frag_netmem(&sinfo->frags[i]),
bq);
head:
libeth_xdp_put_netmem_bulk(virt_to_netmem(sinfo), bq);
}
EXPORT_SYMBOL_GPL(libeth_xdp_return_buff_bulk);
/* Misc */
/**
* libeth_xdp_queue_threshold - calculate XDP queue clean / refill threshold
* @ count : number of descriptors in the queue
*
* The threshold is the limit at which RQs start to refill ( when the number of
* empty buffers exceeds it ) and SQs get cleaned up ( when the number of free
* descriptors goes below it ) . To speed up hotpath processing , threshold is
* always pow - 2 , closest to 1 / 4 of the queue length .
* Don ' t call it on hotpath , calculate and cache the threshold during the
* queue initialization .
*
* Return : the calculated threshold .
*/
u32 libeth_xdp_queue_threshold(u32 count)
{
u32 quarter, low, high;
if (likely(is_power_of_2(count)))
return count >> 2 ;
quarter = DIV_ROUND_CLOSEST(count, 4 );
low = rounddown_pow_of_two(quarter);
high = roundup_pow_of_two(quarter);
return high - quarter <= quarter - low ? high : low;
}
EXPORT_SYMBOL_GPL(libeth_xdp_queue_threshold);
/**
* _ _ libeth_xdp_set_features - set XDP features for netdev
* @ dev : & net_device to configure
* @ xmo : XDP metadata ops ( Rx hints )
* @ zc_segs : maximum number of S / G frags the HW can transmit
* @ tmo : XSk Tx metadata ops ( Tx hints )
*
* Set all the features libeth_xdp supports . Only the first argument is
* necessary ; without the third one ( zero ) , XSk support won ' t be advertised .
* Use the non - underscored versions in drivers instead .
*/
void __libeth_xdp_set_features(struct net_device *dev,
const struct xdp_metadata_ops *xmo,
u32 zc_segs,
const struct xsk_tx_metadata_ops *tmo)
{
xdp_set_features_flag(dev,
NETDEV_XDP_ACT_BASIC |
NETDEV_XDP_ACT_REDIRECT |
NETDEV_XDP_ACT_NDO_XMIT |
(zc_segs ? NETDEV_XDP_ACT_XSK_ZEROCOPY : 0 ) |
NETDEV_XDP_ACT_RX_SG |
NETDEV_XDP_ACT_NDO_XMIT_SG);
dev->xdp_metadata_ops = xmo;
tmo = tmo == libeth_xsktmo ? &libeth_xsktmo_slow : tmo;
dev->xdp_zc_max_segs = zc_segs ? : 1 ;
dev->xsk_tx_metadata_ops = zc_segs ? tmo : NULL;
}
EXPORT_SYMBOL_GPL(__libeth_xdp_set_features);
/**
* libeth_xdp_set_redirect - toggle the XDP redirect feature
* @ dev : & net_device to configure
* @ enable : whether XDP is enabled
*
* Use this when XDPSQs are not always available to dynamically enable
* and disable redirect feature .
*/
void libeth_xdp_set_redirect(struct net_device *dev, bool enable)
{
if (enable)
xdp_features_set_redirect_target(dev, true );
else
xdp_features_clear_redirect_target(dev);
}
EXPORT_SYMBOL_GPL(libeth_xdp_set_redirect);
/* Module */
static const struct libeth_xdp_ops xdp_ops __initconst = {
.bulk = libeth_xdp_return_buff_bulk,
.xsk = libeth_xsk_buff_free_slow,
};
static int __init libeth_xdp_module_init(void )
{
libeth_attach_xdp(&xdp_ops);
return 0 ;
}
module_init(libeth_xdp_module_init);
static void __exit libeth_xdp_module_exit(void )
{
libeth_detach_xdp();
}
module_exit(libeth_xdp_module_exit);
MODULE_DESCRIPTION("Common Ethernet library - XDP infra" );
MODULE_IMPORT_NS("LIBETH" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-10-04)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691