Skip to content

Commit a2046de

Browse files
committed
Merge branch 'bpf-to-bpf-calls-nfp'
Quentin Monnet says: ==================== This patch series adds support for hardware offload of programs containing BPF-to-BPF function calls. First, a new callback is added to the kernel verifier, to collect information after the main part of the verification has been performed. Then support for BPF-to-BPF calls is incrementally added to the nfp driver, before offloading programs containing such calls is eventually allowed by lifting the restriction in the kernel verifier, in the last patch. Please refer to individual patches for details. Many thanks to Jiong and Jakub for their precious help and contribution on the main patches for the JIT-compiler, and everything related to stack accesses. ==================== Signed-off-by: Daniel Borkmann <[email protected]>
2 parents 31ce8c4 + e4052d0 commit a2046de

File tree

10 files changed

+589
-46
lines changed

10 files changed

+589
-46
lines changed

drivers/net/ethernet/netronome/nfp/bpf/jit.c

Lines changed: 360 additions & 21 deletions
Large diffs are not rendered by default.

drivers/net/ethernet/netronome/nfp/bpf/main.h

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ enum nfp_relo_type {
6161
/* internal jumps to parts of the outro */
6262
RELO_BR_GO_OUT,
6363
RELO_BR_GO_ABORT,
64+
RELO_BR_GO_CALL_PUSH_REGS,
65+
RELO_BR_GO_CALL_POP_REGS,
6466
/* external jumps to fixed addresses */
6567
RELO_BR_NEXT_PKT,
6668
RELO_BR_HELPER,
@@ -104,6 +106,7 @@ enum pkt_vec {
104106
#define imma_a(np) reg_a(STATIC_REG_IMMA)
105107
#define imma_b(np) reg_b(STATIC_REG_IMMA)
106108
#define imm_both(np) reg_both(STATIC_REG_IMM)
109+
#define ret_reg(np) imm_a(np)
107110

108111
#define NFP_BPF_ABI_FLAGS reg_imm(0)
109112
#define NFP_BPF_ABI_FLAG_MARK 1
@@ -262,7 +265,9 @@ struct nfp_bpf_reg_state {
262265
bool var_off;
263266
};
264267

265-
#define FLAG_INSN_IS_JUMP_DST BIT(0)
268+
#define FLAG_INSN_IS_JUMP_DST BIT(0)
269+
#define FLAG_INSN_IS_SUBPROG_START BIT(1)
270+
#define FLAG_INSN_PTR_CALLER_STACK_FRAME BIT(2)
266271

267272
/**
268273
* struct nfp_insn_meta - BPF instruction wrapper
@@ -279,6 +284,7 @@ struct nfp_bpf_reg_state {
279284
* @xadd_maybe_16bit: 16bit immediate is possible
280285
* @jmp_dst: destination info for jump instructions
281286
* @jump_neg_op: jump instruction has inverted immediate, use ADD instead of SUB
287+
* @num_insns_after_br: number of insns following a branch jump, used for fixup
282288
* @func_id: function id for call instructions
283289
* @arg1: arg1 for call instructions
284290
* @arg2: arg2 for call instructions
@@ -289,6 +295,7 @@ struct nfp_bpf_reg_state {
289295
* @off: index of first generated machine instruction (in nfp_prog.prog)
290296
* @n: eBPF instruction number
291297
* @flags: eBPF instruction extra optimization flags
298+
* @subprog_idx: index of subprogram to which the instruction belongs
292299
* @skip: skip this instruction (optimized out)
293300
* @double_cb: callback for second part of the instruction
294301
* @l: link on nfp_prog->insns list
@@ -314,6 +321,7 @@ struct nfp_insn_meta {
314321
struct {
315322
struct nfp_insn_meta *jmp_dst;
316323
bool jump_neg_op;
324+
u32 num_insns_after_br; /* only for BPF-to-BPF calls */
317325
};
318326
/* function calls */
319327
struct {
@@ -335,6 +343,7 @@ struct nfp_insn_meta {
335343
unsigned int off;
336344
unsigned short n;
337345
unsigned short flags;
346+
unsigned short subprog_idx;
338347
bool skip;
339348
instr_cb_t double_cb;
340349

@@ -423,6 +432,34 @@ static inline bool is_mbpf_div(const struct nfp_insn_meta *meta)
423432
return is_mbpf_alu(meta) && mbpf_op(meta) == BPF_DIV;
424433
}
425434

435+
static inline bool is_mbpf_helper_call(const struct nfp_insn_meta *meta)
436+
{
437+
struct bpf_insn insn = meta->insn;
438+
439+
return insn.code == (BPF_JMP | BPF_CALL) &&
440+
insn.src_reg != BPF_PSEUDO_CALL;
441+
}
442+
443+
static inline bool is_mbpf_pseudo_call(const struct nfp_insn_meta *meta)
444+
{
445+
struct bpf_insn insn = meta->insn;
446+
447+
return insn.code == (BPF_JMP | BPF_CALL) &&
448+
insn.src_reg == BPF_PSEUDO_CALL;
449+
}
450+
451+
#define STACK_FRAME_ALIGN 64
452+
453+
/**
454+
* struct nfp_bpf_subprog_info - nfp BPF sub-program (a.k.a. function) info
455+
* @stack_depth: maximum stack depth used by this sub-program
456+
* @needs_reg_push: whether sub-program uses callee-saved registers
457+
*/
458+
struct nfp_bpf_subprog_info {
459+
u16 stack_depth;
460+
u8 needs_reg_push : 1;
461+
};
462+
426463
/**
427464
* struct nfp_prog - nfp BPF program
428465
* @bpf: backpointer to the bpf app priv structure
@@ -434,12 +471,16 @@ static inline bool is_mbpf_div(const struct nfp_insn_meta *meta)
434471
* @last_bpf_off: address of the last instruction translated from BPF
435472
* @tgt_out: jump target for normal exit
436473
* @tgt_abort: jump target for abort (e.g. access outside of packet buffer)
474+
* @tgt_call_push_regs: jump target for subroutine for saving R6~R9 to stack
475+
* @tgt_call_pop_regs: jump target for subroutine used for restoring R6~R9
437476
* @n_translated: number of successfully translated instructions (for errors)
438477
* @error: error code if something went wrong
439-
* @stack_depth: max stack depth from the verifier
478+
* @stack_frame_depth: max stack depth for current frame
440479
* @adjust_head_location: if program has single adjust head call - the insn no.
441480
* @map_records_cnt: the number of map pointers recorded for this prog
481+
* @subprog_cnt: number of sub-programs, including main function
442482
* @map_records: the map record pointers from bpf->maps_neutral
483+
* @subprog: pointer to an array of objects holding info about sub-programs
443484
* @insns: list of BPF instruction wrappers (struct nfp_insn_meta)
444485
*/
445486
struct nfp_prog {
@@ -456,15 +497,19 @@ struct nfp_prog {
456497
unsigned int last_bpf_off;
457498
unsigned int tgt_out;
458499
unsigned int tgt_abort;
500+
unsigned int tgt_call_push_regs;
501+
unsigned int tgt_call_pop_regs;
459502

460503
unsigned int n_translated;
461504
int error;
462505

463-
unsigned int stack_depth;
506+
unsigned int stack_frame_depth;
464507
unsigned int adjust_head_location;
465508

466509
unsigned int map_records_cnt;
510+
unsigned int subprog_cnt;
467511
struct nfp_bpf_neutral_map **map_records;
512+
struct nfp_bpf_subprog_info *subprog;
468513

469514
struct list_head insns;
470515
};
@@ -481,6 +526,7 @@ struct nfp_bpf_vnic {
481526
unsigned int tgt_done;
482527
};
483528

529+
bool nfp_is_subprog_start(struct nfp_insn_meta *meta);
484530
void nfp_bpf_jit_prepare(struct nfp_prog *nfp_prog, unsigned int cnt);
485531
int nfp_bpf_jit(struct nfp_prog *prog);
486532
bool nfp_bpf_supported_opcode(u8 code);

drivers/net/ethernet/netronome/nfp/bpf/offload.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ static void nfp_prog_free(struct nfp_prog *nfp_prog)
208208
{
209209
struct nfp_insn_meta *meta, *tmp;
210210

211+
kfree(nfp_prog->subprog);
212+
211213
list_for_each_entry_safe(meta, tmp, &nfp_prog->insns, l) {
212214
list_del(&meta->l);
213215
kfree(meta);
@@ -250,18 +252,9 @@ nfp_bpf_verifier_prep(struct nfp_app *app, struct nfp_net *nn,
250252
static int nfp_bpf_translate(struct nfp_net *nn, struct bpf_prog *prog)
251253
{
252254
struct nfp_prog *nfp_prog = prog->aux->offload->dev_priv;
253-
unsigned int stack_size;
254255
unsigned int max_instr;
255256
int err;
256257

257-
stack_size = nn_readb(nn, NFP_NET_CFG_BPF_STACK_SZ) * 64;
258-
if (prog->aux->stack_depth > stack_size) {
259-
nn_info(nn, "stack too large: program %dB > FW stack %dB\n",
260-
prog->aux->stack_depth, stack_size);
261-
return -EOPNOTSUPP;
262-
}
263-
nfp_prog->stack_depth = round_up(prog->aux->stack_depth, 4);
264-
265258
max_instr = nn_readw(nn, NFP_NET_CFG_BPF_MAX_LEN);
266259
nfp_prog->__prog_alloc_len = max_instr * sizeof(u64);
267260

drivers/net/ethernet/netronome/nfp/bpf/verifier.c

Lines changed: 136 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@
3434
#include <linux/bpf.h>
3535
#include <linux/bpf_verifier.h>
3636
#include <linux/kernel.h>
37+
#include <linux/netdevice.h>
3738
#include <linux/pkt_cls.h>
3839

3940
#include "../nfp_app.h"
4041
#include "../nfp_main.h"
42+
#include "../nfp_net.h"
4143
#include "fw.h"
4244
#include "main.h"
4345

@@ -155,8 +157,9 @@ nfp_bpf_map_call_ok(const char *fname, struct bpf_verifier_env *env,
155157
}
156158

157159
static int
158-
nfp_bpf_check_call(struct nfp_prog *nfp_prog, struct bpf_verifier_env *env,
159-
struct nfp_insn_meta *meta)
160+
nfp_bpf_check_helper_call(struct nfp_prog *nfp_prog,
161+
struct bpf_verifier_env *env,
162+
struct nfp_insn_meta *meta)
160163
{
161164
const struct bpf_reg_state *reg1 = cur_regs(env) + BPF_REG_1;
162165
const struct bpf_reg_state *reg2 = cur_regs(env) + BPF_REG_2;
@@ -333,6 +336,9 @@ nfp_bpf_check_stack_access(struct nfp_prog *nfp_prog,
333336
{
334337
s32 old_off, new_off;
335338

339+
if (reg->frameno != env->cur_state->curframe)
340+
meta->flags |= FLAG_INSN_PTR_CALLER_STACK_FRAME;
341+
336342
if (!tnum_is_const(reg->var_off)) {
337343
pr_vlog(env, "variable ptr stack access\n");
338344
return -EINVAL;
@@ -620,8 +626,8 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
620626
return -EINVAL;
621627
}
622628

623-
if (meta->insn.code == (BPF_JMP | BPF_CALL))
624-
return nfp_bpf_check_call(nfp_prog, env, meta);
629+
if (is_mbpf_helper_call(meta))
630+
return nfp_bpf_check_helper_call(nfp_prog, env, meta);
625631
if (meta->insn.code == (BPF_JMP | BPF_EXIT))
626632
return nfp_bpf_check_exit(nfp_prog, env);
627633

@@ -640,6 +646,131 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
640646
return 0;
641647
}
642648

649+
static int
650+
nfp_assign_subprog_idx_and_regs(struct bpf_verifier_env *env,
651+
struct nfp_prog *nfp_prog)
652+
{
653+
struct nfp_insn_meta *meta;
654+
int index = 0;
655+
656+
list_for_each_entry(meta, &nfp_prog->insns, l) {
657+
if (nfp_is_subprog_start(meta))
658+
index++;
659+
meta->subprog_idx = index;
660+
661+
if (meta->insn.dst_reg >= BPF_REG_6 &&
662+
meta->insn.dst_reg <= BPF_REG_9)
663+
nfp_prog->subprog[index].needs_reg_push = 1;
664+
}
665+
666+
if (index + 1 != nfp_prog->subprog_cnt) {
667+
pr_vlog(env, "BUG: number of processed BPF functions is not consistent (processed %d, expected %d)\n",
668+
index + 1, nfp_prog->subprog_cnt);
669+
return -EFAULT;
670+
}
671+
672+
return 0;
673+
}
674+
675+
static unsigned int
676+
nfp_bpf_get_stack_usage(struct nfp_prog *nfp_prog, unsigned int cnt)
677+
{
678+
struct nfp_insn_meta *meta = nfp_prog_first_meta(nfp_prog);
679+
unsigned int max_depth = 0, depth = 0, frame = 0;
680+
struct nfp_insn_meta *ret_insn[MAX_CALL_FRAMES];
681+
unsigned short frame_depths[MAX_CALL_FRAMES];
682+
unsigned short ret_prog[MAX_CALL_FRAMES];
683+
unsigned short idx = meta->subprog_idx;
684+
685+
/* Inspired from check_max_stack_depth() from kernel verifier.
686+
* Starting from main subprogram, walk all instructions and recursively
687+
* walk all callees that given subprogram can call. Since recursion is
688+
* prevented by the kernel verifier, this algorithm only needs a local
689+
* stack of MAX_CALL_FRAMES to remember callsites.
690+
*/
691+
process_subprog:
692+
frame_depths[frame] = nfp_prog->subprog[idx].stack_depth;
693+
frame_depths[frame] = round_up(frame_depths[frame], STACK_FRAME_ALIGN);
694+
depth += frame_depths[frame];
695+
max_depth = max(max_depth, depth);
696+
697+
continue_subprog:
698+
for (; meta != nfp_prog_last_meta(nfp_prog) && meta->subprog_idx == idx;
699+
meta = nfp_meta_next(meta)) {
700+
if (!is_mbpf_pseudo_call(meta))
701+
continue;
702+
703+
/* We found a call to a subprogram. Remember instruction to
704+
* return to and subprog id.
705+
*/
706+
ret_insn[frame] = nfp_meta_next(meta);
707+
ret_prog[frame] = idx;
708+
709+
/* Find the callee and start processing it. */
710+
meta = nfp_bpf_goto_meta(nfp_prog, meta,
711+
meta->n + 1 + meta->insn.imm, cnt);
712+
idx = meta->subprog_idx;
713+
frame++;
714+
goto process_subprog;
715+
}
716+
/* End of for() loop means the last instruction of the subprog was
717+
* reached. If we popped all stack frames, return; otherwise, go on
718+
* processing remaining instructions from the caller.
719+
*/
720+
if (frame == 0)
721+
return max_depth;
722+
723+
depth -= frame_depths[frame];
724+
frame--;
725+
meta = ret_insn[frame];
726+
idx = ret_prog[frame];
727+
goto continue_subprog;
728+
}
729+
730+
static int nfp_bpf_finalize(struct bpf_verifier_env *env)
731+
{
732+
unsigned int stack_size, stack_needed;
733+
struct bpf_subprog_info *info;
734+
struct nfp_prog *nfp_prog;
735+
struct nfp_net *nn;
736+
int i;
737+
738+
nfp_prog = env->prog->aux->offload->dev_priv;
739+
nfp_prog->subprog_cnt = env->subprog_cnt;
740+
nfp_prog->subprog = kcalloc(nfp_prog->subprog_cnt,
741+
sizeof(nfp_prog->subprog[0]), GFP_KERNEL);
742+
if (!nfp_prog->subprog)
743+
return -ENOMEM;
744+
745+
nfp_assign_subprog_idx_and_regs(env, nfp_prog);
746+
747+
info = env->subprog_info;
748+
for (i = 0; i < nfp_prog->subprog_cnt; i++) {
749+
nfp_prog->subprog[i].stack_depth = info[i].stack_depth;
750+
751+
if (i == 0)
752+
continue;
753+
754+
/* Account for size of return address. */
755+
nfp_prog->subprog[i].stack_depth += REG_WIDTH;
756+
/* Account for size of saved registers, if necessary. */
757+
if (nfp_prog->subprog[i].needs_reg_push)
758+
nfp_prog->subprog[i].stack_depth += BPF_REG_SIZE * 4;
759+
}
760+
761+
nn = netdev_priv(env->prog->aux->offload->netdev);
762+
stack_size = nn_readb(nn, NFP_NET_CFG_BPF_STACK_SZ) * 64;
763+
stack_needed = nfp_bpf_get_stack_usage(nfp_prog, env->prog->len);
764+
if (stack_needed > stack_size) {
765+
pr_vlog(env, "stack too large: program %dB > FW stack %dB\n",
766+
stack_needed, stack_size);
767+
return -EOPNOTSUPP;
768+
}
769+
770+
return 0;
771+
}
772+
643773
const struct bpf_prog_offload_ops nfp_bpf_analyzer_ops = {
644-
.insn_hook = nfp_verify_insn,
774+
.insn_hook = nfp_verify_insn,
775+
.finalize = nfp_bpf_finalize,
645776
};

drivers/net/ethernet/netronome/nfp/nfp_asm.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@
8282
#define OP_BR_BIT_ADDR_LO OP_BR_ADDR_LO
8383
#define OP_BR_BIT_ADDR_HI OP_BR_ADDR_HI
8484

85+
#define OP_BR_ALU_BASE 0x0e800000000ULL
86+
#define OP_BR_ALU_BASE_MASK 0x0ff80000000ULL
87+
#define OP_BR_ALU_A_SRC 0x000000003ffULL
88+
#define OP_BR_ALU_B_SRC 0x000000ffc00ULL
89+
#define OP_BR_ALU_DEFBR 0x00000300000ULL
90+
#define OP_BR_ALU_IMM_HI 0x0007fc00000ULL
91+
#define OP_BR_ALU_SRC_LMEXTN 0x40000000000ULL
92+
#define OP_BR_ALU_DST_LMEXTN 0x80000000000ULL
93+
8594
static inline bool nfp_is_br(u64 insn)
8695
{
8796
return (insn & OP_BR_BASE_MASK) == OP_BR_BASE ||

drivers/net/netdevsim/bpf.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,14 @@ nsim_bpf_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn)
8686
return 0;
8787
}
8888

89+
static int nsim_bpf_finalize(struct bpf_verifier_env *env)
90+
{
91+
return 0;
92+
}
93+
8994
static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = {
90-
.insn_hook = nsim_bpf_verify_insn,
95+
.insn_hook = nsim_bpf_verify_insn,
96+
.finalize = nsim_bpf_finalize,
9197
};
9298

9399
static bool nsim_xdp_offload_active(struct netdevsim *ns)

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ struct bpf_verifier_ops {
263263
struct bpf_prog_offload_ops {
264264
int (*insn_hook)(struct bpf_verifier_env *env,
265265
int insn_idx, int prev_insn_idx);
266+
int (*finalize)(struct bpf_verifier_env *env);
266267
};
267268

268269
struct bpf_prog_offload {

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,5 +245,6 @@ static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env)
245245
int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env);
246246
int bpf_prog_offload_verify_insn(struct bpf_verifier_env *env,
247247
int insn_idx, int prev_insn_idx);
248+
int bpf_prog_offload_finalize(struct bpf_verifier_env *env);
248249

249250
#endif /* _LINUX_BPF_VERIFIER_H */

0 commit comments

Comments
 (0)