numam-dpdk/lib/librte_bpf/bpf_pkt.c
David Marchand cfe3aeb170 remove experimental tags from all symbol definitions
We had some inconsistencies between functions prototypes and actual
definitions.
Let's avoid this by only adding the experimental tag to the prototypes.
Tests with gcc and clang show it is enough.

git grep -l __rte_experimental |grep \.c$ |while read file; do
	sed -i -e '/^__rte_experimental$/d' $file;
	sed -i -e 's/  *__rte_experimental//' $file;
	sed -i -e 's/__rte_experimental  *//' $file;
done

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Adrien Mazarguil <adrien.mazarguil@6wind.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
2019-06-29 19:04:43 +02:00

606 lines
13 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2018 Intel Corporation
*/
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/queue.h>
#include <rte_common.h>
#include <rte_byteorder.h>
#include <rte_malloc.h>
#include <rte_log.h>
#include <rte_debug.h>
#include <rte_cycles.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_atomic.h>
#include <rte_mbuf.h>
#include <rte_ethdev.h>
#include <rte_bpf_ethdev.h>
#include "bpf_impl.h"
/*
* information about installed BPF rx/tx callback
*/
struct bpf_eth_cbi {
/* used by both data & control path */
uint32_t use; /*usage counter */
const struct rte_eth_rxtx_callback *cb; /* callback handle */
struct rte_bpf *bpf;
struct rte_bpf_jit jit;
/* used by control path only */
LIST_ENTRY(bpf_eth_cbi) link;
uint16_t port;
uint16_t queue;
} __rte_cache_aligned;
/*
* Odd number means that callback is used by datapath.
* Even number means that callback is not used by datapath.
*/
#define BPF_ETH_CBI_INUSE 1
/*
* List to manage RX/TX installed callbacks.
*/
LIST_HEAD(bpf_eth_cbi_list, bpf_eth_cbi);
enum {
BPF_ETH_RX,
BPF_ETH_TX,
BPF_ETH_NUM,
};
/*
* information about all installed BPF rx/tx callbacks
*/
struct bpf_eth_cbh {
rte_spinlock_t lock;
struct bpf_eth_cbi_list list;
uint32_t type;
};
static struct bpf_eth_cbh rx_cbh = {
.lock = RTE_SPINLOCK_INITIALIZER,
.list = LIST_HEAD_INITIALIZER(list),
.type = BPF_ETH_RX,
};
static struct bpf_eth_cbh tx_cbh = {
.lock = RTE_SPINLOCK_INITIALIZER,
.list = LIST_HEAD_INITIALIZER(list),
.type = BPF_ETH_TX,
};
/*
* Marks given callback as used by datapath.
*/
static __rte_always_inline void
bpf_eth_cbi_inuse(struct bpf_eth_cbi *cbi)
{
cbi->use++;
/* make sure no store/load reordering could happen */
rte_smp_mb();
}
/*
* Marks given callback list as not used by datapath.
*/
static __rte_always_inline void
bpf_eth_cbi_unuse(struct bpf_eth_cbi *cbi)
{
/* make sure all previous loads are completed */
rte_smp_rmb();
cbi->use++;
}
/*
* Waits till datapath finished using given callback.
*/
static void
bpf_eth_cbi_wait(const struct bpf_eth_cbi *cbi)
{
uint32_t nuse, puse;
/* make sure all previous loads and stores are completed */
rte_smp_mb();
puse = cbi->use;
/* in use, busy wait till current RX/TX iteration is finished */
if ((puse & BPF_ETH_CBI_INUSE) != 0) {
do {
rte_pause();
rte_compiler_barrier();
nuse = cbi->use;
} while (nuse == puse);
}
}
static void
bpf_eth_cbi_cleanup(struct bpf_eth_cbi *bc)
{
bc->bpf = NULL;
memset(&bc->jit, 0, sizeof(bc->jit));
}
static struct bpf_eth_cbi *
bpf_eth_cbh_find(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue)
{
struct bpf_eth_cbi *cbi;
LIST_FOREACH(cbi, &cbh->list, link) {
if (cbi->port == port && cbi->queue == queue)
break;
}
return cbi;
}
static struct bpf_eth_cbi *
bpf_eth_cbh_add(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue)
{
struct bpf_eth_cbi *cbi;
/* return an existing one */
cbi = bpf_eth_cbh_find(cbh, port, queue);
if (cbi != NULL)
return cbi;
cbi = rte_zmalloc(NULL, sizeof(*cbi), RTE_CACHE_LINE_SIZE);
if (cbi != NULL) {
cbi->port = port;
cbi->queue = queue;
LIST_INSERT_HEAD(&cbh->list, cbi, link);
}
return cbi;
}
/*
* BPF packet processing routinies.
*/
static inline uint32_t
apply_filter(struct rte_mbuf *mb[], const uint64_t rc[], uint32_t num,
uint32_t drop)
{
uint32_t i, j, k;
struct rte_mbuf *dr[num];
for (i = 0, j = 0, k = 0; i != num; i++) {
/* filter matches */
if (rc[i] != 0)
mb[j++] = mb[i];
/* no match */
else
dr[k++] = mb[i];
}
if (drop != 0) {
/* free filtered out mbufs */
for (i = 0; i != k; i++)
rte_pktmbuf_free(dr[i]);
} else {
/* copy filtered out mbufs beyond good ones */
for (i = 0; i != k; i++)
mb[j + i] = dr[i];
}
return j;
}
static inline uint32_t
pkt_filter_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num,
uint32_t drop)
{
uint32_t i;
void *dp[num];
uint64_t rc[num];
for (i = 0; i != num; i++)
dp[i] = rte_pktmbuf_mtod(mb[i], void *);
rte_bpf_exec_burst(bpf, dp, rc, num);
return apply_filter(mb, rc, num, drop);
}
static inline uint32_t
pkt_filter_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[],
uint32_t num, uint32_t drop)
{
uint32_t i, n;
void *dp;
uint64_t rc[num];
n = 0;
for (i = 0; i != num; i++) {
dp = rte_pktmbuf_mtod(mb[i], void *);
rc[i] = jit->func(dp);
n += (rc[i] == 0);
}
if (n != 0)
num = apply_filter(mb, rc, num, drop);
return num;
}
static inline uint32_t
pkt_filter_mb_vm(const struct rte_bpf *bpf, struct rte_mbuf *mb[], uint32_t num,
uint32_t drop)
{
uint64_t rc[num];
rte_bpf_exec_burst(bpf, (void **)mb, rc, num);
return apply_filter(mb, rc, num, drop);
}
static inline uint32_t
pkt_filter_mb_jit(const struct rte_bpf_jit *jit, struct rte_mbuf *mb[],
uint32_t num, uint32_t drop)
{
uint32_t i, n;
uint64_t rc[num];
n = 0;
for (i = 0; i != num; i++) {
rc[i] = jit->func(mb[i]);
n += (rc[i] == 0);
}
if (n != 0)
num = apply_filter(mb, rc, num, drop);
return num;
}
/*
* RX/TX callbacks for raw data bpf.
*/
static uint16_t
bpf_rx_callback_vm(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts,
__rte_unused uint16_t max_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_vm(cbi->bpf, pkt, nb_pkts, 1) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
static uint16_t
bpf_rx_callback_jit(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts,
__rte_unused uint16_t max_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_jit(&cbi->jit, pkt, nb_pkts, 1) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
static uint16_t
bpf_tx_callback_vm(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_vm(cbi->bpf, pkt, nb_pkts, 0) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
static uint16_t
bpf_tx_callback_jit(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_jit(&cbi->jit, pkt, nb_pkts, 0) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
/*
* RX/TX callbacks for mbuf.
*/
static uint16_t
bpf_rx_callback_mb_vm(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts,
__rte_unused uint16_t max_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_mb_vm(cbi->bpf, pkt, nb_pkts, 1) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
static uint16_t
bpf_rx_callback_mb_jit(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts,
__rte_unused uint16_t max_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_mb_jit(&cbi->jit, pkt, nb_pkts, 1) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
static uint16_t
bpf_tx_callback_mb_vm(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_mb_vm(cbi->bpf, pkt, nb_pkts, 0) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
static uint16_t
bpf_tx_callback_mb_jit(__rte_unused uint16_t port, __rte_unused uint16_t queue,
struct rte_mbuf *pkt[], uint16_t nb_pkts, void *user_param)
{
struct bpf_eth_cbi *cbi;
uint16_t rc;
cbi = user_param;
bpf_eth_cbi_inuse(cbi);
rc = (cbi->cb != NULL) ?
pkt_filter_mb_jit(&cbi->jit, pkt, nb_pkts, 0) :
nb_pkts;
bpf_eth_cbi_unuse(cbi);
return rc;
}
static rte_rx_callback_fn
select_rx_callback(enum rte_bpf_arg_type type, uint32_t flags)
{
if (flags & RTE_BPF_ETH_F_JIT) {
if (type == RTE_BPF_ARG_PTR)
return bpf_rx_callback_jit;
else if (type == RTE_BPF_ARG_PTR_MBUF)
return bpf_rx_callback_mb_jit;
} else if (type == RTE_BPF_ARG_PTR)
return bpf_rx_callback_vm;
else if (type == RTE_BPF_ARG_PTR_MBUF)
return bpf_rx_callback_mb_vm;
return NULL;
}
static rte_tx_callback_fn
select_tx_callback(enum rte_bpf_arg_type type, uint32_t flags)
{
if (flags & RTE_BPF_ETH_F_JIT) {
if (type == RTE_BPF_ARG_PTR)
return bpf_tx_callback_jit;
else if (type == RTE_BPF_ARG_PTR_MBUF)
return bpf_tx_callback_mb_jit;
} else if (type == RTE_BPF_ARG_PTR)
return bpf_tx_callback_vm;
else if (type == RTE_BPF_ARG_PTR_MBUF)
return bpf_tx_callback_mb_vm;
return NULL;
}
/*
* helper function to perform BPF unload for given port/queue.
* have to introduce extra complexity (and possible slowdown) here,
* as right now there is no safe generic way to remove RX/TX callback
* while IO is active.
* Still don't free memory allocated for callback handle itself,
* again right now there is no safe way to do that without stopping RX/TX
* on given port/queue first.
*/
static void
bpf_eth_cbi_unload(struct bpf_eth_cbi *bc)
{
/* mark this cbi as empty */
bc->cb = NULL;
rte_smp_mb();
/* make sure datapath doesn't use bpf anymore, then destroy bpf */
bpf_eth_cbi_wait(bc);
rte_bpf_destroy(bc->bpf);
bpf_eth_cbi_cleanup(bc);
}
static void
bpf_eth_unload(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue)
{
struct bpf_eth_cbi *bc;
bc = bpf_eth_cbh_find(cbh, port, queue);
if (bc == NULL || bc->cb == NULL)
return;
if (cbh->type == BPF_ETH_RX)
rte_eth_remove_rx_callback(port, queue, bc->cb);
else
rte_eth_remove_tx_callback(port, queue, bc->cb);
bpf_eth_cbi_unload(bc);
}
void
rte_bpf_eth_rx_unload(uint16_t port, uint16_t queue)
{
struct bpf_eth_cbh *cbh;
cbh = &rx_cbh;
rte_spinlock_lock(&cbh->lock);
bpf_eth_unload(cbh, port, queue);
rte_spinlock_unlock(&cbh->lock);
}
void
rte_bpf_eth_tx_unload(uint16_t port, uint16_t queue)
{
struct bpf_eth_cbh *cbh;
cbh = &tx_cbh;
rte_spinlock_lock(&cbh->lock);
bpf_eth_unload(cbh, port, queue);
rte_spinlock_unlock(&cbh->lock);
}
static int
bpf_eth_elf_load(struct bpf_eth_cbh *cbh, uint16_t port, uint16_t queue,
const struct rte_bpf_prm *prm, const char *fname, const char *sname,
uint32_t flags)
{
int32_t rc;
struct bpf_eth_cbi *bc;
struct rte_bpf *bpf;
rte_rx_callback_fn frx;
rte_tx_callback_fn ftx;
struct rte_bpf_jit jit;
frx = NULL;
ftx = NULL;
if (prm == NULL || rte_eth_dev_is_valid_port(port) == 0 ||
queue >= RTE_MAX_QUEUES_PER_PORT)
return -EINVAL;
if (cbh->type == BPF_ETH_RX)
frx = select_rx_callback(prm->prog_arg.type, flags);
else
ftx = select_tx_callback(prm->prog_arg.type, flags);
if (frx == NULL && ftx == NULL) {
RTE_BPF_LOG(ERR, "%s(%u, %u): no callback selected;\n",
__func__, port, queue);
return -EINVAL;
}
bpf = rte_bpf_elf_load(prm, fname, sname);
if (bpf == NULL)
return -rte_errno;
rte_bpf_get_jit(bpf, &jit);
if ((flags & RTE_BPF_ETH_F_JIT) != 0 && jit.func == NULL) {
RTE_BPF_LOG(ERR, "%s(%u, %u): no JIT generated;\n",
__func__, port, queue);
rte_bpf_destroy(bpf);
return -ENOTSUP;
}
/* setup/update global callback info */
bc = bpf_eth_cbh_add(cbh, port, queue);
if (bc == NULL)
return -ENOMEM;
/* remove old one, if any */
if (bc->cb != NULL)
bpf_eth_unload(cbh, port, queue);
bc->bpf = bpf;
bc->jit = jit;
if (cbh->type == BPF_ETH_RX)
bc->cb = rte_eth_add_rx_callback(port, queue, frx, bc);
else
bc->cb = rte_eth_add_tx_callback(port, queue, ftx, bc);
if (bc->cb == NULL) {
rc = -rte_errno;
rte_bpf_destroy(bpf);
bpf_eth_cbi_cleanup(bc);
} else
rc = 0;
return rc;
}
int
rte_bpf_eth_rx_elf_load(uint16_t port, uint16_t queue,
const struct rte_bpf_prm *prm, const char *fname, const char *sname,
uint32_t flags)
{
int32_t rc;
struct bpf_eth_cbh *cbh;
cbh = &rx_cbh;
rte_spinlock_lock(&cbh->lock);
rc = bpf_eth_elf_load(cbh, port, queue, prm, fname, sname, flags);
rte_spinlock_unlock(&cbh->lock);
return rc;
}
int
rte_bpf_eth_tx_elf_load(uint16_t port, uint16_t queue,
const struct rte_bpf_prm *prm, const char *fname, const char *sname,
uint32_t flags)
{
int32_t rc;
struct bpf_eth_cbh *cbh;
cbh = &tx_cbh;
rte_spinlock_lock(&cbh->lock);
rc = bpf_eth_elf_load(cbh, port, queue, prm, fname, sname, flags);
rte_spinlock_unlock(&cbh->lock);
return rc;
}