Re: [PATCH bpf-next v6 2/6] bpf: Propagate async callback instructions to scheduling subprograms

Eduard Zingerman <[email protected]> Wed, 05 Aug 2026 11:18:21 -0700
Newsgroups org.kernel.vger.bpf
Message-ID <[email protected]>
On Wed, 2026-08-05 at 03:15 +0200, Kumar Kartikeya Dwivedi wrote:

...

> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index 9de45ade473b..42fa464c520f 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -447,6 +447,8 @@ static_assert(MAX_BPF_STACK / 8 <= (1 << 6));
>  struct bpf_verifier_state {
>  	/* call stack tracking */
>  	struct bpf_func_state *frame[MAX_CALL_FRAMES];
> +	u32 async_stats_subprog_ids[MAX_CALL_FRAMES];
> +	u32 async_stats_subprog_cnt;

One async callback can call another async callback, e.g. see program
'test1' in progs/timer.c. Meaning that this array is not really bound
by MAX_CALL_FRAMES and the code below may overflow it.

Overall, it seems that having an alternative full call stack solely
for the purpose of accounting is an overkill. I'd just show async
subprograms as their own roots, tbh. On the other hand, same
subprogram can be called both as an async and as a regular subprogram :)
so the idea of a separate spine is not w/o it's merit.
If we decide to stick with it, maybe pick a better name?
'verification_call_stack' or something like this?

If we decide to go with this separate call stack, would it be possible
to adapt the code in a way that only this call stack is used for
accounting? (e.g. is filled on regular subprogram calls etc).

Another option is to build an implicit call graph in env while
verifying and count only self instructions and counters on edges.
Then propagate the data over the graph in post-order traversal
(subprog_topo_order is already computed). I think I like this idea
the most.

...

> @@ -2293,6 +2298,12 @@ static struct bpf_verifier_state *push_async_cb(struct bpf_verifier_env *env,
>  			0 /* frameno within this callchain */,
>  			subprog /* subprog number within this prog */);
>  	elem->st.frame[0] = frame;
> +	cnt = env->cur_state->async_stats_subprog_cnt;
> +	memcpy(elem->st.async_stats_subprog_ids, env->cur_state->async_stats_subprog_ids,
> +	       cnt * sizeof(elem->st.async_stats_subprog_ids[0]));
> +	for (i = 0; i <= env->cur_state->curframe; i++)
> +		elem->st.async_stats_subprog_ids[cnt++] = env->cur_state->frame[i]->subprogno;
> +	elem->st.async_stats_subprog_cnt = cnt;
>  	return &elem->st;
>  }

...