Skip to content

Commit dc2a4eb

Browse files
Alexei Starovoitovborkmann
authored andcommitted
bpf: convert explored_states to hash table
All prune points inside a callee bpf function most likely will have different callsites. For example, if function foo() is called from two callsites the half of explored states in all prune points in foo() will be useless for subsequent walking of one of those callsites. Fortunately explored_states pruning heuristics keeps the number of states per prune point small, but walking these states is still a waste of cpu time when the callsite of the current state is different from the callsite of the explored state. To improve pruning logic convert explored_states into hash table and use simple insn_idx ^ callsite hash to select hash bucket. This optimization has no effect on programs without bpf2bpf calls and drastically improves programs with calls. In the later case it reduces total memory consumption in 1M scale tests by almost 3 times (peak_states drops from 5752 to 2016). Care should be taken when comparing the states for equivalency. Since the same hash bucket can now contain states with different indices the insn_idx has to be part of verifier_state and compared. Different hash table sizes and different hash functions were explored, but the results were not significantly better vs this patch. They can be improved in the future. Hit/miss heuristic is not counting index miscompare as a miss. Otherwise verifier stats become unstable when experimenting with different hash functions. Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent a8f500a commit dc2a4eb

File tree

2 files changed

+19
-5
lines changed

2 files changed

+19
-5
lines changed

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ struct bpf_func_state {
187187
struct bpf_verifier_state {
188188
/* call stack tracking */
189189
struct bpf_func_state *frame[MAX_CALL_FRAMES];
190+
u32 insn_idx;
190191
u32 curframe;
191192
u32 active_spin_lock;
192193
bool speculative;

kernel/bpf/verifier.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5436,11 +5436,19 @@ enum {
54365436
BRANCH = 2,
54375437
};
54385438

5439+
static u32 state_htab_size(struct bpf_verifier_env *env)
5440+
{
5441+
return env->prog->len;
5442+
}
5443+
54395444
static struct bpf_verifier_state_list **explored_state(
54405445
struct bpf_verifier_env *env,
54415446
int idx)
54425447
{
5443-
return &env->explored_states[idx];
5448+
struct bpf_verifier_state *cur = env->cur_state;
5449+
struct bpf_func_state *state = cur->frame[cur->curframe];
5450+
5451+
return &env->explored_states[(idx ^ state->callsite) % state_htab_size(env)];
54445452
}
54455453

54465454
static void init_explored_state(struct bpf_verifier_env *env, int idx)
@@ -6018,7 +6026,8 @@ static void clean_live_states(struct bpf_verifier_env *env, int insn,
60186026

60196027
sl = *explored_state(env, insn);
60206028
while (sl) {
6021-
if (sl->state.curframe != cur->curframe)
6029+
if (sl->state.insn_idx != insn ||
6030+
sl->state.curframe != cur->curframe)
60226031
goto next;
60236032
for (i = 0; i <= cur->curframe; i++)
60246033
if (sl->state.frame[i]->callsite != cur->frame[i]->callsite)
@@ -6384,6 +6393,9 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
63846393
clean_live_states(env, insn_idx, cur);
63856394

63866395
while (sl) {
6396+
states_cnt++;
6397+
if (sl->state.insn_idx != insn_idx)
6398+
goto next;
63876399
if (states_equal(env, &sl->state, cur)) {
63886400
sl->hit_cnt++;
63896401
/* reached equivalent register/stack state,
@@ -6401,7 +6413,6 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
64016413
return err;
64026414
return 1;
64036415
}
6404-
states_cnt++;
64056416
sl->miss_cnt++;
64066417
/* heuristic to determine whether this state is beneficial
64076418
* to keep checking from state equivalence point of view.
@@ -6428,6 +6439,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
64286439
sl = *pprev;
64296440
continue;
64306441
}
6442+
next:
64316443
pprev = &sl->next;
64326444
sl = *pprev;
64336445
}
@@ -6459,6 +6471,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
64596471
kfree(new_sl);
64606472
return err;
64616473
}
6474+
new->insn_idx = insn_idx;
64626475
new_sl->next = *explored_state(env, insn_idx);
64636476
*explored_state(env, insn_idx) = new_sl;
64646477
/* connect new state to parentage chain. Current frame needs all
@@ -8138,7 +8151,7 @@ static void free_states(struct bpf_verifier_env *env)
81388151
if (!env->explored_states)
81398152
return;
81408153

8141-
for (i = 0; i < env->prog->len; i++) {
8154+
for (i = 0; i < state_htab_size(env); i++) {
81428155
sl = env->explored_states[i];
81438156

81448157
while (sl) {
@@ -8246,7 +8259,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
82468259
goto skip_full_check;
82478260
}
82488261

8249-
env->explored_states = kvcalloc(env->prog->len,
8262+
env->explored_states = kvcalloc(state_htab_size(env),
82508263
sizeof(struct bpf_verifier_state_list *),
82518264
GFP_USER);
82528265
ret = -ENOMEM;

0 commit comments

Comments
 (0)