bpf: Print breakdown of insns processed by subprogs

When using global functions (i.e. subprogs), the verifier performs
function-by-function verification. In that case, the sum of the
instructions processed in each global function and in the main program
counts towards the 1 million instructions limit. Only that sum is
reported in the verifier logs.

While starting to use global functions in Cilium (finally!), we found it
can be useful to have the breakdown per global function, to understand
exactly where the budget is currently spent. This patch implements this
breakdown, under BPF_LOG_STATS, as done for the stack depths.

When iterating over subprogs, we need to skip the hidden subprogs at the
end because they don't have a corresponding func_info_aux entry and
calling bpf_subprog_is_global() would result in an OOB access.

Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
Link: https://lore.kernel.org/bpf/5590f9c67e614ec9054d0c7e74e87cc690a52c56.1777538384.git.paul.chaignon@gmail.com
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Paul Chaignon
2026-04-30 13:18:44 +02:00
parent 9f5b3ffc3f
commit f603e84ab7
2 changed files with 17 additions and 9 deletions
+1
View File
@@ -779,6 +779,7 @@ struct bpf_subprog_info {
u32 exit_idx; /* Index of one of the BPF_EXIT instructions in this subprogram */
u16 stack_depth; /* max. stack depth used by this function */
u16 stack_extra;
u32 insn_processed;
/* offsets in range [stack_depth .. fastcall_stack_off)
* are used for bpf_fastcall spills and fills.
*/
+16 -9
View File
@@ -18215,6 +18215,7 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
struct bpf_prog_aux *aux = env->prog->aux;
struct bpf_func_info_aux *sub_aux;
int i, ret, new_cnt;
u32 insn_processed;
if (!aux->func_info)
return 0;
@@ -18229,6 +18230,8 @@ again:
if (!bpf_subprog_is_global(env, i))
continue;
insn_processed = env->insn_processed;
sub_aux = subprog_aux(env, i);
if (!sub_aux->called || sub_aux->verified)
continue;
@@ -18236,6 +18239,7 @@ again:
env->insn_idx = env->subprog_info[i].start;
WARN_ON_ONCE(env->insn_idx == 0);
ret = do_check_common(env, i);
env->subprog_info[i].insn_processed = env->insn_processed - insn_processed;
if (ret) {
return ret;
} else if (env->log.level & BPF_LOG_LEVEL) {
@@ -18262,10 +18266,12 @@ again:
static int do_check_main(struct bpf_verifier_env *env)
{
u32 insn_processed = env->insn_processed;
int ret;
env->insn_idx = 0;
ret = do_check_common(env, 0);
env->subprog_info[0].insn_processed = env->insn_processed - insn_processed;
if (!ret)
env->prog->aux->stack_depth = env->subprog_info[0].stack_depth;
return ret;
@@ -18274,19 +18280,20 @@ static int do_check_main(struct bpf_verifier_env *env)
static void print_verification_stats(struct bpf_verifier_env *env)
{
int i;
/* Skip over hidden subprogs which are not verified. */
int i, subprog_cnt = env->subprog_cnt - env->hidden_subprog_cnt;
if (env->log.level & BPF_LOG_STATS) {
verbose(env, "verification time %lld usec\n",
div_u64(env->verification_time, 1000));
verbose(env, "stack depth ");
for (i = 0; i < env->subprog_cnt; i++) {
u32 depth = env->subprog_info[i].stack_depth;
verbose(env, "%d", depth);
if (i + 1 < env->subprog_cnt)
verbose(env, "+");
}
verbose(env, "stack depth %d", env->subprog_info[0].stack_depth);
for (i = 1; i < subprog_cnt; i++)
verbose(env, "+%d", env->subprog_info[i].stack_depth);
verbose(env, "\n");
verbose(env, "insns processed %d", env->subprog_info[0].insn_processed);
for (i = 1; i < subprog_cnt; i++)
if (bpf_subprog_is_global(env, i))
verbose(env, "+%d", env->subprog_info[i].insn_processed);
verbose(env, "\n");
}
verbose(env, "processed %d insns (limit %d) max_states_per_insn %d "