mirror of https://github.com/torvalds/linux.git
272 lines
6.7 KiB
C
272 lines
6.7 KiB
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/xsk.h>
|
|
|
|
#include "priv.h"
|
|
|
|
/* ``XDP_TX`` bulking */
|
|
|
|
void __cold libeth_xsk_tx_return_bulk(const struct libeth_xdp_tx_frame *bq,
|
|
u32 count)
|
|
{
|
|
for (u32 i = 0; i < count; i++)
|
|
libeth_xsk_buff_free_slow(bq[i].xsk);
|
|
}
|
|
|
|
/* XSk TMO */
|
|
|
|
const struct xsk_tx_metadata_ops libeth_xsktmo_slow = {
|
|
.tmo_request_checksum = libeth_xsktmo_req_csum,
|
|
};
|
|
|
|
/* Rx polling path */
|
|
|
|
/**
|
|
* libeth_xsk_buff_free_slow - free an XSk Rx buffer
|
|
* @xdp: buffer to free
|
|
*
|
|
* Slowpath version of xsk_buff_free() to be used on exceptions, cleanups etc.
|
|
* to avoid unwanted inlining.
|
|
*/
|
|
void libeth_xsk_buff_free_slow(struct libeth_xdp_buff *xdp)
|
|
{
|
|
xsk_buff_free(&xdp->base);
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xsk_buff_free_slow);
|
|
|
|
/**
|
|
* libeth_xsk_buff_add_frag - add frag to XSk Rx buffer
|
|
* @head: head buffer
|
|
* @xdp: frag buffer
|
|
*
|
|
* External helper used by libeth_xsk_process_buff(), do not call directly.
|
|
* Frees both main and frag buffers on error.
|
|
*
|
|
* Return: main buffer with attached frag on success, %NULL on error (no space
|
|
* for a new frag).
|
|
*/
|
|
struct libeth_xdp_buff *libeth_xsk_buff_add_frag(struct libeth_xdp_buff *head,
|
|
struct libeth_xdp_buff *xdp)
|
|
{
|
|
if (!xsk_buff_add_frag(&head->base, &xdp->base))
|
|
goto free;
|
|
|
|
return head;
|
|
|
|
free:
|
|
libeth_xsk_buff_free_slow(xdp);
|
|
libeth_xsk_buff_free_slow(head);
|
|
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xsk_buff_add_frag);
|
|
|
|
/**
|
|
* libeth_xsk_buff_stats_frags - update onstack RQ stats with XSk frags info
|
|
* @rs: onstack stats to update
|
|
* @xdp: buffer to account
|
|
*
|
|
* External helper used by __libeth_xsk_run_pass(), do not call directly.
|
|
* Adds buffer's frags count and total len to the onstack stats.
|
|
*/
|
|
void libeth_xsk_buff_stats_frags(struct libeth_rq_napi_stats *rs,
|
|
const struct libeth_xdp_buff *xdp)
|
|
{
|
|
libeth_xdp_buff_stats_frags(rs, xdp);
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xsk_buff_stats_frags);
|
|
|
|
/**
|
|
* __libeth_xsk_run_prog_slow - process the non-``XDP_REDIRECT`` verdicts
|
|
* @xdp: buffer to process
|
|
* @bq: Tx bulk for queueing on ``XDP_TX``
|
|
* @act: verdict to process
|
|
* @ret: error code if ``XDP_REDIRECT`` failed
|
|
*
|
|
* External helper used by __libeth_xsk_run_prog(), do not call directly.
|
|
* ``XDP_REDIRECT`` is the most common and hottest verdict on XSk, thus
|
|
* it is processed inline. The rest goes here for out-of-line processing,
|
|
* together with redirect errors.
|
|
*
|
|
* Return: libeth_xdp XDP prog verdict.
|
|
*/
|
|
u32 __libeth_xsk_run_prog_slow(struct libeth_xdp_buff *xdp,
|
|
const struct libeth_xdp_tx_bulk *bq,
|
|
enum xdp_action act, int ret)
|
|
{
|
|
switch (act) {
|
|
case XDP_DROP:
|
|
xsk_buff_free(&xdp->base);
|
|
|
|
return LIBETH_XDP_DROP;
|
|
case XDP_TX:
|
|
return LIBETH_XDP_TX;
|
|
case XDP_PASS:
|
|
return LIBETH_XDP_PASS;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return libeth_xdp_prog_exception(bq, xdp, act, ret);
|
|
}
|
|
EXPORT_SYMBOL_GPL(__libeth_xsk_run_prog_slow);
|
|
|
|
/**
|
|
* libeth_xsk_prog_exception - handle XDP prog exceptions on XSk
|
|
* @xdp: buffer to process
|
|
* @act: verdict returned by the prog
|
|
* @ret: error code if ``XDP_REDIRECT`` failed
|
|
*
|
|
* Internal. Frees the buffer and, if the queue uses XSk wakeups, stop the
|
|
* current NAPI poll when there are no free buffers left.
|
|
*
|
|
* Return: libeth_xdp's XDP prog verdict.
|
|
*/
|
|
u32 __cold libeth_xsk_prog_exception(struct libeth_xdp_buff *xdp,
|
|
enum xdp_action act, int ret)
|
|
{
|
|
const struct xdp_buff_xsk *xsk;
|
|
u32 __ret = LIBETH_XDP_DROP;
|
|
|
|
if (act != XDP_REDIRECT)
|
|
goto drop;
|
|
|
|
xsk = container_of(&xdp->base, typeof(*xsk), xdp);
|
|
if (xsk_uses_need_wakeup(xsk->pool) && ret == -ENOBUFS)
|
|
__ret = LIBETH_XDP_ABORTED;
|
|
|
|
drop:
|
|
libeth_xsk_buff_free_slow(xdp);
|
|
|
|
return __ret;
|
|
}
|
|
|
|
/* Refill */
|
|
|
|
/**
|
|
* libeth_xskfq_create - create an XSkFQ
|
|
* @fq: fill queue to initialize
|
|
*
|
|
* Allocates the FQEs and initializes the fields used by libeth_xdp: number
|
|
* of buffers to refill, refill threshold and buffer len.
|
|
*
|
|
* Return: %0 on success, -errno otherwise.
|
|
*/
|
|
int libeth_xskfq_create(struct libeth_xskfq *fq)
|
|
{
|
|
fq->fqes = kvcalloc_node(fq->count, sizeof(*fq->fqes), GFP_KERNEL,
|
|
fq->nid);
|
|
if (!fq->fqes)
|
|
return -ENOMEM;
|
|
|
|
fq->pending = fq->count;
|
|
fq->thresh = libeth_xdp_queue_threshold(fq->count);
|
|
fq->buf_len = xsk_pool_get_rx_frame_size(fq->pool);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xskfq_create);
|
|
|
|
/**
|
|
* libeth_xskfq_destroy - destroy an XSkFQ
|
|
* @fq: fill queue to destroy
|
|
*
|
|
* Zeroes the used fields and frees the FQEs array.
|
|
*/
|
|
void libeth_xskfq_destroy(struct libeth_xskfq *fq)
|
|
{
|
|
fq->buf_len = 0;
|
|
fq->thresh = 0;
|
|
fq->pending = 0;
|
|
|
|
kvfree(fq->fqes);
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xskfq_destroy);
|
|
|
|
/* .ndo_xsk_wakeup */
|
|
|
|
static void libeth_xsk_napi_sched(void *info)
|
|
{
|
|
__napi_schedule_irqoff(info);
|
|
}
|
|
|
|
/**
|
|
* libeth_xsk_init_wakeup - initialize libeth XSk wakeup structure
|
|
* @csd: struct to initialize
|
|
* @napi: NAPI corresponding to this queue
|
|
*
|
|
* libeth_xdp uses inter-processor interrupts to perform XSk wakeups. In order
|
|
* to do that, the corresponding CSDs must be initialized when creating the
|
|
* queues.
|
|
*/
|
|
void libeth_xsk_init_wakeup(call_single_data_t *csd, struct napi_struct *napi)
|
|
{
|
|
INIT_CSD(csd, libeth_xsk_napi_sched, napi);
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xsk_init_wakeup);
|
|
|
|
/**
|
|
* libeth_xsk_wakeup - perform an XSk wakeup
|
|
* @csd: CSD corresponding to the queue
|
|
* @qid: the stack queue index
|
|
*
|
|
* Try to mark the NAPI as missed first, so that it could be rescheduled.
|
|
* If it's not, schedule it on the corresponding CPU using IPIs (or directly
|
|
* if already running on it).
|
|
*/
|
|
void libeth_xsk_wakeup(call_single_data_t *csd, u32 qid)
|
|
{
|
|
struct napi_struct *napi = csd->info;
|
|
|
|
if (napi_if_scheduled_mark_missed(napi) ||
|
|
unlikely(!napi_schedule_prep(napi)))
|
|
return;
|
|
|
|
if (unlikely(qid >= nr_cpu_ids))
|
|
qid %= nr_cpu_ids;
|
|
|
|
if (qid != raw_smp_processor_id() && cpu_online(qid))
|
|
smp_call_function_single_async(qid, csd);
|
|
else
|
|
__napi_schedule(napi);
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xsk_wakeup);
|
|
|
|
/* Pool setup */
|
|
|
|
#define LIBETH_XSK_DMA_ATTR \
|
|
(DMA_ATTR_WEAK_ORDERING | DMA_ATTR_SKIP_CPU_SYNC)
|
|
|
|
/**
|
|
* libeth_xsk_setup_pool - setup or destroy an XSk pool for a queue
|
|
* @dev: target &net_device
|
|
* @qid: stack queue index to configure
|
|
* @enable: whether to enable or disable the pool
|
|
*
|
|
* Check that @qid is valid and then map or unmap the pool.
|
|
*
|
|
* Return: %0 on success, -errno otherwise.
|
|
*/
|
|
int libeth_xsk_setup_pool(struct net_device *dev, u32 qid, bool enable)
|
|
{
|
|
struct xsk_buff_pool *pool;
|
|
|
|
pool = xsk_get_pool_from_qid(dev, qid);
|
|
if (!pool)
|
|
return -EINVAL;
|
|
|
|
if (enable)
|
|
return xsk_pool_dma_map(pool, dev->dev.parent,
|
|
LIBETH_XSK_DMA_ATTR);
|
|
else
|
|
xsk_pool_dma_unmap(pool, LIBETH_XSK_DMA_ATTR);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(libeth_xsk_setup_pool);
|