mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-28 17:18:39 -05:00
libeth: xdp: add .ndo_xdp_xmit() helpers
Add helpers for implementing .ndo_xdp_xmit(). Same as for XDP_TX, accumulate up to 16 DMA-mapped frames on the stack, then flush. If DMA mapping is failed for some reason, don't try mapping further frames, but still flush what was already prepared. DMA address of a head frame is stored in its headroom, assuming it has enough of it for an 8 (or 4) byte value. In addition to @prep and @xmit driver callbacks in XDP_TX, xmit also needs @finalize to kick the XDPSQ after filling. Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com> Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
committed by
Tony Nguyen
parent
8591c3afe8
commit
084ceda7de
@@ -42,7 +42,7 @@ static void __cold libeth_trace_xdp_exception(const struct net_device *dev,
|
||||
* 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
|
||||
* @flags: internal libeth_xdp flags (.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
|
||||
@@ -54,7 +54,8 @@ void __cold libeth_xdp_tx_exception(struct libeth_xdp_tx_bulk *bq, u32 sent,
|
||||
const struct libeth_xdp_tx_frame *pos = &bq->bulk[sent];
|
||||
u32 left = bq->count - sent;
|
||||
|
||||
libeth_trace_xdp_exception(bq->dev, bq->prog, XDP_TX);
|
||||
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));
|
||||
@@ -63,12 +64,42 @@ void __cold libeth_xdp_tx_exception(struct libeth_xdp_tx_bulk *bq, u32 sent,
|
||||
return;
|
||||
}
|
||||
|
||||
libeth_xdp_tx_return_bulk(pos, left);
|
||||
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 */
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
* @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
|
||||
*/
|
||||
enum libeth_sqe_type {
|
||||
LIBETH_SQE_EMPTY = 0U,
|
||||
@@ -29,6 +31,8 @@ enum libeth_sqe_type {
|
||||
|
||||
__LIBETH_SQE_XDP_START,
|
||||
LIBETH_SQE_XDP_TX = __LIBETH_SQE_XDP_START,
|
||||
LIBETH_SQE_XDP_XMIT,
|
||||
LIBETH_SQE_XDP_XMIT_FRAG,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -38,6 +42,7 @@ enum libeth_sqe_type {
|
||||
* @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()
|
||||
* @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
|
||||
@@ -53,6 +58,7 @@ struct libeth_sqe {
|
||||
void *raw;
|
||||
struct sk_buff *skb;
|
||||
struct skb_shared_info *sinfo;
|
||||
struct xdp_frame *xdpf;
|
||||
};
|
||||
|
||||
DEFINE_DMA_UNMAP_ADDR(dma);
|
||||
|
||||
@@ -11,6 +11,17 @@
|
||||
#include <net/libeth/tx.h>
|
||||
#include <net/xsk_buff_pool.h>
|
||||
|
||||
/*
|
||||
* Defined as bits to be able to use them as a mask on Rx.
|
||||
* Also used as internal return values on Tx.
|
||||
*/
|
||||
enum {
|
||||
LIBETH_XDP_PASS = 0U,
|
||||
LIBETH_XDP_DROP = BIT(0),
|
||||
LIBETH_XDP_ABORTED = BIT(1),
|
||||
LIBETH_XDP_TX = BIT(2),
|
||||
};
|
||||
|
||||
/*
|
||||
* &xdp_buff_xsk is the largest structure &libeth_xdp_buff gets casted to,
|
||||
* pick maximum pointer-compatible alignment.
|
||||
@@ -56,12 +67,14 @@ static_assert(IS_ALIGNED(sizeof(struct xdp_buff_xsk),
|
||||
* @LIBETH_XDP_TX_BULK: one bulk size at which it will be flushed to the queue
|
||||
* @LIBETH_XDP_TX_BATCH: batch size for which the queue fill loop is unrolled
|
||||
* @LIBETH_XDP_TX_DROP: indicates the send function must drop frames not sent
|
||||
* @LIBETH_XDP_TX_NDO: whether the send function is called from .ndo_xdp_xmit()
|
||||
*/
|
||||
enum {
|
||||
LIBETH_XDP_TX_BULK = DEV_MAP_BULK_SIZE,
|
||||
LIBETH_XDP_TX_BATCH = 8,
|
||||
|
||||
LIBETH_XDP_TX_DROP = BIT(0),
|
||||
LIBETH_XDP_TX_NDO = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -88,6 +101,11 @@ enum {
|
||||
* @len_fl: ``XDP_TX``, combined flags [31:16] and len [15:0] field for speed
|
||||
* @soff: ``XDP_TX``, offset from @data to the start of &skb_shared_info
|
||||
* @frag: one (non-head) frag for ``XDP_TX``
|
||||
* @xdpf: &xdp_frame for the head frag for .ndo_xdp_xmit()
|
||||
* @dma: DMA address of the non-head frag for .ndo_xdp_xmit()
|
||||
* @len: frag length for .ndo_xdp_xmit()
|
||||
* @flags: Tx flags for the above
|
||||
* @opts: combined @len + @flags for the above for speed
|
||||
*/
|
||||
struct libeth_xdp_tx_frame {
|
||||
union {
|
||||
@@ -100,6 +118,21 @@ struct libeth_xdp_tx_frame {
|
||||
|
||||
/* ``XDP_TX`` frag */
|
||||
skb_frag_t frag;
|
||||
|
||||
/* .ndo_xdp_xmit() */
|
||||
struct {
|
||||
union {
|
||||
struct xdp_frame *xdpf;
|
||||
dma_addr_t dma;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
u32 len;
|
||||
u32 flags;
|
||||
};
|
||||
aligned_u64 opts;
|
||||
};
|
||||
};
|
||||
};
|
||||
} __aligned_largest;
|
||||
static_assert(offsetof(struct libeth_xdp_tx_frame, frag.len) ==
|
||||
@@ -107,7 +140,7 @@ static_assert(offsetof(struct libeth_xdp_tx_frame, frag.len) ==
|
||||
|
||||
/**
|
||||
* struct libeth_xdp_tx_bulk - XDP Tx frame bulk for bulk sending
|
||||
* @prog: corresponding active XDP program
|
||||
* @prog: corresponding active XDP program, %NULL for .ndo_xdp_xmit()
|
||||
* @dev: &net_device which the frames are transmitted on
|
||||
* @xdpsq: shortcut to the corresponding driver-specific XDPSQ structure
|
||||
* @count: current number of frames in @bulk
|
||||
@@ -445,7 +478,7 @@ void libeth_xdp_tx_exception(struct libeth_xdp_tx_bulk *bq, u32 sent,
|
||||
/**
|
||||
* __libeth_xdp_tx_flush_bulk - internal helper to flush one XDP Tx bulk
|
||||
* @bq: bulk to flush
|
||||
* @flags: XDP TX flags
|
||||
* @flags: XDP TX flags (.ndo_xdp_xmit() etc.)
|
||||
* @prep: driver-specific callback to prepare the queue for sending
|
||||
* @fill: libeth_xdp callback to fill &libeth_sqe and &libeth_xdp_tx_desc
|
||||
* @xmit: driver callback to fill a HW descriptor
|
||||
@@ -495,6 +528,259 @@ __libeth_xdp_tx_flush_bulk(struct libeth_xdp_tx_bulk *bq, u32 flags,
|
||||
__libeth_xdp_tx_flush_bulk(bq, flags, prep, libeth_xdp_tx_fill_buf, \
|
||||
xmit)
|
||||
|
||||
/* .ndo_xdp_xmit() implementation */
|
||||
|
||||
/**
|
||||
* libeth_xdp_xmit_frame_dma - internal helper to access DMA of an &xdp_frame
|
||||
* @xf: pointer to the XDP frame
|
||||
*
|
||||
* There's no place in &libeth_xdp_tx_frame to store DMA address for an
|
||||
* &xdp_frame head. The headroom is used then, the address is placed right
|
||||
* after the frame struct, naturally aligned.
|
||||
*
|
||||
* Return: pointer to the DMA address to use.
|
||||
*/
|
||||
#define libeth_xdp_xmit_frame_dma(xf) \
|
||||
_Generic((xf), \
|
||||
const struct xdp_frame *: \
|
||||
(const dma_addr_t *)__libeth_xdp_xmit_frame_dma(xf), \
|
||||
struct xdp_frame *: \
|
||||
(dma_addr_t *)__libeth_xdp_xmit_frame_dma(xf) \
|
||||
)
|
||||
|
||||
static inline void *__libeth_xdp_xmit_frame_dma(const struct xdp_frame *xdpf)
|
||||
{
|
||||
void *addr = (void *)(xdpf + 1);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) &&
|
||||
__alignof(*xdpf) < sizeof(dma_addr_t))
|
||||
addr = PTR_ALIGN(addr, sizeof(dma_addr_t));
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libeth_xdp_xmit_queue_head - internal helper for queueing one XDP xmit head
|
||||
* @bq: XDP Tx bulk to queue the head frag to
|
||||
* @xdpf: XDP frame with the head to queue
|
||||
* @dev: device to perform DMA mapping
|
||||
*
|
||||
* Return: ``LIBETH_XDP_DROP`` on DMA mapping error,
|
||||
* ``LIBETH_XDP_PASS`` if it's the only frag in the frame,
|
||||
* ``LIBETH_XDP_TX`` if it's an S/G frame.
|
||||
*/
|
||||
static inline u32 libeth_xdp_xmit_queue_head(struct libeth_xdp_tx_bulk *bq,
|
||||
struct xdp_frame *xdpf,
|
||||
struct device *dev)
|
||||
{
|
||||
dma_addr_t dma;
|
||||
|
||||
dma = dma_map_single(dev, xdpf->data, xdpf->len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(dev, dma))
|
||||
return LIBETH_XDP_DROP;
|
||||
|
||||
*libeth_xdp_xmit_frame_dma(xdpf) = dma;
|
||||
|
||||
bq->bulk[bq->count++] = (typeof(*bq->bulk)){
|
||||
.xdpf = xdpf,
|
||||
.len = xdpf->len,
|
||||
.flags = LIBETH_XDP_TX_FIRST,
|
||||
};
|
||||
|
||||
if (!xdp_frame_has_frags(xdpf))
|
||||
return LIBETH_XDP_PASS;
|
||||
|
||||
bq->bulk[bq->count - 1].flags |= LIBETH_XDP_TX_MULTI;
|
||||
|
||||
return LIBETH_XDP_TX;
|
||||
}
|
||||
|
||||
/**
|
||||
* libeth_xdp_xmit_queue_frag - internal helper for queueing one XDP xmit frag
|
||||
* @bq: XDP Tx bulk to queue the frag to
|
||||
* @frag: frag to queue
|
||||
* @dev: device to perform DMA mapping
|
||||
*
|
||||
* Return: true on success, false on DMA mapping error.
|
||||
*/
|
||||
static inline bool libeth_xdp_xmit_queue_frag(struct libeth_xdp_tx_bulk *bq,
|
||||
const skb_frag_t *frag,
|
||||
struct device *dev)
|
||||
{
|
||||
dma_addr_t dma;
|
||||
|
||||
dma = skb_frag_dma_map(dev, frag);
|
||||
if (dma_mapping_error(dev, dma))
|
||||
return false;
|
||||
|
||||
bq->bulk[bq->count++] = (typeof(*bq->bulk)){
|
||||
.dma = dma,
|
||||
.len = skb_frag_size(frag),
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* libeth_xdp_xmit_queue_bulk - internal helper for queueing one XDP xmit frame
|
||||
* @bq: XDP Tx bulk to queue the frame to
|
||||
* @xdpf: XDP frame to queue
|
||||
* @flush_bulk: driver callback to flush the bulk to the HW queue
|
||||
*
|
||||
* Return: ``LIBETH_XDP_TX`` on success,
|
||||
* ``LIBETH_XDP_DROP`` if the frame should be dropped by the stack,
|
||||
* ``LIBETH_XDP_ABORTED`` if the frame will be dropped by libeth_xdp.
|
||||
*/
|
||||
static __always_inline u32
|
||||
libeth_xdp_xmit_queue_bulk(struct libeth_xdp_tx_bulk *bq,
|
||||
struct xdp_frame *xdpf,
|
||||
bool (*flush_bulk)(struct libeth_xdp_tx_bulk *bq,
|
||||
u32 flags))
|
||||
{
|
||||
u32 head, nr_frags, i, ret = LIBETH_XDP_TX;
|
||||
struct device *dev = bq->dev->dev.parent;
|
||||
const struct skb_shared_info *sinfo;
|
||||
|
||||
if (unlikely(bq->count == LIBETH_XDP_TX_BULK) &&
|
||||
unlikely(!flush_bulk(bq, LIBETH_XDP_TX_NDO)))
|
||||
return LIBETH_XDP_DROP;
|
||||
|
||||
head = libeth_xdp_xmit_queue_head(bq, xdpf, dev);
|
||||
if (head == LIBETH_XDP_PASS)
|
||||
goto out;
|
||||
else if (head == LIBETH_XDP_DROP)
|
||||
return LIBETH_XDP_DROP;
|
||||
|
||||
sinfo = xdp_get_shared_info_from_frame(xdpf);
|
||||
nr_frags = sinfo->nr_frags;
|
||||
|
||||
for (i = 0; i < nr_frags; i++) {
|
||||
if (unlikely(bq->count == LIBETH_XDP_TX_BULK) &&
|
||||
unlikely(!flush_bulk(bq, LIBETH_XDP_TX_NDO)))
|
||||
break;
|
||||
|
||||
if (!libeth_xdp_xmit_queue_frag(bq, &sinfo->frags[i], dev))
|
||||
break;
|
||||
}
|
||||
|
||||
if (unlikely(i < nr_frags))
|
||||
ret = LIBETH_XDP_ABORTED;
|
||||
|
||||
out:
|
||||
bq->bulk[bq->count - 1].flags |= LIBETH_XDP_TX_LAST;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* libeth_xdp_xmit_fill_buf - internal helper to fill one XDP xmit &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 mapped DMA and other info to pass to
|
||||
* the driver callback.
|
||||
*/
|
||||
static inline struct libeth_xdp_tx_desc
|
||||
libeth_xdp_xmit_fill_buf(struct libeth_xdp_tx_frame frm, u32 i,
|
||||
const struct libeth_xdpsq *sq, u64 priv)
|
||||
{
|
||||
struct libeth_xdp_tx_desc desc;
|
||||
struct libeth_sqe *sqe;
|
||||
struct xdp_frame *xdpf;
|
||||
|
||||
if (frm.flags & LIBETH_XDP_TX_FIRST) {
|
||||
xdpf = frm.xdpf;
|
||||
desc.addr = *libeth_xdp_xmit_frame_dma(xdpf);
|
||||
} else {
|
||||
xdpf = NULL;
|
||||
desc.addr = frm.dma;
|
||||
}
|
||||
desc.opts = frm.opts;
|
||||
|
||||
sqe = &sq->sqes[i];
|
||||
dma_unmap_addr_set(sqe, dma, desc.addr);
|
||||
dma_unmap_len_set(sqe, len, desc.len);
|
||||
|
||||
if (!xdpf) {
|
||||
sqe->type = LIBETH_SQE_XDP_XMIT_FRAG;
|
||||
return desc;
|
||||
}
|
||||
|
||||
sqe->type = LIBETH_SQE_XDP_XMIT;
|
||||
sqe->xdpf = xdpf;
|
||||
libeth_xdp_tx_fill_stats(sqe, &desc,
|
||||
xdp_get_shared_info_from_frame(xdpf));
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* libeth_xdp_xmit_flush_bulk - wrapper to define flush of one XDP xmit 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
|
||||
*/
|
||||
#define libeth_xdp_xmit_flush_bulk(bq, flags, prep, xmit) \
|
||||
__libeth_xdp_tx_flush_bulk(bq, (flags) | LIBETH_XDP_TX_NDO, prep, \
|
||||
libeth_xdp_xmit_fill_buf, xmit)
|
||||
|
||||
u32 libeth_xdp_xmit_return_bulk(const struct libeth_xdp_tx_frame *bq,
|
||||
u32 count, const struct net_device *dev);
|
||||
|
||||
/**
|
||||
* __libeth_xdp_xmit_do_bulk - internal function to implement .ndo_xdp_xmit()
|
||||
* @bq: XDP Tx bulk to queue frames to
|
||||
* @frames: XDP frames passed by the stack
|
||||
* @n: number of frames
|
||||
* @flags: flags passed by the stack
|
||||
* @flush_bulk: driver callback to flush an XDP xmit bulk
|
||||
* @finalize: driver callback to finalize sending XDP Tx frames on the queue
|
||||
*
|
||||
* Perform common checks, map the frags and queue them to the bulk, then flush
|
||||
* the bulk to the XDPSQ. If requested by the stack, finalize the queue.
|
||||
*
|
||||
* Return: number of frames send or -errno on error.
|
||||
*/
|
||||
static __always_inline int
|
||||
__libeth_xdp_xmit_do_bulk(struct libeth_xdp_tx_bulk *bq,
|
||||
struct xdp_frame **frames, u32 n, u32 flags,
|
||||
bool (*flush_bulk)(struct libeth_xdp_tx_bulk *bq,
|
||||
u32 flags),
|
||||
void (*finalize)(void *xdpsq, bool sent, bool flush))
|
||||
{
|
||||
u32 nxmit = 0;
|
||||
|
||||
if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
for (u32 i = 0; likely(i < n); i++) {
|
||||
u32 ret;
|
||||
|
||||
ret = libeth_xdp_xmit_queue_bulk(bq, frames[i], flush_bulk);
|
||||
if (unlikely(ret != LIBETH_XDP_TX)) {
|
||||
nxmit += ret == LIBETH_XDP_ABORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
nxmit++;
|
||||
}
|
||||
|
||||
if (bq->count) {
|
||||
flush_bulk(bq, LIBETH_XDP_TX_NDO);
|
||||
if (unlikely(bq->count))
|
||||
nxmit -= libeth_xdp_xmit_return_bulk(bq->bulk,
|
||||
bq->count,
|
||||
bq->dev);
|
||||
}
|
||||
|
||||
finalize(bq->xdpsq, nxmit, flags & XDP_XMIT_FLUSH);
|
||||
|
||||
return nxmit;
|
||||
}
|
||||
|
||||
/* Rx polling path */
|
||||
|
||||
static inline void libeth_xdp_return_va(const void *data, bool napi)
|
||||
|
||||
Reference in New Issue
Block a user