@@ -7124,6 +7124,41 @@ static int check_get_func_ip(struct bpf_verifier_env *env)
7124
7124
return - ENOTSUPP ;
7125
7125
}
7126
7126
7127
+ static struct bpf_insn_aux_data * cur_aux (struct bpf_verifier_env * env )
7128
+ {
7129
+ return & env -> insn_aux_data [env -> insn_idx ];
7130
+ }
7131
+
7132
+ static bool loop_flag_is_zero (struct bpf_verifier_env * env )
7133
+ {
7134
+ struct bpf_reg_state * regs = cur_regs (env );
7135
+ struct bpf_reg_state * reg = & regs [BPF_REG_4 ];
7136
+ bool reg_is_null = register_is_null (reg );
7137
+
7138
+ if (reg_is_null )
7139
+ mark_chain_precision (env , BPF_REG_4 );
7140
+
7141
+ return reg_is_null ;
7142
+ }
7143
+
7144
+ static void update_loop_inline_state (struct bpf_verifier_env * env , u32 subprogno )
7145
+ {
7146
+ struct bpf_loop_inline_state * state = & cur_aux (env )-> loop_inline_state ;
7147
+
7148
+ if (!state -> initialized ) {
7149
+ state -> initialized = 1 ;
7150
+ state -> fit_for_inline = loop_flag_is_zero (env );
7151
+ state -> callback_subprogno = subprogno ;
7152
+ return ;
7153
+ }
7154
+
7155
+ if (!state -> fit_for_inline )
7156
+ return ;
7157
+
7158
+ state -> fit_for_inline = (loop_flag_is_zero (env ) &&
7159
+ state -> callback_subprogno == subprogno );
7160
+ }
7161
+
7127
7162
static int check_helper_call (struct bpf_verifier_env * env , struct bpf_insn * insn ,
7128
7163
int * insn_idx_p )
7129
7164
{
@@ -7276,6 +7311,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
7276
7311
err = check_bpf_snprintf_call (env , regs );
7277
7312
break ;
7278
7313
case BPF_FUNC_loop :
7314
+ update_loop_inline_state (env , meta .subprogno );
7279
7315
err = __check_func_call (env , insn , insn_idx_p , meta .subprogno ,
7280
7316
set_loop_callback_state );
7281
7317
break ;
@@ -7682,11 +7718,6 @@ static bool check_reg_sane_offset(struct bpf_verifier_env *env,
7682
7718
return true;
7683
7719
}
7684
7720
7685
- static struct bpf_insn_aux_data * cur_aux (struct bpf_verifier_env * env )
7686
- {
7687
- return & env -> insn_aux_data [env -> insn_idx ];
7688
- }
7689
-
7690
7721
enum {
7691
7722
REASON_BOUNDS = -1 ,
7692
7723
REASON_TYPE = -2 ,
@@ -14315,6 +14346,142 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
14315
14346
return 0 ;
14316
14347
}
14317
14348
14349
+ static struct bpf_prog * inline_bpf_loop (struct bpf_verifier_env * env ,
14350
+ int position ,
14351
+ s32 stack_base ,
14352
+ u32 callback_subprogno ,
14353
+ u32 * cnt )
14354
+ {
14355
+ s32 r6_offset = stack_base + 0 * BPF_REG_SIZE ;
14356
+ s32 r7_offset = stack_base + 1 * BPF_REG_SIZE ;
14357
+ s32 r8_offset = stack_base + 2 * BPF_REG_SIZE ;
14358
+ int reg_loop_max = BPF_REG_6 ;
14359
+ int reg_loop_cnt = BPF_REG_7 ;
14360
+ int reg_loop_ctx = BPF_REG_8 ;
14361
+
14362
+ struct bpf_prog * new_prog ;
14363
+ u32 callback_start ;
14364
+ u32 call_insn_offset ;
14365
+ s32 callback_offset ;
14366
+
14367
+ /* This represents an inlined version of bpf_iter.c:bpf_loop,
14368
+ * be careful to modify this code in sync.
14369
+ */
14370
+ struct bpf_insn insn_buf [] = {
14371
+ /* Return error and jump to the end of the patch if
14372
+ * expected number of iterations is too big.
14373
+ */
14374
+ BPF_JMP_IMM (BPF_JLE , BPF_REG_1 , BPF_MAX_LOOPS , 2 ),
14375
+ BPF_MOV32_IMM (BPF_REG_0 , - E2BIG ),
14376
+ BPF_JMP_IMM (BPF_JA , 0 , 0 , 16 ),
14377
+ /* spill R6, R7, R8 to use these as loop vars */
14378
+ BPF_STX_MEM (BPF_DW , BPF_REG_10 , BPF_REG_6 , r6_offset ),
14379
+ BPF_STX_MEM (BPF_DW , BPF_REG_10 , BPF_REG_7 , r7_offset ),
14380
+ BPF_STX_MEM (BPF_DW , BPF_REG_10 , BPF_REG_8 , r8_offset ),
14381
+ /* initialize loop vars */
14382
+ BPF_MOV64_REG (reg_loop_max , BPF_REG_1 ),
14383
+ BPF_MOV32_IMM (reg_loop_cnt , 0 ),
14384
+ BPF_MOV64_REG (reg_loop_ctx , BPF_REG_3 ),
14385
+ /* loop header,
14386
+ * if reg_loop_cnt >= reg_loop_max skip the loop body
14387
+ */
14388
+ BPF_JMP_REG (BPF_JGE , reg_loop_cnt , reg_loop_max , 5 ),
14389
+ /* callback call,
14390
+ * correct callback offset would be set after patching
14391
+ */
14392
+ BPF_MOV64_REG (BPF_REG_1 , reg_loop_cnt ),
14393
+ BPF_MOV64_REG (BPF_REG_2 , reg_loop_ctx ),
14394
+ BPF_CALL_REL (0 ),
14395
+ /* increment loop counter */
14396
+ BPF_ALU64_IMM (BPF_ADD , reg_loop_cnt , 1 ),
14397
+ /* jump to loop header if callback returned 0 */
14398
+ BPF_JMP_IMM (BPF_JEQ , BPF_REG_0 , 0 , -6 ),
14399
+ /* return value of bpf_loop,
14400
+ * set R0 to the number of iterations
14401
+ */
14402
+ BPF_MOV64_REG (BPF_REG_0 , reg_loop_cnt ),
14403
+ /* restore original values of R6, R7, R8 */
14404
+ BPF_LDX_MEM (BPF_DW , BPF_REG_6 , BPF_REG_10 , r6_offset ),
14405
+ BPF_LDX_MEM (BPF_DW , BPF_REG_7 , BPF_REG_10 , r7_offset ),
14406
+ BPF_LDX_MEM (BPF_DW , BPF_REG_8 , BPF_REG_10 , r8_offset ),
14407
+ };
14408
+
14409
+ * cnt = ARRAY_SIZE (insn_buf );
14410
+ new_prog = bpf_patch_insn_data (env , position , insn_buf , * cnt );
14411
+ if (!new_prog )
14412
+ return new_prog ;
14413
+
14414
+ /* callback start is known only after patching */
14415
+ callback_start = env -> subprog_info [callback_subprogno ].start ;
14416
+ /* Note: insn_buf[12] is an offset of BPF_CALL_REL instruction */
14417
+ call_insn_offset = position + 12 ;
14418
+ callback_offset = callback_start - call_insn_offset - 1 ;
14419
+ env -> prog -> insnsi [call_insn_offset ].imm = callback_offset ;
14420
+
14421
+ return new_prog ;
14422
+ }
14423
+
14424
+ static bool is_bpf_loop_call (struct bpf_insn * insn )
14425
+ {
14426
+ return insn -> code == (BPF_JMP | BPF_CALL ) &&
14427
+ insn -> src_reg == 0 &&
14428
+ insn -> imm == BPF_FUNC_loop ;
14429
+ }
14430
+
14431
+ /* For all sub-programs in the program (including main) check
14432
+ * insn_aux_data to see if there are bpf_loop calls that require
14433
+ * inlining. If such calls are found the calls are replaced with a
14434
+ * sequence of instructions produced by `inline_bpf_loop` function and
14435
+ * subprog stack_depth is increased by the size of 3 registers.
14436
+ * This stack space is used to spill values of the R6, R7, R8. These
14437
+ * registers are used to store the loop bound, counter and context
14438
+ * variables.
14439
+ */
14440
+ static int optimize_bpf_loop (struct bpf_verifier_env * env )
14441
+ {
14442
+ struct bpf_subprog_info * subprogs = env -> subprog_info ;
14443
+ int i , cur_subprog = 0 , cnt , delta = 0 ;
14444
+ struct bpf_insn * insn = env -> prog -> insnsi ;
14445
+ int insn_cnt = env -> prog -> len ;
14446
+ u16 stack_depth = subprogs [cur_subprog ].stack_depth ;
14447
+ u16 stack_depth_roundup = round_up (stack_depth , 8 ) - stack_depth ;
14448
+ u16 stack_depth_extra = 0 ;
14449
+
14450
+ for (i = 0 ; i < insn_cnt ; i ++ , insn ++ ) {
14451
+ struct bpf_loop_inline_state * inline_state =
14452
+ & env -> insn_aux_data [i + delta ].loop_inline_state ;
14453
+
14454
+ if (is_bpf_loop_call (insn ) && inline_state -> fit_for_inline ) {
14455
+ struct bpf_prog * new_prog ;
14456
+
14457
+ stack_depth_extra = BPF_REG_SIZE * 3 + stack_depth_roundup ;
14458
+ new_prog = inline_bpf_loop (env ,
14459
+ i + delta ,
14460
+ - (stack_depth + stack_depth_extra ),
14461
+ inline_state -> callback_subprogno ,
14462
+ & cnt );
14463
+ if (!new_prog )
14464
+ return - ENOMEM ;
14465
+
14466
+ delta += cnt - 1 ;
14467
+ env -> prog = new_prog ;
14468
+ insn = new_prog -> insnsi + i + delta ;
14469
+ }
14470
+
14471
+ if (subprogs [cur_subprog + 1 ].start == i + delta + 1 ) {
14472
+ subprogs [cur_subprog ].stack_depth += stack_depth_extra ;
14473
+ cur_subprog ++ ;
14474
+ stack_depth = subprogs [cur_subprog ].stack_depth ;
14475
+ stack_depth_roundup = round_up (stack_depth , 8 ) - stack_depth ;
14476
+ stack_depth_extra = 0 ;
14477
+ }
14478
+ }
14479
+
14480
+ env -> prog -> aux -> stack_depth = env -> subprog_info [0 ].stack_depth ;
14481
+
14482
+ return 0 ;
14483
+ }
14484
+
14318
14485
static void free_states (struct bpf_verifier_env * env )
14319
14486
{
14320
14487
struct bpf_verifier_state_list * sl , * sln ;
@@ -15052,6 +15219,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr)
15052
15219
ret = check_max_stack_depth (env );
15053
15220
15054
15221
/* instruction rewrites happen after this point */
15222
+ if (ret == 0 )
15223
+ ret = optimize_bpf_loop (env );
15224
+
15055
15225
if (is_priv ) {
15056
15226
if (ret == 0 )
15057
15227
opt_hard_wire_dead_code_branches (env );
0 commit comments