Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2024 Intel Corporation */
#ifndef __LIBETH_CACHE_H
#define __LIBETH_CACHE_H
#include <linux/cache.h>
/**
* libeth_cacheline_group_assert - make sure cacheline group size is expected
* @type: type of the structure containing the group
* @grp: group name inside the struct
* @sz: expected group size
*/
#if defined(CONFIG_64BIT) && SMP_CACHE_BYTES == 64
#define libeth_cacheline_group_assert(type, grp, sz) \
static_assert(offsetof(type, __cacheline_group_end__##grp) - \
offsetofend(type, __cacheline_group_begin__##grp) == \
(sz))
#define __libeth_cacheline_struct_assert(type, sz) \
static_assert(sizeof(type) == (sz))
#else /* !CONFIG_64BIT || SMP_CACHE_BYTES != 64 */
#define libeth_cacheline_group_assert(type, grp, sz) \
static_assert(offsetof(type, __cacheline_group_end__##grp) - \
offsetofend(type, __cacheline_group_begin__##grp) <= \
(sz))
#define __libeth_cacheline_struct_assert(type, sz) \
static_assert(sizeof(type) <= (sz))
#endif /* !CONFIG_64BIT || SMP_CACHE_BYTES != 64 */
#define __libeth_cls1(sz1) SMP_CACHE_ALIGN(sz1)
#define __libeth_cls2(sz1, sz2) (SMP_CACHE_ALIGN(sz1) + SMP_CACHE_ALIGN(sz2))
#define __libeth_cls3(sz1, sz2, sz3) \
(SMP_CACHE_ALIGN(sz1) + SMP_CACHE_ALIGN(sz2) + SMP_CACHE_ALIGN(sz3))
#define __libeth_cls(...) \
CONCATENATE(__libeth_cls, COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__)
/**
* libeth_cacheline_struct_assert - make sure CL-based struct size is expected
* @type: type of the struct
* @...: from 1 to 3 CL group sizes (read-mostly, read-write, cold)
*
* When a struct contains several CL groups, it's difficult to predict its size
* on different architectures. The macro instead takes sizes of all of the
* groups the structure contains and generates the final struct size.
*/
#define libeth_cacheline_struct_assert(type, ...) \
__libeth_cacheline_struct_assert(type, __libeth_cls(__VA_ARGS__)); \
static_assert(__alignof(type) >= SMP_CACHE_BYTES)
/**
* libeth_cacheline_set_assert - make sure CL-based struct layout is expected
* @type: type of the struct
* @ro: expected size of the read-mostly group
* @rw: expected size of the read-write group
* @c: expected size of the cold group
*
* Check that each group size is expected and then do final struct size check.
*/
#define libeth_cacheline_set_assert(type, ro, rw, c) \
libeth_cacheline_group_assert(type, read_mostly, ro); \
libeth_cacheline_group_assert(type, read_write, rw); \
libeth_cacheline_group_assert(type, cold, c); \
libeth_cacheline_struct_assert(type, ro, rw, c)
#endif /* __LIBETH_CACHE_H */
+314
View File
@@ -0,0 +1,314 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2024-2025 Intel Corporation */
#ifndef __LIBETH_RX_H
#define __LIBETH_RX_H
#include <linux/if_vlan.h>
#include <net/page_pool/helpers.h>
#include <net/xdp.h>
/* Rx buffer management */
/* Space reserved in front of each frame */
#define LIBETH_SKB_HEADROOM (NET_SKB_PAD + NET_IP_ALIGN)
#define LIBETH_XDP_HEADROOM (ALIGN(XDP_PACKET_HEADROOM, NET_SKB_PAD) + \
NET_IP_ALIGN)
/* Maximum headroom for worst-case calculations */
#define LIBETH_MAX_HEADROOM LIBETH_XDP_HEADROOM
/* Link layer / L2 overhead: Ethernet, 2 VLAN tags (C + S), FCS */
#define LIBETH_RX_LL_LEN (ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN)
/* Maximum supported L2-L4 header length */
#define LIBETH_MAX_HEAD roundup_pow_of_two(max(MAX_HEADER, 256))
/* Always use order-0 pages */
#define LIBETH_RX_PAGE_ORDER 0
/* Pick a sane buffer stride and align to a cacheline boundary */
#define LIBETH_RX_BUF_STRIDE SKB_DATA_ALIGN(128)
/* HW-writeable space in one buffer: truesize - headroom/tailroom, aligned */
#define LIBETH_RX_PAGE_LEN(hr) \
ALIGN_DOWN(SKB_MAX_ORDER(hr, LIBETH_RX_PAGE_ORDER), \
LIBETH_RX_BUF_STRIDE)
/**
* struct libeth_fqe - structure representing an Rx buffer (fill queue element)
* @netmem: network memory reference holding the buffer
* @offset: offset from the page start (to the headroom)
* @truesize: total space occupied by the buffer (w/ headroom and tailroom)
*
* Depending on the MTU, API switches between one-page-per-frame and shared
* page model (to conserve memory on bigger-page platforms). In case of the
* former, @offset is always 0 and @truesize is always ```PAGE_SIZE```.
*/
struct libeth_fqe {
netmem_ref netmem;
u32 offset;
u32 truesize;
} __aligned_largest;
/**
* enum libeth_fqe_type - enum representing types of Rx buffers
* @LIBETH_FQE_MTU: buffer size is determined by MTU
* @LIBETH_FQE_SHORT: buffer size is smaller than MTU, for short frames
* @LIBETH_FQE_HDR: buffer size is ```LIBETH_MAX_HEAD```-sized, for headers
*/
enum libeth_fqe_type {
LIBETH_FQE_MTU = 0U,
LIBETH_FQE_SHORT,
LIBETH_FQE_HDR,
};
/**
* struct libeth_fq - structure representing a buffer (fill) queue
* @fp: hotpath part of the structure
* @pp: &page_pool for buffer management
* @fqes: array of Rx buffers
* @truesize: size to allocate per buffer, w/overhead
* @count: number of descriptors/buffers the queue has
* @type: type of the buffers this queue has
* @hsplit: flag whether header split is enabled
* @xdp: flag indicating whether XDP is enabled
* @buf_len: HW-writeable length per each buffer
* @nid: ID of the closest NUMA node with memory
*/
struct libeth_fq {
struct_group_tagged(libeth_fq_fp, fp,
struct page_pool *pp;
struct libeth_fqe *fqes;
u32 truesize;
u32 count;
);
/* Cold fields */
enum libeth_fqe_type type:2;
bool hsplit:1;
bool xdp:1;
u32 buf_len;
int nid;
};
int libeth_rx_fq_create(struct libeth_fq *fq, struct napi_struct *napi);
void libeth_rx_fq_destroy(struct libeth_fq *fq);
/**
* libeth_rx_alloc - allocate a new Rx buffer
* @fq: fill queue to allocate for
* @i: index of the buffer within the queue
*
* Return: DMA address to be passed to HW for Rx on successful allocation,
* ```DMA_MAPPING_ERROR``` otherwise.
*/
static inline dma_addr_t libeth_rx_alloc(const struct libeth_fq_fp *fq, u32 i)
{
struct libeth_fqe *buf = &fq->fqes[i];
buf->truesize = fq->truesize;
buf->netmem = page_pool_dev_alloc_netmem(fq->pp, &buf->offset,
&buf->truesize);
if (unlikely(!buf->netmem))
return DMA_MAPPING_ERROR;
return page_pool_get_dma_addr_netmem(buf->netmem) + buf->offset +
fq->pp->p.offset;
}
void libeth_rx_recycle_slow(netmem_ref netmem);
/**
* libeth_rx_sync_for_cpu - synchronize or recycle buffer post DMA
* @fqe: buffer to process
* @len: frame length from the descriptor
*
* Process the buffer after it's written by HW. The regular path is to
* synchronize DMA for CPU, but in case of no data it will be immediately
* recycled back to its PP.
*
* Return: true when there's data to process, false otherwise.
*/
static inline bool libeth_rx_sync_for_cpu(const struct libeth_fqe *fqe,
u32 len)
{
netmem_ref netmem = fqe->netmem;
/* Very rare, but possible case. The most common reason:
* the last fragment contained FCS only, which was then
* stripped by the HW.
*/
if (unlikely(!len)) {
libeth_rx_recycle_slow(netmem);
return false;
}
page_pool_dma_sync_netmem_for_cpu(netmem_get_pp(netmem), netmem,
fqe->offset, len);
return true;
}
/* Converting abstract packet type numbers into a software structure with
* the packet parameters to do O(1) lookup on Rx.
*/
enum {
LIBETH_RX_PT_OUTER_L2 = 0U,
LIBETH_RX_PT_OUTER_IPV4,
LIBETH_RX_PT_OUTER_IPV6,
};
enum {
LIBETH_RX_PT_NOT_FRAG = 0U,
LIBETH_RX_PT_FRAG,
};
enum {
LIBETH_RX_PT_TUNNEL_IP_NONE = 0U,
LIBETH_RX_PT_TUNNEL_IP_IP,
LIBETH_RX_PT_TUNNEL_IP_GRENAT,
LIBETH_RX_PT_TUNNEL_IP_GRENAT_MAC,
LIBETH_RX_PT_TUNNEL_IP_GRENAT_MAC_VLAN,
};
enum {
LIBETH_RX_PT_TUNNEL_END_NONE = 0U,
LIBETH_RX_PT_TUNNEL_END_IPV4,
LIBETH_RX_PT_TUNNEL_END_IPV6,
};
enum {
LIBETH_RX_PT_INNER_NONE = 0U,
LIBETH_RX_PT_INNER_UDP,
LIBETH_RX_PT_INNER_TCP,
LIBETH_RX_PT_INNER_SCTP,
LIBETH_RX_PT_INNER_ICMP,
LIBETH_RX_PT_INNER_TIMESYNC,
};
#define LIBETH_RX_PT_PAYLOAD_NONE PKT_HASH_TYPE_NONE
#define LIBETH_RX_PT_PAYLOAD_L2 PKT_HASH_TYPE_L2
#define LIBETH_RX_PT_PAYLOAD_L3 PKT_HASH_TYPE_L3
#define LIBETH_RX_PT_PAYLOAD_L4 PKT_HASH_TYPE_L4
struct libeth_rx_pt {
u32 outer_ip:2;
u32 outer_frag:1;
u32 tunnel_type:3;
u32 tunnel_end_prot:2;
u32 tunnel_end_frag:1;
u32 inner_prot:3;
enum pkt_hash_types payload_layer:2;
u32 pad:2;
enum xdp_rss_hash_type hash_type:16;
};
/**
* struct libeth_rx_csum - checksum offload bits decoded from the Rx descriptor
* @l3l4p: detectable L3 and L4 integrity check is processed by the hardware
* @ipe: IP checksum error
* @eipe: external (outermost) IP header (only for tunels)
* @eudpe: external (outermost) UDP checksum error (only for tunels)
* @ipv6exadd: IPv6 header with extension headers
* @l4e: L4 integrity error
* @pprs: set for packets that skip checksum calculation in the HW pre parser
* @nat: the packet is a UDP tunneled packet
* @raw_csum_valid: set if raw checksum is valid
* @pad: padding to naturally align raw_csum field
* @raw_csum: raw checksum
*/
struct libeth_rx_csum {
u32 l3l4p:1;
u32 ipe:1;
u32 eipe:1;
u32 eudpe:1;
u32 ipv6exadd:1;
u32 l4e:1;
u32 pprs:1;
u32 nat:1;
u32 raw_csum_valid:1;
u32 pad:7;
u32 raw_csum:16;
};
/**
* struct libeth_rqe_info - receive queue element info
* @len: packet length
* @ptype: packet type based on types programmed into the device
* @eop: whether it's the last fragment of the packet
* @rxe: MAC errors: CRC, Alignment, Oversize, Undersizes, Length error
* @vlan: C-VLAN or S-VLAN tag depending on the VLAN offload configuration
*/
struct libeth_rqe_info {
u32 len;
u32 ptype:14;
u32 eop:1;
u32 rxe:1;
u32 vlan:16;
};
void libeth_rx_pt_gen_hash_type(struct libeth_rx_pt *pt);
/**
* libeth_rx_pt_get_ip_ver - get IP version from a packet type structure
* @pt: packet type params
*
* Wrapper to compile out the IPv6 code from the drivers when not supported
* by the kernel.
*
* Return: @pt.outer_ip or stub for IPv6 when not compiled-in.
*/
static inline u32 libeth_rx_pt_get_ip_ver(struct libeth_rx_pt pt)
{
#if !IS_ENABLED(CONFIG_IPV6)
switch (pt.outer_ip) {
case LIBETH_RX_PT_OUTER_IPV4:
return LIBETH_RX_PT_OUTER_IPV4;
default:
return LIBETH_RX_PT_OUTER_L2;
}
#else
return pt.outer_ip;
#endif
}
/* libeth_has_*() can be used to quickly check whether the HW metadata is
* available to avoid further expensive processing such as descriptor reads.
* They already check for the corresponding netdev feature to be enabled,
* thus can be used as drop-in replacements.
*/
static inline bool libeth_rx_pt_has_checksum(const struct net_device *dev,
struct libeth_rx_pt pt)
{
/* Non-zero _INNER* is only possible when _OUTER_IPV* is set,
* it is enough to check only for the L4 type.
*/
return likely(pt.inner_prot > LIBETH_RX_PT_INNER_NONE &&
(dev->features & NETIF_F_RXCSUM));
}
static inline bool libeth_rx_pt_has_hash(const struct net_device *dev,
struct libeth_rx_pt pt)
{
return likely(pt.payload_layer > LIBETH_RX_PT_PAYLOAD_NONE &&
(dev->features & NETIF_F_RXHASH));
}
/**
* libeth_rx_pt_set_hash - fill in skb hash value basing on the PT
* @skb: skb to fill the hash in
* @hash: 32-bit hash value from the descriptor
* @pt: packet type
*/
static inline void libeth_rx_pt_set_hash(struct sk_buff *skb, u32 hash,
struct libeth_rx_pt pt)
{
skb_set_hash(skb, hash, pt.payload_layer);
}
#endif /* __LIBETH_RX_H */
+159
View File
@@ -0,0 +1,159 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2024-2025 Intel Corporation */
#ifndef __LIBETH_TX_H
#define __LIBETH_TX_H
#include <linux/skbuff.h>
#include <net/libeth/types.h>
/* Tx buffer completion */
/**
* enum libeth_sqe_type - type of &libeth_sqe to act on Tx completion
* @LIBETH_SQE_EMPTY: unused/empty OR XDP_TX/XSk frame, no action required
* @LIBETH_SQE_CTX: context descriptor with empty SQE, no action required
* @LIBETH_SQE_SLAB: kmalloc-allocated buffer, unmap and kfree()
* @LIBETH_SQE_FRAG: mapped skb frag, only unmap DMA
* @LIBETH_SQE_SKB: &sk_buff, unmap and napi_consume_skb(), update stats
* @__LIBETH_SQE_XDP_START: separator between skb and XDP types
* @LIBETH_SQE_XDP_TX: &skb_shared_info, libeth_xdp_return_buff_bulk(), stats
* @LIBETH_SQE_XDP_XMIT: &xdp_frame, unmap and xdp_return_frame_bulk(), stats
* @LIBETH_SQE_XDP_XMIT_FRAG: &xdp_frame frag, only unmap DMA
* @LIBETH_SQE_XSK_TX: &libeth_xdp_buff on XSk queue, xsk_buff_free(), stats
* @LIBETH_SQE_XSK_TX_FRAG: &libeth_xdp_buff frag on XSk queue, xsk_buff_free()
*/
enum libeth_sqe_type {
LIBETH_SQE_EMPTY = 0U,
LIBETH_SQE_CTX,
LIBETH_SQE_SLAB,
LIBETH_SQE_FRAG,
LIBETH_SQE_SKB,
__LIBETH_SQE_XDP_START,
LIBETH_SQE_XDP_TX = __LIBETH_SQE_XDP_START,
LIBETH_SQE_XDP_XMIT,
LIBETH_SQE_XDP_XMIT_FRAG,
LIBETH_SQE_XSK_TX,
LIBETH_SQE_XSK_TX_FRAG,
};
/**
* struct libeth_sqe - represents a Send Queue Element / Tx buffer
* @type: type of the buffer, see the enum above
* @rs_idx: index of the last buffer from the batch this one was sent in
* @raw: slab buffer to free via kfree()
* @skb: &sk_buff to consume
* @sinfo: skb shared info of an XDP_TX frame
* @xdpf: XDP frame from ::ndo_xdp_xmit()
* @xsk: XSk Rx frame from XDP_TX action
* @dma: DMA address to unmap
* @len: length of the mapped region to unmap
* @nr_frags: number of frags in the frame this buffer belongs to
* @packets: number of physical packets sent for this frame
* @bytes: number of physical bytes sent for this frame
* @priv: driver-private scratchpad
*/
struct libeth_sqe {
enum libeth_sqe_type type:32;
u32 rs_idx;
union {
void *raw;
struct sk_buff *skb;
struct skb_shared_info *sinfo;
struct xdp_frame *xdpf;
struct libeth_xdp_buff *xsk;
};
DEFINE_DMA_UNMAP_ADDR(dma);
DEFINE_DMA_UNMAP_LEN(len);
u32 nr_frags;
u32 packets;
u32 bytes;
unsigned long priv;
} __aligned_largest;
/**
* LIBETH_SQE_CHECK_PRIV - check the driver's private SQE data
* @p: type or name of the object the driver wants to fit into &libeth_sqe
*
* Make sure the driver's private data fits into libeth_sqe::priv. To be used
* right after its declaration.
*/
#define LIBETH_SQE_CHECK_PRIV(p) \
static_assert(sizeof(p) <= sizeof_field(struct libeth_sqe, priv))
/**
* struct libeth_cq_pp - completion queue poll params
* @dev: &device to perform DMA unmapping
* @bq: XDP frame bulk to combine return operations
* @ss: onstack NAPI stats to fill
* @xss: onstack XDPSQ NAPI stats to fill
* @xdp_tx: number of XDP-not-XSk frames processed
* @napi: whether it's called from the NAPI context
*
* libeth uses this structure to access objects needed for performing full
* Tx complete operation without passing lots of arguments and change the
* prototypes each time a new one is added.
*/
struct libeth_cq_pp {
struct device *dev;
struct xdp_frame_bulk *bq;
union {
struct libeth_sq_napi_stats *ss;
struct libeth_xdpsq_napi_stats *xss;
};
u32 xdp_tx;
bool napi;
};
/**
* libeth_tx_complete - perform Tx completion for one SQE
* @sqe: SQE to complete
* @cp: poll params
*
* Do Tx complete for all the types of buffers, incl. freeing, unmapping,
* updating the stats etc.
*/
static inline void libeth_tx_complete(struct libeth_sqe *sqe,
const struct libeth_cq_pp *cp)
{
switch (sqe->type) {
case LIBETH_SQE_EMPTY:
return;
case LIBETH_SQE_SKB:
case LIBETH_SQE_FRAG:
case LIBETH_SQE_SLAB:
dma_unmap_page(cp->dev, dma_unmap_addr(sqe, dma),
dma_unmap_len(sqe, len), DMA_TO_DEVICE);
break;
default:
break;
}
switch (sqe->type) {
case LIBETH_SQE_SKB:
cp->ss->packets += sqe->packets;
cp->ss->bytes += sqe->bytes;
napi_consume_skb(sqe->skb, cp->napi);
break;
case LIBETH_SQE_SLAB:
kfree(sqe->raw);
break;
default:
break;
}
sqe->type = LIBETH_SQE_EMPTY;
}
void libeth_tx_complete_any(struct libeth_sqe *sqe, struct libeth_cq_pp *cp);
#endif /* __LIBETH_TX_H */
+127
View File
@@ -0,0 +1,127 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2024-2025 Intel Corporation */
#ifndef __LIBETH_TYPES_H
#define __LIBETH_TYPES_H
#include <linux/workqueue.h>
/* Stats */
/**
* struct libeth_rq_napi_stats - "hot" counters to update in Rx polling loop
* @packets: received frames counter
* @bytes: sum of bytes of received frames above
* @fragments: sum of fragments of received S/G frames
* @hsplit: number of frames the device performed the header split for
* @raw: alias to access all the fields as an array
*/
struct libeth_rq_napi_stats {
union {
struct {
u32 packets;
u32 bytes;
u32 fragments;
u32 hsplit;
};
DECLARE_FLEX_ARRAY(u32, raw);
};
};
/**
* struct libeth_sq_napi_stats - "hot" counters to update in Tx completion loop
* @packets: completed frames counter
* @bytes: sum of bytes of completed frames above
* @raw: alias to access all the fields as an array
*/
struct libeth_sq_napi_stats {
union {
struct {
u32 packets;
u32 bytes;
};
DECLARE_FLEX_ARRAY(u32, raw);
};
};
/**
* struct libeth_xdpsq_napi_stats - "hot" counters to update in XDP Tx
* completion loop
* @packets: completed frames counter
* @bytes: sum of bytes of completed frames above
* @fragments: sum of fragments of completed S/G frames
* @raw: alias to access all the fields as an array
*/
struct libeth_xdpsq_napi_stats {
union {
struct {
u32 packets;
u32 bytes;
u32 fragments;
};
DECLARE_FLEX_ARRAY(u32, raw);
};
};
/* XDP */
/*
* The following structures should be embedded into driver's queue structure
* and passed to the libeth_xdp helpers, never used directly.
*/
/* XDPSQ sharing */
/**
* struct libeth_xdpsq_lock - locking primitive for sharing XDPSQs
* @lock: spinlock for locking the queue
* @share: whether this particular queue is shared
*/
struct libeth_xdpsq_lock {
spinlock_t lock;
bool share;
};
/* XDPSQ clean-up timers */
/**
* struct libeth_xdpsq_timer - timer for cleaning up XDPSQs w/o interrupts
* @xdpsq: queue this timer belongs to
* @lock: lock for the queue
* @dwork: work performing cleanups
*
* XDPSQs not using interrupts but lazy cleaning, i.e. only when there's no
* space for sending the current queued frame/bulk, must fire up timers to
* make sure there are no stale buffers to free.
*/
struct libeth_xdpsq_timer {
void *xdpsq;
struct libeth_xdpsq_lock *lock;
struct delayed_work dwork;
};
/* Rx polling path */
/**
* struct libeth_xdp_buff_stash - struct for stashing &xdp_buff onto a queue
* @data: pointer to the start of the frame, xdp_buff.data
* @headroom: frame headroom, xdp_buff.data - xdp_buff.data_hard_start
* @len: frame linear space length, xdp_buff.data_end - xdp_buff.data
* @frame_sz: truesize occupied by the frame, xdp_buff.frame_sz
* @flags: xdp_buff.flags
*
* &xdp_buff is 56 bytes long on x64, &libeth_xdp_buff is 64 bytes. This
* structure carries only necessary fields to save/restore a partially built
* frame on the queue structure to finish it during the next NAPI poll.
*/
struct libeth_xdp_buff_stash {
void *data;
u16 headroom;
u16 len;
u32 frame_sz:24;
u32 flags:8;
} __aligned_largest;
#endif /* __LIBETH_TYPES_H */
File diff suppressed because it is too large Load Diff
+688
View File
@@ -0,0 +1,688 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 Intel Corporation */
#ifndef __LIBETH_XSK_H
#define __LIBETH_XSK_H
#include <net/libeth/xdp.h>
#include <net/xdp_sock_drv.h>
/* ``XDP_TXMD_FLAGS_VALID`` is defined only under ``CONFIG_XDP_SOCKETS`` */
#ifdef XDP_TXMD_FLAGS_VALID
static_assert(XDP_TXMD_FLAGS_VALID <= LIBETH_XDP_TX_XSKMD);
#endif
/* ``XDP_TX`` bulking */
/**
* libeth_xsk_tx_queue_head - internal helper for queueing XSk ``XDP_TX`` head
* @bq: XDP Tx bulk to queue the head frag to
* @xdp: XSk buffer with the head to queue
*
* Return: false if it's the only frag of the frame, true if it's an S/G frame.
*/
static inline bool libeth_xsk_tx_queue_head(struct libeth_xdp_tx_bulk *bq,
struct libeth_xdp_buff *xdp)
{
bq->bulk[bq->count++] = (typeof(*bq->bulk)){
.xsk = xdp,
__libeth_xdp_tx_len(xdp->base.data_end - xdp->data,
LIBETH_XDP_TX_FIRST),
};
if (likely(!xdp_buff_has_frags(&xdp->base)))
return false;
bq->bulk[bq->count - 1].flags |= LIBETH_XDP_TX_MULTI;
return true;
}
/**
* libeth_xsk_tx_queue_frag - internal helper for queueing XSk ``XDP_TX`` frag
* @bq: XDP Tx bulk to queue the frag to
* @frag: XSk frag to queue
*/
static inline void libeth_xsk_tx_queue_frag(struct libeth_xdp_tx_bulk *bq,
struct libeth_xdp_buff *frag)
{
bq->bulk[bq->count++] = (typeof(*bq->bulk)){
.xsk = frag,
__libeth_xdp_tx_len(frag->base.data_end - frag->data),
};
}
/**
* libeth_xsk_tx_queue_bulk - internal helper for queueing XSk ``XDP_TX`` frame
* @bq: XDP Tx bulk to queue the frame to
* @xdp: XSk buffer to queue
* @flush_bulk: driver callback to flush the bulk to the HW queue
*
* Return: true on success, false on flush error.
*/
static __always_inline bool
libeth_xsk_tx_queue_bulk(struct libeth_xdp_tx_bulk *bq,
struct libeth_xdp_buff *xdp,
bool (*flush_bulk)(struct libeth_xdp_tx_bulk *bq,
u32 flags))
{
bool ret = true;
if (unlikely(bq->count == LIBETH_XDP_TX_BULK) &&
unlikely(!flush_bulk(bq, LIBETH_XDP_TX_XSK))) {
libeth_xsk_buff_free_slow(xdp);
return false;
}
if (!libeth_xsk_tx_queue_head(bq, xdp))
goto out;
for (const struct libeth_xdp_buff *head = xdp; ; ) {
xdp = container_of(xsk_buff_get_frag(&head->base),
typeof(*xdp), base);
if (!xdp)
break;
if (unlikely(bq->count == LIBETH_XDP_TX_BULK) &&
unlikely(!flush_bulk(bq, LIBETH_XDP_TX_XSK))) {
ret = false;
break;
}
libeth_xsk_tx_queue_frag(bq, xdp);
}
out:
bq->bulk[bq->count - 1].flags |= LIBETH_XDP_TX_LAST;
return ret;
}
/**
* libeth_xsk_tx_fill_buf - internal helper to fill XSk ``XDP_TX`` &libeth_sqe
* @frm: XDP Tx frame from the bulk
* @i: index on the HW queue
* @sq: XDPSQ abstraction for the queue
* @priv: private data
*
* Return: XDP Tx descriptor with the synced DMA and other info to pass to
* the driver callback.
*/
static inline struct libeth_xdp_tx_desc
libeth_xsk_tx_fill_buf(struct libeth_xdp_tx_frame frm, u32 i,
const struct libeth_xdpsq *sq, u64 priv)
{
struct libeth_xdp_buff *xdp = frm.xsk;
struct libeth_xdp_tx_desc desc = {
.addr = xsk_buff_xdp_get_dma(&xdp->base),
.opts = frm.opts,
};
struct libeth_sqe *sqe;
xsk_buff_raw_dma_sync_for_device(sq->pool, desc.addr, desc.len);
sqe = &sq->sqes[i];
sqe->xsk = xdp;
if (!(desc.flags & LIBETH_XDP_TX_FIRST)) {
sqe->type = LIBETH_SQE_XSK_TX_FRAG;
return desc;
}
sqe->type = LIBETH_SQE_XSK_TX;
libeth_xdp_tx_fill_stats(sqe, &desc,
xdp_get_shared_info_from_buff(&xdp->base));
return desc;
}
/**
* libeth_xsk_tx_flush_bulk - wrapper to define flush of XSk ``XDP_TX`` bulk
* @bq: bulk to flush
* @flags: Tx flags, see __libeth_xdp_tx_flush_bulk()
* @prep: driver callback to prepare the queue
* @xmit: driver callback to fill a HW descriptor
*
* Use via LIBETH_XSK_DEFINE_FLUSH_TX() to define an XSk ``XDP_TX`` driver
* callback.
*/
#define libeth_xsk_tx_flush_bulk(bq, flags, prep, xmit) \
__libeth_xdp_tx_flush_bulk(bq, (flags) | LIBETH_XDP_TX_XSK, prep, \
libeth_xsk_tx_fill_buf, xmit)
/* XSk TMO */
/**
* libeth_xsktmo_req_csum - XSk Tx metadata op to request checksum offload
* @csum_start: unused
* @csum_offset: unused
* @priv: &libeth_xdp_tx_desc from the filling helper
*
* Generic implementation of ::tmo_request_checksum. Works only when HW doesn't
* require filling checksum offsets and other parameters beside the checksum
* request bit.
* Consider using within @libeth_xsktmo unless the driver requires HW-specific
* callbacks.
*/
static inline void libeth_xsktmo_req_csum(u16 csum_start, u16 csum_offset,
void *priv)
{
((struct libeth_xdp_tx_desc *)priv)->flags |= LIBETH_XDP_TX_CSUM;
}
/* Only to inline the callbacks below, use @libeth_xsktmo in drivers instead */
static const struct xsk_tx_metadata_ops __libeth_xsktmo = {
.tmo_request_checksum = libeth_xsktmo_req_csum,
};
/**
* __libeth_xsk_xmit_fill_buf_md - internal helper to prepare XSk xmit w/meta
* @xdesc: &xdp_desc from the XSk buffer pool
* @sq: XDPSQ abstraction for the queue
* @priv: XSk Tx metadata ops
*
* Same as __libeth_xsk_xmit_fill_buf(), but requests metadata pointer and
* fills additional fields in &libeth_xdp_tx_desc to ask for metadata offload.
*
* Return: XDP Tx descriptor with the DMA, metadata request bits, and other
* info to pass to the driver callback.
*/
static __always_inline struct libeth_xdp_tx_desc
__libeth_xsk_xmit_fill_buf_md(const struct xdp_desc *xdesc,
const struct libeth_xdpsq *sq,
u64 priv)
{
const struct xsk_tx_metadata_ops *tmo = libeth_xdp_priv_to_ptr(priv);
struct libeth_xdp_tx_desc desc;
struct xdp_desc_ctx ctx;
ctx = xsk_buff_raw_get_ctx(sq->pool, xdesc->addr);
desc = (typeof(desc)){
.addr = ctx.dma,
__libeth_xdp_tx_len(xdesc->len),
};
BUILD_BUG_ON(!__builtin_constant_p(tmo == libeth_xsktmo));
tmo = tmo == libeth_xsktmo ? &__libeth_xsktmo : tmo;
xsk_tx_metadata_request(sq->pool, &ctx.meta, tmo, &desc);
return desc;
}
/* XSk xmit implementation */
/**
* __libeth_xsk_xmit_fill_buf - internal helper to prepare XSk xmit w/o meta
* @xdesc: &xdp_desc from the XSk buffer pool
* @sq: XDPSQ abstraction for the queue
*
* Return: XDP Tx descriptor with the DMA and other info to pass to
* the driver callback.
*/
static inline struct libeth_xdp_tx_desc
__libeth_xsk_xmit_fill_buf(const struct xdp_desc *xdesc,
const struct libeth_xdpsq *sq)
{
return (struct libeth_xdp_tx_desc){
.addr = xsk_buff_raw_get_dma(sq->pool, xdesc->addr),
__libeth_xdp_tx_len(xdesc->len),
};
}
/**
* libeth_xsk_xmit_fill_buf - internal helper to prepare an XSk xmit
* @frm: &xdp_desc from the XSk buffer pool
* @i: index on the HW queue
* @sq: XDPSQ abstraction for the queue
* @priv: XSk Tx metadata ops
*
* Depending on the metadata ops presence (determined at compile time), calls
* the quickest helper to build a libeth XDP Tx descriptor.
*
* Return: XDP Tx descriptor with the synced DMA, metadata request bits,
* and other info to pass to the driver callback.
*/
static __always_inline struct libeth_xdp_tx_desc
libeth_xsk_xmit_fill_buf(struct libeth_xdp_tx_frame frm, u32 i,
const struct libeth_xdpsq *sq, u64 priv)
{
struct libeth_xdp_tx_desc desc;
if (priv)
desc = __libeth_xsk_xmit_fill_buf_md(&frm.desc, sq, priv);
else
desc = __libeth_xsk_xmit_fill_buf(&frm.desc, sq);
desc.flags |= xsk_is_eop_desc(&frm.desc) ? LIBETH_XDP_TX_LAST : 0;
xsk_buff_raw_dma_sync_for_device(sq->pool, desc.addr, desc.len);
return desc;
}
/**
* libeth_xsk_xmit_do_bulk - send XSk xmit frames
* @pool: XSk buffer pool containing the frames to send
* @xdpsq: opaque pointer to driver's XDPSQ struct
* @budget: maximum number of frames can be sent
* @tmo: optional XSk Tx metadata ops
* @prep: driver callback to build a &libeth_xdpsq
* @xmit: driver callback to put frames to a HW queue
* @finalize: driver callback to start a transmission
*
* Implements generic XSk xmit. Always turns on XSk Tx wakeup as it's assumed
* lazy cleaning is used and interrupts are disabled for the queue.
* HW descriptor filling is unrolled by ``LIBETH_XDP_TX_BATCH`` to optimize
* writes.
* Note that unlike other XDP Tx ops, the queue must be locked and cleaned
* prior to calling this function to already know available @budget.
* @prepare must only build a &libeth_xdpsq and return ``U32_MAX``.
*
* Return: false if @budget was exhausted, true otherwise.
*/
static __always_inline bool
libeth_xsk_xmit_do_bulk(struct xsk_buff_pool *pool, void *xdpsq, u32 budget,
const struct xsk_tx_metadata_ops *tmo,
u32 (*prep)(void *xdpsq, struct libeth_xdpsq *sq),
void (*xmit)(struct libeth_xdp_tx_desc desc, u32 i,
const struct libeth_xdpsq *sq, u64 priv),
void (*finalize)(void *xdpsq, bool sent, bool flush))
{
const struct libeth_xdp_tx_frame *bulk;
bool wake;
u32 n;
wake = xsk_uses_need_wakeup(pool);
if (wake)
xsk_clear_tx_need_wakeup(pool);
n = xsk_tx_peek_release_desc_batch(pool, budget);
bulk = container_of(&pool->tx_descs[0], typeof(*bulk), desc);
libeth_xdp_tx_xmit_bulk(bulk, xdpsq, n, true,
libeth_xdp_ptr_to_priv(tmo), prep,
libeth_xsk_xmit_fill_buf, xmit);
finalize(xdpsq, n, true);
if (wake)
xsk_set_tx_need_wakeup(pool);
return n < budget;
}
/* Rx polling path */
/**
* libeth_xsk_tx_init_bulk - initialize XDP Tx bulk for an XSk Rx NAPI poll
* @bq: bulk to initialize
* @prog: RCU pointer to the XDP program (never %NULL)
* @dev: target &net_device
* @xdpsqs: array of driver XDPSQ structs
* @num: number of active XDPSQs, the above array length
*
* Should be called on an onstack XDP Tx bulk before the XSk NAPI polling loop.
* Initializes all the needed fields to run libeth_xdp functions.
* Never checks if @prog is %NULL or @num == 0 as XDP must always be enabled
* when hitting this path.
*/
#define libeth_xsk_tx_init_bulk(bq, prog, dev, xdpsqs, num) \
__libeth_xdp_tx_init_bulk(bq, prog, dev, xdpsqs, num, true, \
__UNIQUE_ID(bq_), __UNIQUE_ID(nqs_))
struct libeth_xdp_buff *libeth_xsk_buff_add_frag(struct libeth_xdp_buff *head,
struct libeth_xdp_buff *xdp);
/**
* libeth_xsk_process_buff - attach XSk Rx buffer to &libeth_xdp_buff
* @head: head XSk buffer to attach the XSk buffer to (or %NULL)
* @xdp: XSk buffer to process
* @len: received data length from the descriptor
*
* If @head == %NULL, treats the XSk buffer as head and initializes
* the required fields. Otherwise, attaches the buffer as a frag.
* Already performs DMA sync-for-CPU and frame start prefetch
* (for head buffers only).
*
* Return: head XSk buffer on success or if the descriptor must be skipped
* (empty), %NULL if there is no space for a new frag.
*/
static inline struct libeth_xdp_buff *
libeth_xsk_process_buff(struct libeth_xdp_buff *head,
struct libeth_xdp_buff *xdp, u32 len)
{
if (unlikely(!len)) {
libeth_xsk_buff_free_slow(xdp);
return head;
}
xsk_buff_set_size(&xdp->base, len);
xsk_buff_dma_sync_for_cpu(&xdp->base);
if (head)
return libeth_xsk_buff_add_frag(head, xdp);
prefetch(xdp->data);
return xdp;
}
void libeth_xsk_buff_stats_frags(struct libeth_rq_napi_stats *rs,
const struct libeth_xdp_buff *xdp);
u32 __libeth_xsk_run_prog_slow(struct libeth_xdp_buff *xdp,
const struct libeth_xdp_tx_bulk *bq,
enum xdp_action act, int ret);
/**
* __libeth_xsk_run_prog - run XDP program on XSk buffer
* @xdp: XSk buffer to run the prog on
* @bq: buffer bulk for ``XDP_TX`` queueing
*
* Internal inline abstraction to run XDP program on XSk Rx path. Handles
* only the most common ``XDP_REDIRECT`` inline, the rest is processed
* externally.
* Reports an XDP prog exception on errors.
*
* Return: libeth_xdp prog verdict depending on the prog's verdict.
*/
static __always_inline u32
__libeth_xsk_run_prog(struct libeth_xdp_buff *xdp,
const struct libeth_xdp_tx_bulk *bq)
{
enum xdp_action act;
int ret = 0;
act = bpf_prog_run_xdp(bq->prog, &xdp->base);
if (unlikely(act != XDP_REDIRECT))
rest:
return __libeth_xsk_run_prog_slow(xdp, bq, act, ret);
ret = xdp_do_redirect(bq->dev, &xdp->base, bq->prog);
if (unlikely(ret))
goto rest;
return LIBETH_XDP_REDIRECT;
}
/**
* libeth_xsk_run_prog - run XDP program on XSk path and handle all verdicts
* @xdp: XSk buffer to process
* @bq: XDP Tx bulk to queue ``XDP_TX`` buffers
* @fl: driver ``XDP_TX`` bulk flush callback
*
* Run the attached XDP program and handle all possible verdicts.
* Prefer using it via LIBETH_XSK_DEFINE_RUN{,_PASS,_PROG}().
*
* Return: libeth_xdp prog verdict depending on the prog's verdict.
*/
#define libeth_xsk_run_prog(xdp, bq, fl) \
__libeth_xdp_run_flush(xdp, bq, __libeth_xsk_run_prog, \
libeth_xsk_tx_queue_bulk, fl)
/**
* __libeth_xsk_run_pass - helper to run XDP program and handle the result
* @xdp: XSk buffer to process
* @bq: XDP Tx bulk to queue ``XDP_TX`` frames
* @napi: NAPI to build an skb and pass it up the stack
* @rs: onstack libeth RQ stats
* @md: metadata that should be filled to the XSk buffer
* @prep: callback for filling the metadata
* @run: driver wrapper to run XDP program
* @populate: driver callback to populate an skb with the HW descriptor data
*
* Inline abstraction, XSk's counterpart of __libeth_xdp_run_pass(), see its
* doc for details.
*
* Return: false if the polling loop must be exited due to lack of free
* buffers, true otherwise.
*/
static __always_inline bool
__libeth_xsk_run_pass(struct libeth_xdp_buff *xdp,
struct libeth_xdp_tx_bulk *bq, struct napi_struct *napi,
struct libeth_rq_napi_stats *rs, const void *md,
void (*prep)(struct libeth_xdp_buff *xdp,
const void *md),
u32 (*run)(struct libeth_xdp_buff *xdp,
struct libeth_xdp_tx_bulk *bq),
bool (*populate)(struct sk_buff *skb,
const struct libeth_xdp_buff *xdp,
struct libeth_rq_napi_stats *rs))
{
struct sk_buff *skb;
u32 act;
rs->bytes += xdp->base.data_end - xdp->data;
rs->packets++;
if (unlikely(xdp_buff_has_frags(&xdp->base)))
libeth_xsk_buff_stats_frags(rs, xdp);
if (prep && (!__builtin_constant_p(!!md) || md))
prep(xdp, md);
act = run(xdp, bq);
if (likely(act == LIBETH_XDP_REDIRECT))
return true;
if (act != LIBETH_XDP_PASS)
return act != LIBETH_XDP_ABORTED;
skb = xdp_build_skb_from_zc(&xdp->base);
if (unlikely(!skb)) {
libeth_xsk_buff_free_slow(xdp);
return true;
}
if (unlikely(!populate(skb, xdp, rs))) {
napi_consume_skb(skb, true);
return true;
}
napi_gro_receive(napi, skb);
return true;
}
/**
* libeth_xsk_run_pass - helper to run XDP program and handle the result
* @xdp: XSk buffer to process
* @bq: XDP Tx bulk to queue ``XDP_TX`` frames
* @napi: NAPI to build an skb and pass it up the stack
* @rs: onstack libeth RQ stats
* @desc: pointer to the HW descriptor for that frame
* @run: driver wrapper to run XDP program
* @populate: driver callback to populate an skb with the HW descriptor data
*
* Wrapper around the underscored version when "fill the descriptor metadata"
* means just writing the pointer to the HW descriptor as @xdp->desc.
*/
#define libeth_xsk_run_pass(xdp, bq, napi, rs, desc, run, populate) \
__libeth_xsk_run_pass(xdp, bq, napi, rs, desc, libeth_xdp_prep_desc, \
run, populate)
/**
* libeth_xsk_finalize_rx - finalize XDPSQ after an XSk NAPI polling loop
* @bq: ``XDP_TX`` frame bulk
* @flush: driver callback to flush the bulk
* @finalize: driver callback to start sending the frames and run the timer
*
* Flush the bulk if there are frames left to send, kick the queue and flush
* the XDP maps.
*/
#define libeth_xsk_finalize_rx(bq, flush, finalize) \
__libeth_xdp_finalize_rx(bq, LIBETH_XDP_TX_XSK, flush, finalize)
/*
* Helpers to reduce boilerplate code in drivers.
*
* Typical driver XSk Rx flow would be (excl. bulk and buff init, frag attach):
*
* LIBETH_XDP_DEFINE_START();
* LIBETH_XSK_DEFINE_FLUSH_TX(static driver_xsk_flush_tx, driver_xsk_tx_prep,
* driver_xdp_xmit);
* LIBETH_XSK_DEFINE_RUN(static driver_xsk_run, driver_xsk_run_prog,
* driver_xsk_flush_tx, driver_populate_skb);
* LIBETH_XSK_DEFINE_FINALIZE(static driver_xsk_finalize_rx,
* driver_xsk_flush_tx, driver_xdp_finalize_sq);
* LIBETH_XDP_DEFINE_END();
*
* This will build a set of 4 static functions. The compiler is free to decide
* whether to inline them.
* Then, in the NAPI polling function:
*
* while (packets < budget) {
* // ...
* if (!driver_xsk_run(xdp, &bq, napi, &rs, desc))
* break;
* }
* driver_xsk_finalize_rx(&bq);
*/
/**
* LIBETH_XSK_DEFINE_FLUSH_TX - define a driver XSk ``XDP_TX`` flush function
* @name: name of the function to define
* @prep: driver callback to clean an XDPSQ
* @xmit: driver callback to write a HW Tx descriptor
*/
#define LIBETH_XSK_DEFINE_FLUSH_TX(name, prep, xmit) \
__LIBETH_XDP_DEFINE_FLUSH_TX(name, prep, xmit, xsk)
/**
* LIBETH_XSK_DEFINE_RUN_PROG - define a driver XDP program run function
* @name: name of the function to define
* @flush: driver callback to flush an XSk ``XDP_TX`` bulk
*/
#define LIBETH_XSK_DEFINE_RUN_PROG(name, flush) \
u32 __LIBETH_XDP_DEFINE_RUN_PROG(name, flush, xsk)
/**
* LIBETH_XSK_DEFINE_RUN_PASS - define a driver buffer process + pass function
* @name: name of the function to define
* @run: driver callback to run XDP program (above)
* @populate: driver callback to fill an skb with HW descriptor info
*/
#define LIBETH_XSK_DEFINE_RUN_PASS(name, run, populate) \
bool __LIBETH_XDP_DEFINE_RUN_PASS(name, run, populate, xsk)
/**
* LIBETH_XSK_DEFINE_RUN - define a driver buffer process, run + pass function
* @name: name of the function to define
* @run: name of the XDP prog run function to define
* @flush: driver callback to flush an XSk ``XDP_TX`` bulk
* @populate: driver callback to fill an skb with HW descriptor info
*/
#define LIBETH_XSK_DEFINE_RUN(name, run, flush, populate) \
__LIBETH_XDP_DEFINE_RUN(name, run, flush, populate, XSK)
/**
* LIBETH_XSK_DEFINE_FINALIZE - define a driver XSk NAPI poll finalize function
* @name: name of the function to define
* @flush: driver callback to flush an XSk ``XDP_TX`` bulk
* @finalize: driver callback to finalize an XDPSQ and run the timer
*/
#define LIBETH_XSK_DEFINE_FINALIZE(name, flush, finalize) \
__LIBETH_XDP_DEFINE_FINALIZE(name, flush, finalize, xsk)
/* Refilling */
/**
* struct libeth_xskfq - structure representing an XSk buffer (fill) queue
* @fp: hotpath part of the structure
* @pool: &xsk_buff_pool for buffer management
* @fqes: array of XSk buffer pointers
* @descs: opaque pointer to the HW descriptor array
* @ntu: index of the next buffer to poll
* @count: number of descriptors/buffers the queue has
* @pending: current number of XSkFQEs to refill
* @thresh: threshold below which the queue is refilled
* @buf_len: HW-writeable length per each buffer
* @truesize: step between consecutive buffers, 0 if none exists
* @nid: ID of the closest NUMA node with memory
*/
struct libeth_xskfq {
struct_group_tagged(libeth_xskfq_fp, fp,
struct xsk_buff_pool *pool;
struct libeth_xdp_buff **fqes;
void *descs;
u32 ntu;
u32 count;
);
/* Cold fields */
u32 pending;
u32 thresh;
u32 buf_len;
u32 truesize;
int nid;
};
int libeth_xskfq_create(struct libeth_xskfq *fq);
void libeth_xskfq_destroy(struct libeth_xskfq *fq);
/**
* libeth_xsk_buff_xdp_get_dma - get DMA address of XSk &libeth_xdp_buff
* @xdp: buffer to get the DMA addr for
*/
#define libeth_xsk_buff_xdp_get_dma(xdp) \
xsk_buff_xdp_get_dma(&(xdp)->base)
/**
* libeth_xskfqe_alloc - allocate @n XSk Rx buffers
* @fq: hotpath part of the XSkFQ, usually onstack
* @n: number of buffers to allocate
* @fill: driver callback to write DMA addresses to HW descriptors
*
* Note that @fq->ntu gets updated, but ::pending must be recalculated
* by the caller.
*
* Return: number of buffers refilled.
*/
static __always_inline u32
libeth_xskfqe_alloc(struct libeth_xskfq_fp *fq, u32 n,
void (*fill)(const struct libeth_xskfq_fp *fq, u32 i))
{
u32 this, ret, done = 0;
struct xdp_buff **xskb;
this = fq->count - fq->ntu;
if (likely(this > n))
this = n;
again:
xskb = (typeof(xskb))&fq->fqes[fq->ntu];
ret = xsk_buff_alloc_batch(fq->pool, xskb, this);
for (u32 i = 0, ntu = fq->ntu; likely(i < ret); i++)
fill(fq, ntu + i);
done += ret;
fq->ntu += ret;
if (likely(fq->ntu < fq->count) || unlikely(ret < this))
goto out;
fq->ntu = 0;
if (this < n) {
this = n - this;
goto again;
}
out:
return done;
}
/* .ndo_xsk_wakeup */
void libeth_xsk_init_wakeup(call_single_data_t *csd, struct napi_struct *napi);
void libeth_xsk_wakeup(call_single_data_t *csd, u32 qid);
/* Pool setup */
int libeth_xsk_setup_pool(struct net_device *dev, u32 qid, bool enable);
#endif /* __LIBETH_XSK_H */