| /* 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(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 |
| * @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; |
| 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 */ |