/
phprus
/
github_dpdk
Обзор
Документация
Войти
/
phprus
/
github_dpdk
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/bpf/bpf_exec.c
646 строк
17 KB
Marat Khalili
bpf: support up to 5 arguments
23 июн 2026, 12:16
23 июн 2026, 12:16
9bb8c92
Код
Авторство
О чём код?
/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2018 Intel Corporation */ #include <stdio.h> #include <stdint.h> #include <eal_export.h> #include <rte_common.h> #include <rte_log.h> #include <rte_debug.h> #include <rte_byteorder.h> #include <rte_errno.h> #include "bpf_impl.h" #define BPF_JMP_UNC(ins) ((ins) += (ins)->off) #define BPF_JMP_CND_REG(reg, ins, op, type) \ ((ins) += \ ((type)(reg)[(ins)->dst_reg] op (type)(reg)[(ins)->src_reg]) ? \ (ins)->off : 0) #define BPF_JMP_CND_IMM(reg, ins, op, type) \ ((ins) += \ ((type)(reg)[(ins)->dst_reg] op (type)(ins)->imm) ? \ (ins)->off : 0) #define BPF_NEG_ALU(reg, ins, type) \ ((reg)[(ins)->dst_reg] = (type)(-(reg)[(ins)->dst_reg])) #define EBPF_MOV_ALU_REG(reg, ins, type) \ ((reg)[(ins)->dst_reg] = (type)(reg)[(ins)->src_reg]) #define BPF_OP_ALU_REG(reg, ins, op, type) \ ((reg)[(ins)->dst_reg] = \ (type)(reg)[(ins)->dst_reg] op (type)(reg)[(ins)->src_reg]) #define EBPF_MOV_ALU_IMM(reg, ins, type) \ ((reg)[(ins)->dst_reg] = (type)(ins)->imm) #define BPF_OP_ALU_IMM(reg, ins, op, type) \ ((reg)[(ins)->dst_reg] = \ (type)(reg)[(ins)->dst_reg] op (type)(ins)->imm) #define BPF_DIV_ZERO_CHECK(bpf, reg, ins, type) do { \ if ((type)(reg)[(ins)->src_reg] == 0) { \ RTE_BPF_LOG_LINE(ERR, \ "%s(%p): division by 0 at pc: %#zx;", \ __func__, bpf, \ (uintptr_t)(ins) - (uintptr_t)(bpf)->prm.raw.ins); \ return 0; \ } \ } while (0) #define BPF_LD_REG(reg, ins, type) \ ((reg)[(ins)->dst_reg] = \ *(type *)(uintptr_t)((reg)[(ins)->src_reg] + (ins)->off)) #define BPF_ST_IMM(reg, ins, type) \ (*(type *)(uintptr_t)((reg)[(ins)->dst_reg] + (ins)->off) = \ (type)(ins)->imm) #define BPF_ST_REG(reg, ins, type) \ (*(type *)(uintptr_t)((reg)[(ins)->dst_reg] + (ins)->off) = \ (type)(reg)[(ins)->src_reg]) #define BPF_ST_ATOMIC_REG(reg, ins, tp) do { \ switch (ins->imm) { \ case BPF_ATOMIC_ADD: \ rte_atomic##tp##_add((rte_atomic##tp##_t *) \ (uintptr_t)((reg)[(ins)->dst_reg] + (ins)->off), \ (reg)[(ins)->src_reg]); \ break; \ case BPF_ATOMIC_XCHG: \ (reg)[(ins)->src_reg] = rte_atomic##tp##_exchange((uint##tp##_t *) \ (uintptr_t)((reg)[(ins)->dst_reg] + (ins)->off), \ (reg)[(ins)->src_reg]); \ break; \ default: \ /* this should be caught by validator and never reach here */ \ RTE_BPF_LOG_LINE(ERR, \ "%s(%p): unsupported atomic operation at pc: %#zx;", \ __func__, bpf, \ (uintptr_t)(ins) - (uintptr_t)(bpf)->prm.raw.ins); \ return 0; \ } \ } while (0) /* BPF_LD | BPF_ABS/BPF_IND */ #define NOP(x) (x) #define BPF_LD_ABS(bpf, reg, ins, type, op) do { \ const type *p = bpf_ld_mbuf(bpf, reg, ins, (ins)->imm, sizeof(type)); \ if (p == NULL) \ return 0; \ reg[EBPF_REG_0] = op(p[0]); \ } while (0) #define BPF_LD_IND(bpf, reg, ins, type, op) do { \ uint32_t ofs = reg[ins->src_reg] + (ins)->imm; \ const type *p = bpf_ld_mbuf(bpf, reg, ins, ofs, sizeof(type)); \ if (p == NULL) \ return 0; \ reg[EBPF_REG_0] = op(p[0]); \ } while (0) static inline void bpf_alu_be(uint64_t reg[EBPF_REG_NUM], const struct ebpf_insn *ins) { uint64_t *v; v = reg + ins->dst_reg; switch (ins->imm) { case 16: *v = rte_cpu_to_be_16(*v); break; case 32: *v = rte_cpu_to_be_32(*v); break; case 64: *v = rte_cpu_to_be_64(*v); break; } } static inline void bpf_alu_le(uint64_t reg[EBPF_REG_NUM], const struct ebpf_insn *ins) { uint64_t *v; v = reg + ins->dst_reg; switch (ins->imm) { case 16: *v = rte_cpu_to_le_16(*v); break; case 32: *v = rte_cpu_to_le_32(*v); break; case 64: *v = rte_cpu_to_le_64(*v); break; } } static inline const void * bpf_ld_mbuf(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM], const struct ebpf_insn *ins, uint32_t off, uint32_t len) { const struct rte_mbuf *mb; const void *p; mb = (const struct rte_mbuf *)(uintptr_t)reg[EBPF_REG_6]; p = rte_pktmbuf_read(mb, off, len, reg + EBPF_REG_0); if (p == NULL) RTE_BPF_LOG_LINE(DEBUG, "%s(bpf=%p, mbuf=%p, ofs=%u, len=%u): " "load beyond packet boundary at pc: %#zx;", __func__, bpf, mb, off, len, (uintptr_t)(ins) - (uintptr_t)(bpf)->prm.raw.ins); return p; } static inline uint64_t bpf_exec(const struct rte_bpf *bpf, uint64_t reg[EBPF_REG_NUM]) { const struct ebpf_insn *ins; for (ins = bpf->prm.raw.ins; ; ins++) { switch (ins->code) { /* 32 bit ALU IMM operations */ case (BPF_ALU | BPF_ADD | BPF_K): BPF_OP_ALU_IMM(reg, ins, +, uint32_t); break; case (BPF_ALU | BPF_SUB | BPF_K): BPF_OP_ALU_IMM(reg, ins, -, uint32_t); break; case (BPF_ALU | BPF_AND | BPF_K): BPF_OP_ALU_IMM(reg, ins, &, uint32_t); break; case (BPF_ALU | BPF_OR | BPF_K): BPF_OP_ALU_IMM(reg, ins, |, uint32_t); break; case (BPF_ALU | BPF_LSH | BPF_K): BPF_OP_ALU_IMM(reg, ins, <<, uint32_t); break; case (BPF_ALU | BPF_RSH | BPF_K): BPF_OP_ALU_IMM(reg, ins, >>, uint32_t); break; case (BPF_ALU | BPF_XOR | BPF_K): BPF_OP_ALU_IMM(reg, ins, ^, uint32_t); break; case (BPF_ALU | BPF_MUL | BPF_K): BPF_OP_ALU_IMM(reg, ins, *, uint32_t); break; case (BPF_ALU | BPF_DIV | BPF_K): BPF_OP_ALU_IMM(reg, ins, /, uint32_t); break; case (BPF_ALU | BPF_MOD | BPF_K): BPF_OP_ALU_IMM(reg, ins, %, uint32_t); break; case (BPF_ALU | EBPF_MOV | BPF_K): EBPF_MOV_ALU_IMM(reg, ins, uint32_t); break; /* 32 bit ALU REG operations */ case (BPF_ALU | BPF_ADD | BPF_X): BPF_OP_ALU_REG(reg, ins, +, uint32_t); break; case (BPF_ALU | BPF_SUB | BPF_X): BPF_OP_ALU_REG(reg, ins, -, uint32_t); break; case (BPF_ALU | BPF_AND | BPF_X): BPF_OP_ALU_REG(reg, ins, &, uint32_t); break; case (BPF_ALU | BPF_OR | BPF_X): BPF_OP_ALU_REG(reg, ins, |, uint32_t); break; case (BPF_ALU | BPF_LSH | BPF_X): BPF_OP_ALU_REG(reg, ins, <<, uint32_t); break; case (BPF_ALU | BPF_RSH | BPF_X): BPF_OP_ALU_REG(reg, ins, >>, uint32_t); break; case (BPF_ALU | BPF_XOR | BPF_X): BPF_OP_ALU_REG(reg, ins, ^, uint32_t); break; case (BPF_ALU | BPF_MUL | BPF_X): BPF_OP_ALU_REG(reg, ins, *, uint32_t); break; case (BPF_ALU | BPF_DIV | BPF_X): BPF_DIV_ZERO_CHECK(bpf, reg, ins, uint32_t); BPF_OP_ALU_REG(reg, ins, /, uint32_t); break; case (BPF_ALU | BPF_MOD | BPF_X): BPF_DIV_ZERO_CHECK(bpf, reg, ins, uint32_t); BPF_OP_ALU_REG(reg, ins, %, uint32_t); break; case (BPF_ALU | EBPF_MOV | BPF_X): EBPF_MOV_ALU_REG(reg, ins, uint32_t); break; case (BPF_ALU | BPF_NEG): BPF_NEG_ALU(reg, ins, uint32_t); break; case (BPF_ALU | EBPF_END | EBPF_TO_BE): bpf_alu_be(reg, ins); break; case (BPF_ALU | EBPF_END | EBPF_TO_LE): bpf_alu_le(reg, ins); break; /* 64 bit ALU IMM operations */ case (EBPF_ALU64 | BPF_ADD | BPF_K): BPF_OP_ALU_IMM(reg, ins, +, uint64_t); break; case (EBPF_ALU64 | BPF_SUB | BPF_K): BPF_OP_ALU_IMM(reg, ins, -, uint64_t); break; case (EBPF_ALU64 | BPF_AND | BPF_K): BPF_OP_ALU_IMM(reg, ins, &, uint64_t); break; case (EBPF_ALU64 | BPF_OR | BPF_K): BPF_OP_ALU_IMM(reg, ins, |, uint64_t); break; case (EBPF_ALU64 | BPF_LSH | BPF_K): BPF_OP_ALU_IMM(reg, ins, <<, uint64_t); break; case (EBPF_ALU64 | BPF_RSH | BPF_K): BPF_OP_ALU_IMM(reg, ins, >>, uint64_t); break; case (EBPF_ALU64 | EBPF_ARSH | BPF_K): BPF_OP_ALU_IMM(reg, ins, >>, int64_t); break; case (EBPF_ALU64 | BPF_XOR | BPF_K): BPF_OP_ALU_IMM(reg, ins, ^, uint64_t); break; case (EBPF_ALU64 | BPF_MUL | BPF_K): BPF_OP_ALU_IMM(reg, ins, *, uint64_t); break; case (EBPF_ALU64 | BPF_DIV | BPF_K): BPF_OP_ALU_IMM(reg, ins, /, uint64_t); break; case (EBPF_ALU64 | BPF_MOD | BPF_K): BPF_OP_ALU_IMM(reg, ins, %, uint64_t); break; case (EBPF_ALU64 | EBPF_MOV | BPF_K): EBPF_MOV_ALU_IMM(reg, ins, uint64_t); break; /* 64 bit ALU REG operations */ case (EBPF_ALU64 | BPF_ADD | BPF_X): BPF_OP_ALU_REG(reg, ins, +, uint64_t); break; case (EBPF_ALU64 | BPF_SUB | BPF_X): BPF_OP_ALU_REG(reg, ins, -, uint64_t); break; case (EBPF_ALU64 | BPF_AND | BPF_X): BPF_OP_ALU_REG(reg, ins, &, uint64_t); break; case (EBPF_ALU64 | BPF_OR | BPF_X): BPF_OP_ALU_REG(reg, ins, |, uint64_t); break; case (EBPF_ALU64 | BPF_LSH | BPF_X): BPF_OP_ALU_REG(reg, ins, <<, uint64_t); break; case (EBPF_ALU64 | BPF_RSH | BPF_X): BPF_OP_ALU_REG(reg, ins, >>, uint64_t); break; case (EBPF_ALU64 | EBPF_ARSH | BPF_X): BPF_OP_ALU_REG(reg, ins, >>, int64_t); break; case (EBPF_ALU64 | BPF_XOR | BPF_X): BPF_OP_ALU_REG(reg, ins, ^, uint64_t); break; case (EBPF_ALU64 | BPF_MUL | BPF_X): BPF_OP_ALU_REG(reg, ins, *, uint64_t); break; case (EBPF_ALU64 | BPF_DIV | BPF_X): BPF_DIV_ZERO_CHECK(bpf, reg, ins, uint64_t); BPF_OP_ALU_REG(reg, ins, /, uint64_t); break; case (EBPF_ALU64 | BPF_MOD | BPF_X): BPF_DIV_ZERO_CHECK(bpf, reg, ins, uint64_t); BPF_OP_ALU_REG(reg, ins, %, uint64_t); break; case (EBPF_ALU64 | EBPF_MOV | BPF_X): EBPF_MOV_ALU_REG(reg, ins, uint64_t); break; case (EBPF_ALU64 | BPF_NEG): BPF_NEG_ALU(reg, ins, uint64_t); break; /* load instructions */ case (BPF_LDX | BPF_MEM | BPF_B): BPF_LD_REG(reg, ins, uint8_t); break; case (BPF_LDX | BPF_MEM | BPF_H): BPF_LD_REG(reg, ins, uint16_t); break; case (BPF_LDX | BPF_MEM | BPF_W): BPF_LD_REG(reg, ins, uint32_t); break; case (BPF_LDX | BPF_MEM | EBPF_DW): BPF_LD_REG(reg, ins, uint64_t); break; /* load 64 bit immediate value */ case (BPF_LD | BPF_IMM | EBPF_DW): reg[ins->dst_reg] = (uint32_t)ins[0].imm | (uint64_t)(uint32_t)ins[1].imm << 32; ins++; break; /* load absolute instructions */ case (BPF_LD | BPF_ABS | BPF_B): BPF_LD_ABS(bpf, reg, ins, uint8_t, NOP); break; case (BPF_LD | BPF_ABS | BPF_H): BPF_LD_ABS(bpf, reg, ins, uint16_t, rte_be_to_cpu_16); break; case (BPF_LD | BPF_ABS | BPF_W): BPF_LD_ABS(bpf, reg, ins, uint32_t, rte_be_to_cpu_32); break; /* load indirect instructions */ case (BPF_LD | BPF_IND | BPF_B): BPF_LD_IND(bpf, reg, ins, uint8_t, NOP); break; case (BPF_LD | BPF_IND | BPF_H): BPF_LD_IND(bpf, reg, ins, uint16_t, rte_be_to_cpu_16); break; case (BPF_LD | BPF_IND | BPF_W): BPF_LD_IND(bpf, reg, ins, uint32_t, rte_be_to_cpu_32); break; /* store instructions */ case (BPF_STX | BPF_MEM | BPF_B): BPF_ST_REG(reg, ins, uint8_t); break; case (BPF_STX | BPF_MEM | BPF_H): BPF_ST_REG(reg, ins, uint16_t); break; case (BPF_STX | BPF_MEM | BPF_W): BPF_ST_REG(reg, ins, uint32_t); break; case (BPF_STX | BPF_MEM | EBPF_DW): BPF_ST_REG(reg, ins, uint64_t); break; case (BPF_ST | BPF_MEM | BPF_B): BPF_ST_IMM(reg, ins, uint8_t); break; case (BPF_ST | BPF_MEM | BPF_H): BPF_ST_IMM(reg, ins, uint16_t); break; case (BPF_ST | BPF_MEM | BPF_W): BPF_ST_IMM(reg, ins, uint32_t); break; case (BPF_ST | BPF_MEM | EBPF_DW): BPF_ST_IMM(reg, ins, uint64_t); break; /* atomic instructions */ case (BPF_STX | EBPF_ATOMIC | BPF_W): BPF_ST_ATOMIC_REG(reg, ins, 32); break; case (BPF_STX | EBPF_ATOMIC | EBPF_DW): BPF_ST_ATOMIC_REG(reg, ins, 64); break; /* jump instructions */ case (BPF_JMP | BPF_JA): BPF_JMP_UNC(ins); break; /* jump IMM instructions */ case (BPF_JMP | BPF_JEQ | BPF_K): BPF_JMP_CND_IMM(reg, ins, ==, uint64_t); break; case (BPF_JMP | EBPF_JNE | BPF_K): BPF_JMP_CND_IMM(reg, ins, !=, uint64_t); break; case (BPF_JMP | BPF_JGT | BPF_K): BPF_JMP_CND_IMM(reg, ins, >, uint64_t); break; case (BPF_JMP | EBPF_JLT | BPF_K): BPF_JMP_CND_IMM(reg, ins, <, uint64_t); break; case (BPF_JMP | BPF_JGE | BPF_K): BPF_JMP_CND_IMM(reg, ins, >=, uint64_t); break; case (BPF_JMP | EBPF_JLE | BPF_K): BPF_JMP_CND_IMM(reg, ins, <=, uint64_t); break; case (BPF_JMP | EBPF_JSGT | BPF_K): BPF_JMP_CND_IMM(reg, ins, >, int64_t); break; case (BPF_JMP | EBPF_JSLT | BPF_K): BPF_JMP_CND_IMM(reg, ins, <, int64_t); break; case (BPF_JMP | EBPF_JSGE | BPF_K): BPF_JMP_CND_IMM(reg, ins, >=, int64_t); break; case (BPF_JMP | EBPF_JSLE | BPF_K): BPF_JMP_CND_IMM(reg, ins, <=, int64_t); break; case (BPF_JMP | BPF_JSET | BPF_K): BPF_JMP_CND_IMM(reg, ins, &, uint64_t); break; /* jump REG instructions */ case (BPF_JMP | BPF_JEQ | BPF_X): BPF_JMP_CND_REG(reg, ins, ==, uint64_t); break; case (BPF_JMP | EBPF_JNE | BPF_X): BPF_JMP_CND_REG(reg, ins, !=, uint64_t); break; case (BPF_JMP | BPF_JGT | BPF_X): BPF_JMP_CND_REG(reg, ins, >, uint64_t); break; case (BPF_JMP | EBPF_JLT | BPF_X): BPF_JMP_CND_REG(reg, ins, <, uint64_t); break; case (BPF_JMP | BPF_JGE | BPF_X): BPF_JMP_CND_REG(reg, ins, >=, uint64_t); break; case (BPF_JMP | EBPF_JLE | BPF_X): BPF_JMP_CND_REG(reg, ins, <=, uint64_t); break; case (BPF_JMP | EBPF_JSGT | BPF_X): BPF_JMP_CND_REG(reg, ins, >, int64_t); break; case (BPF_JMP | EBPF_JSLT | BPF_X): BPF_JMP_CND_REG(reg, ins, <, int64_t); break; case (BPF_JMP | EBPF_JSGE | BPF_X): BPF_JMP_CND_REG(reg, ins, >=, int64_t); break; case (BPF_JMP | EBPF_JSLE | BPF_X): BPF_JMP_CND_REG(reg, ins, <=, int64_t); break; case (BPF_JMP | BPF_JSET | BPF_X): BPF_JMP_CND_REG(reg, ins, &, uint64_t); break; /* call instructions */ case (BPF_JMP | EBPF_CALL): reg[EBPF_REG_0] = bpf->prm.xsym[ins->imm].func.val( reg[EBPF_REG_1], reg[EBPF_REG_2], reg[EBPF_REG_3], reg[EBPF_REG_4], reg[EBPF_REG_5]); break; /* return instruction */ case (BPF_JMP | EBPF_EXIT): return reg[EBPF_REG_0]; default: RTE_BPF_LOG_LINE(ERR, "%s(%p): invalid opcode %#x at pc: %#zx;", __func__, bpf, ins->code, (uintptr_t)ins - (uintptr_t)bpf->prm.raw.ins); return 0; } } /* should never be reached */ RTE_VERIFY(0); return 0; } RTE_EXPORT_SYMBOL(rte_bpf_exec_burst) uint32_t rte_bpf_exec_burst(const struct rte_bpf *bpf, void *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)]; if (bpf->prm.nb_prog_arg != 1) { /* Use rte_bpf_exec_burst_ex with this program. */ rte_errno = EINVAL; return 0; } for (i = 0; i != num; i++) { reg[EBPF_REG_1] = (uintptr_t)ctx[i]; reg[EBPF_REG_10] = (uintptr_t)(stack + RTE_DIM(stack)); rc[i] = bpf_exec(bpf, reg); } 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++) { switch (bpf->prm.nb_prog_arg) { case 5: reg[EBPF_REG_5] = ctx[i].arg[4].u64; /* FALLTHROUGH */ case 4: reg[EBPF_REG_4] = ctx[i].arg[3].u64; /* FALLTHROUGH */ case 3: reg[EBPF_REG_3] = ctx[i].arg[2].u64; /* FALLTHROUGH */ case 2: reg[EBPF_REG_2] = ctx[i].arg[1].u64; /* FALLTHROUGH */ case 1: reg[EBPF_REG_1] = ctx[i].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 = 0; const struct rte_bpf_jit_ex jit = bpf->jit; /* * Fast path: assumes application pre-validated RTE_BPF_EXEC_FLAG_JIT * and successful JIT generation. No explicit NULL checks here. */ 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; } return i; } RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_exec_burst_ex, 26.07) 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) { rte_errno = EINVAL; return 0; } 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) { uint64_t rc = UINT64_MAX; rte_bpf_exec_burst(bpf, &ctx, &rc, 1); return rc; } RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_bpf_exec_ex, 26.07) uint64_t rte_bpf_exec_ex(const struct rte_bpf *bpf, const struct rte_bpf_prog_ctx *ctx, uint64_t flags) { uint64_t rc = UINT64_MAX; rte_bpf_exec_burst_ex(bpf, ctx, &rc, 1, flags); return rc; }