When using rte_bpf_load_ex allow up to 5 arguments for a BPF program. Particularly useful for call-backs and other internal functions.
Signed-off-by: Marat Khalili <[email protected]> --- lib/bpf/bpf.c | 32 +++++++++-- lib/bpf/bpf_exec.c | 116 +++++++++++++++++++++++++++++++++++++++ lib/bpf/bpf_impl.h | 2 +- lib/bpf/bpf_jit_arm64.c | 2 +- lib/bpf/bpf_jit_x86.c | 2 +- lib/bpf/bpf_load.c | 6 ++- lib/bpf/bpf_validate.c | 45 ++++++++++++---- lib/bpf/rte_bpf.h | 117 ++++++++++++++++++++++++++++++++++++++-- 8 files changed, 299 insertions(+), 23 deletions(-) diff --git a/lib/bpf/bpf.c b/lib/bpf/bpf.c index 5239b3e11e0e..67dededd9ae8 100644 --- a/lib/bpf/bpf.c +++ b/lib/bpf/bpf.c @@ -16,8 +16,8 @@ void rte_bpf_destroy(struct rte_bpf *bpf) { if (bpf != NULL) { - if (bpf->jit.func != NULL) - munmap(bpf->jit.func, bpf->jit.sz); + if (bpf->jit.raw != NULL) + munmap(bpf->jit.raw, bpf->jit.sz); munmap(bpf, bpf->sz); } } @@ -29,7 +29,33 @@ rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit) if (bpf == NULL || jit == NULL) return -EINVAL; - jit[0] = bpf->jit; + if (bpf->prm.nb_prog_arg != 1) { + RTE_BPF_LOG_LINE(ERR, + "this program takes %d arguments, use rte_bpf_get_jit_ex", + bpf->prm.nb_prog_arg); + return -EINVAL; + } + + *jit = (struct rte_bpf_jit) { + .func = bpf->jit.raw, + .sz = bpf->jit.sz, + }; + return 0; +} + +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_get_jit_ex, 26.11) +int +rte_bpf_get_jit_ex(const struct rte_bpf *bpf, struct rte_bpf_jit_ex *jit) +{ + if (bpf == NULL || jit == NULL) + return -EINVAL; + + if (bpf->jit.raw == NULL) { + RTE_BPF_LOG_LINE(ERR, "no JIT-compiled version"); + return -ENOENT; + } + + *jit = bpf->jit; return 0; } diff --git a/lib/bpf/bpf_exec.c b/lib/bpf/bpf_exec.c index e4668ba10b64..d77c59991632 100644 --- a/lib/bpf/bpf_exec.c +++ b/lib/bpf/bpf_exec.c @@ -502,6 +502,10 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], uint64_t reg[EBPF_REG_NUM]; uint64_t stack[MAX_BPF_STACK_SIZE / sizeof(uint64_t)]; + if (bpf->prm.nb_prog_arg != 1) + /* Use rte_bpf_exec_burst_ex with this program. */ + return -EINVAL; + for (i = 0; i != num; i++) { reg[EBPF_REG_1] = (uintptr_t)ctx[i]; @@ -513,6 +517,107 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], return i; } +static uint32_t +exec_vm_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx, + uint64_t rc[], uint32_t num) +{ + uint32_t i; + uint64_t reg[EBPF_REG_NUM]; + uint64_t stack[MAX_BPF_STACK_SIZE / sizeof(uint64_t)]; + + for (i = 0; i != num; i++) { + const union rte_bpf_func_arg *const arg = ctx[i].arg; + + switch (bpf->prm.nb_prog_arg) { + case 5: + reg[EBPF_REG_5] = arg[4].u64; + /* FALLTHROUGH */ + case 4: + reg[EBPF_REG_4] = arg[3].u64; + /* FALLTHROUGH */ + case 3: + reg[EBPF_REG_3] = arg[2].u64; + /* FALLTHROUGH */ + case 2: + reg[EBPF_REG_2] = arg[1].u64; + /* FALLTHROUGH */ + case 1: + reg[EBPF_REG_1] = arg[0].u64; + /* FALLTHROUGH */ + case 0: + break; + } + + reg[EBPF_REG_10] = (uintptr_t)(stack + RTE_DIM(stack)); + + rc[i] = bpf_exec(bpf, reg); + } + + return i; +} + +static uint32_t +exec_jit_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx, + uint64_t rc[], uint32_t num) +{ + uint32_t i; + const struct rte_bpf_jit_ex jit = bpf->jit; + + switch (bpf->prm.nb_prog_arg) { + case 0: + for (i = 0; i != num; i++) + rc[i] = jit.func0(); + break; + case 1: + for (i = 0; i != num; i++) { + const union rte_bpf_func_arg *const arg = ctx[i].arg; + rc[i] = jit.func1(arg[0]); + } + break; + case 2: + for (i = 0; i != num; i++) { + const union rte_bpf_func_arg *const arg = ctx[i].arg; + rc[i] = jit.func2(arg[0], arg[1]); + } + break; + case 3: + for (i = 0; i != num; i++) { + const union rte_bpf_func_arg *const arg = ctx[i].arg; + rc[i] = jit.func3(arg[0], arg[1], arg[2]); + } + break; + case 4: + for (i = 0; i != num; i++) { + const union rte_bpf_func_arg *const arg = ctx[i].arg; + rc[i] = jit.func4(arg[0], arg[1], arg[2], arg[3]); + } + break; + case 5: + for (i = 0; i != num; i++) { + const union rte_bpf_func_arg *const arg = ctx[i].arg; + rc[i] = jit.func5(arg[0], arg[1], arg[2], arg[3], arg[4]); + } + break; + default: + return -EINVAL; + } + + return i; +} + +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_exec_burst_ex, 26.11) +uint32_t +rte_bpf_exec_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx, + uint64_t rc[], uint32_t num, uint64_t flags) +{ + if ((flags & ~RTE_BPF_EXEC_FLAG_MASK) != 0) + return -EINVAL; + + return (flags & RTE_BPF_EXEC_FLAG_JIT) != 0 ? + exec_jit_burst_ex(bpf, ctx, rc, num) : + exec_vm_burst_ex(bpf, ctx, rc, num); +} + RTE_EXPORT_SYMBOL(rte_bpf_exec) uint64_t rte_bpf_exec(const struct rte_bpf *bpf, void *ctx) @@ -522,3 +627,14 @@ rte_bpf_exec(const struct rte_bpf *bpf, void *ctx) rte_bpf_exec_burst(bpf, &ctx, &rc, 1); return rc; } + +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_exec_ex, 26.11) +uint64_t +rte_bpf_exec_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx, + uint64_t flags) +{ + uint64_t rc; + + rte_bpf_exec_burst_ex(bpf, ctx, &rc, 1, flags); + return rc; +} diff --git a/lib/bpf/bpf_impl.h b/lib/bpf/bpf_impl.h index 1cee109bc98a..4a98b3373067 100644 --- a/lib/bpf/bpf_impl.h +++ b/lib/bpf/bpf_impl.h @@ -12,7 +12,7 @@ struct rte_bpf { struct rte_bpf_prm_ex prm; - struct rte_bpf_jit jit; + struct rte_bpf_jit_ex jit; size_t sz; uint32_t stack_sz; }; diff --git a/lib/bpf/bpf_jit_arm64.c b/lib/bpf/bpf_jit_arm64.c index 9e5e142c13ba..ba7ae4d680c5 100644 --- a/lib/bpf/bpf_jit_arm64.c +++ b/lib/bpf/bpf_jit_arm64.c @@ -1471,7 +1471,7 @@ __rte_bpf_jit_arm64(struct rte_bpf *bpf) /* Flush the icache */ __builtin___clear_cache((char *)ctx.ins, (char *)(ctx.ins + ctx.idx)); - bpf->jit.func = (void *)ctx.ins; + bpf->jit.raw = ctx.ins; bpf->jit.sz = size; goto finish; diff --git a/lib/bpf/bpf_jit_x86.c b/lib/bpf/bpf_jit_x86.c index 6f4235d43499..54eb279643b9 100644 --- a/lib/bpf/bpf_jit_x86.c +++ b/lib/bpf/bpf_jit_x86.c @@ -1568,7 +1568,7 @@ __rte_bpf_jit_x86(struct rte_bpf *bpf) if (rc != 0) munmap(st.ins, st.sz); else { - bpf->jit.func = (void *)st.ins; + bpf->jit.raw = st.ins; bpf->jit.sz = st.sz; } diff --git a/lib/bpf/bpf_load.c b/lib/bpf/bpf_load.c index 650184167609..c9cbaf6ded7e 100644 --- a/lib/bpf/bpf_load.c +++ b/lib/bpf/bpf_load.c @@ -144,7 +144,8 @@ rte_bpf_load(const struct rte_bpf_prm *prm) .raw.nb_ins = prm->nb_ins, .xsym = prm->xsym, .nb_xsym = prm->nb_xsym, - .prog_arg = prm->prog_arg, + .prog_arg[0] = prm->prog_arg, + .nb_prog_arg = 1, }); } @@ -160,7 +161,8 @@ rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname, .elf_file.section = sname, .xsym = prm->xsym, .nb_xsym = prm->nb_xsym, - .prog_arg = prm->prog_arg, + .prog_arg[0] = prm->prog_arg, + .nb_prog_arg = 1, }); } diff --git a/lib/bpf/bpf_validate.c b/lib/bpf/bpf_validate.c index 5bfc59296d05..bf8a4abb5a5a 100644 --- a/lib/bpf/bpf_validate.c +++ b/lib/bpf/bpf_validate.c @@ -2425,10 +2425,14 @@ evaluate(struct bpf_verifier *bvf) .s = {.min = MAX_BPF_STACK_SIZE, .max = MAX_BPF_STACK_SIZE}, }; - bvf->evst->rv[EBPF_REG_1].v = bvf->prm->prog_arg; - bvf->evst->rv[EBPF_REG_1].mask = UINT64_MAX; - if (bvf->prm->prog_arg.type == RTE_BPF_ARG_RAW) - eval_max_bound(bvf->evst->rv + EBPF_REG_1, UINT64_MAX); + for (uint32_t pai = 0; pai != bvf->prm->nb_prog_arg; ++pai) { + struct bpf_reg_val *reg = &bvf->evst->rv[EBPF_REG_1 + pai]; + + reg->v = bvf->prm->prog_arg[pai]; + reg->mask = UINT64_MAX; + if (reg->v.type == RTE_BPF_ARG_RAW) + eval_max_bound(reg, UINT64_MAX); + } bvf->evst->rv[EBPF_REG_10] = rvfp; @@ -2521,21 +2525,42 @@ evaluate(struct bpf_verifier *bvf) return rc; } +static bool +prog_arg_is_valid(const struct rte_bpf_arg *prog_arg) +{ + /* check input argument type, don't allow mbuf ptr on 32-bit */ + if (prog_arg->type != RTE_BPF_ARG_RAW && + prog_arg->type != RTE_BPF_ARG_PTR && + (sizeof(uint64_t) != sizeof(uintptr_t) || + prog_arg->type != RTE_BPF_ARG_PTR_MBUF)) { + RTE_BPF_LOG_FUNC_LINE(ERR, "unsupported argument type"); + return false; + } + + return true; +} + int __rte_bpf_validate(const struct rte_bpf_prm_ex *prm, uint32_t *stack_sz) { int32_t rc; struct bpf_verifier bvf; - /* check input argument type, don't allow mbuf ptr on 32-bit */ - if (prm->prog_arg.type != RTE_BPF_ARG_RAW && - prm->prog_arg.type != RTE_BPF_ARG_PTR && - (sizeof(uint64_t) != sizeof(uintptr_t) || - prm->prog_arg.type != RTE_BPF_ARG_PTR_MBUF)) { - RTE_BPF_LOG_FUNC_LINE(ERR, "unsupported argument type"); + if (prm->nb_prog_arg > EBPF_FUNC_MAX_ARGS) { + RTE_BPF_LOG_FUNC_LINE(ERR, + "support up to %u arguments, found %u", + EBPF_FUNC_MAX_ARGS, prm->nb_prog_arg); return -ENOTSUP; } + for (uint32_t pai = 0; pai != prm->nb_prog_arg; ++pai) + if (!prog_arg_is_valid(&prm->prog_arg[pai])) { + RTE_BPF_LOG_FUNC_LINE(ERR, + "unsupported argument %d (r%d) type", + pai, EBPF_REG_1 + pai); + return -ENOTSUP; + } + memset(&bvf, 0, sizeof(bvf)); bvf.prm = prm; bvf.in = calloc(prm->raw.nb_ins, sizeof(bvf.in[0])); diff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h index bf58a418191e..751b879bb7fd 100644 --- a/lib/bpf/rte_bpf.h +++ b/lib/bpf/rte_bpf.h @@ -25,6 +25,11 @@ extern "C" { #endif +#define RTE_BPF_EXEC_FLAG_JIT RTE_BIT64(0) /**< use JIT-compiled version */ + +/** Mask with all supported `RTE_BPF_EXEC_FLAG_*` flags set. */ +#define RTE_BPF_EXEC_FLAG_MASK RTE_BPF_EXEC_FLAG_JIT + /** * Possible types for function/BPF program arguments. */ @@ -122,7 +127,8 @@ struct rte_bpf_prm_ex { /**< array of external symbols that eBPF code is allowed to reference */ uint32_t nb_xsym; /**< number of elements in xsym */ - struct rte_bpf_arg prog_arg; /**< input arg description */ + struct rte_bpf_arg prog_arg[EBPF_FUNC_MAX_ARGS]; /**< program arguments */ + uint32_t nb_prog_arg; /**< program argument count */ }; /** @@ -138,13 +144,49 @@ struct rte_bpf_prm { }; /** - * Information about compiled into native ISA eBPF code. + * Information about compiled into native ISA eBPF code accepting 1 argument. */ struct rte_bpf_jit { uint64_t (*func)(void *); /**< JIT-ed native code */ size_t sz; /**< size of JIT-ed code */ }; +union rte_bpf_func_arg { + uint64_t u64; + void *ptr; +}; + +typedef uint64_t (*rte_bpf_jit_func0_t)(void); +typedef uint64_t (*rte_bpf_jit_func1_t)(union rte_bpf_func_arg); +typedef uint64_t (*rte_bpf_jit_func2_t)(union rte_bpf_func_arg, union rte_bpf_func_arg); +typedef uint64_t (*rte_bpf_jit_func3_t)(union rte_bpf_func_arg, union rte_bpf_func_arg, + union rte_bpf_func_arg); +typedef uint64_t (*rte_bpf_jit_func4_t)(union rte_bpf_func_arg, union rte_bpf_func_arg, + union rte_bpf_func_arg, union rte_bpf_func_arg); +typedef uint64_t (*rte_bpf_jit_func5_t)(union rte_bpf_func_arg, union rte_bpf_func_arg, + union rte_bpf_func_arg, union rte_bpf_func_arg, union rte_bpf_func_arg); + +/** + * JIT-ed native code, member depends on number of program arguments. + */ +struct rte_bpf_jit_ex { + union { + void *raw; + rte_bpf_jit_func0_t func0; /* nullary function */ + rte_bpf_jit_func1_t func1; /* unary function */ + rte_bpf_jit_func2_t func2; /* binary function */ + rte_bpf_jit_func3_t func3; /* ternary function */ + rte_bpf_jit_func4_t func4; /* quaternary function */ + rte_bpf_jit_func5_t func5; /* quinary function */ + }; + size_t sz; +}; + +/* Tuple of eBPF program arguments. */ +struct rte_bpf_prog_ctx { + union rte_bpf_func_arg arg[EBPF_FUNC_MAX_ARGS]; +}; + struct rte_bpf; /** @@ -224,7 +266,7 @@ rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname, __rte_malloc __rte_dealloc(rte_bpf_destroy, 1); /** - * Execute given BPF bytecode. + * Execute given BPF bytecode accepting 1 argument. * * @param bpf * handle for the BPF code to execute. @@ -237,7 +279,27 @@ uint64_t rte_bpf_exec(const struct rte_bpf *bpf, void *ctx); /** - * Execute given BPF bytecode over a set of input contexts. + * @warning + * @b EXPERIMENTAL: This API may change, or be removed, without prior notice. + * + * Execute given BPF bytecode accepting any number of arguments. + * + * @param bpf + * handle for the BPF code to execute. + * @param ctx + * program arguments tuple. + * @param flags + * bitwise OR of `RTE_BPF_EXEC_FLAG_*` values controlling execution. + * @return + * BPF execution return value. + */ +__rte_experimental +uint64_t +rte_bpf_exec_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx, + uint64_t flags); + +/** + * Execute given BPF bytecode accepting 1 argument over a set of input contexts. * * @param bpf * handle for the BPF code to execute. @@ -255,7 +317,33 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], uint32_t num); /** - * Provide information about natively compiled code for given BPF handle. + * @warning + * @b EXPERIMENTAL: This API may change, or be removed, without prior notice. + * + * Execute given BPF program accepting any number of arguments over a set of + * input contexts. + * + * @param bpf + * handle for the BPF code to execute. + * @param ctx + * pointer to array of program argument tuples, can be NULL for nullary programs. + * @param rc + * array of return values (one per input). + * @param num + * number executions, number of elements in arrays ctx and rc[]. + * @param flags + * bitwise OR of `RTE_BPF_EXEC_FLAG_*` values controlling execution. + * @return + * number of successfully processed inputs. + */ +__rte_experimental +uint32_t +rte_bpf_exec_burst_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx, + uint64_t rc[], uint32_t num, uint64_t flags); + +/** + * Provide information about natively compiled code for given BPF program + * accepting 1 argument. * * @param bpf * handle for the BPF code. @@ -268,6 +356,25 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], int rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit); +/** + * @warning + * @b EXPERIMENTAL: This API may change, or be removed, without prior notice. + * + * Get function JIT-compiled from the BPF program. + * + * @param bpf + * handle for the BPF code. + * @param jit + * pointer to the struct rte_bpf_jit_ex. + * @return + * - -EINVAL if the parameters are invalid. + * - -ENOENT if there is no JIT-compiled version. + * - Zero if operation completed successfully. + */ +__rte_experimental +int +rte_bpf_get_jit_ex(const struct rte_bpf *bpf, struct rte_bpf_jit_ex *jit); + /** * Dump epf instructions to a file. * -- 2.43.0

