]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: Print breakdown of insns processed by subprogs
authorPaul Chaignon <paul.chaignon@gmail.com>
Thu, 30 Apr 2026 08:44:28 +0000 (10:44 +0200)
committerKumar Kartikeya Dwivedi <memxor@gmail.com>
Thu, 30 Apr 2026 11:18:44 +0000 (13:18 +0200)
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>
include/linux/bpf_verifier.h
kernel/bpf/verifier.c

index 101ca6cc5424656a1a03c963c20fbe66fb47920b..976e2b2f40e87bd7e9d06f6bc51195ba68a7357e 100644 (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.
         */
index 03f9e16c2abeed289db2929600cf090802dbeda5..11054ad89c14d4c302118c783f4c1ea2dc528944 100644 (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 "