Re: [PATCH v4 2/6] powerpc64/bpf: Support tailcalls with subprogs

Hari Bathini
2026-01-23T12:49:39Z

On 23/01/26 2:48 am, adubey@linux.ibm.com wrote:
> From: Abhishek Dubey <adubey@linux.ibm.com>
>
> Enabling tailcalls with subprog combinations by referencing
> method. The actual tailcall count is always maintained in the
> tail_call_info variable present in the frame of main function
> (also called entry function). The tail_call_info field in the
> stack frame of subprogs contains reference to the tail_call_info
> field in the stack frame of main BPF program.

Describe the changes in imperative mood..

Instead of:

"Enabling tailcalls with subprog combinations by referencing
method. The actual tailcall count is always maintained in the
tail_call_info variable present in the frame of main function
(also called entry function). The tail_call_info field in the
stack frame of subprogs contains reference to the tail_call_info
field in the stack frame of main BPF program."

use:

"Enable tailcalls support in subprogs by passing tail call count as
reference instead of value. The actual tailcall count is always
maintained in the tailcall field present in the frame of main
function (also called entry function). The tailcall field in the
stack frame of subprogs contains reference to the tailcall field
in the stack frame of main BPF program. Accordingly, rename
tail_call_cnt field in the stack layout to tail_call_info."

>
> Dynamic resolution interprets the tail_call_info either as
> value or reference depending on the context of active frame
> while tailcall is invoked.
>
> Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
> ---
> arch/powerpc/net/bpf_jit.h | 13 ++++++
> arch/powerpc/net/bpf_jit_comp.c | 59 +++++++++++++++++++++++----
> arch/powerpc/net/bpf_jit_comp64.c | 68 +++++++++++++++++++++++--------
> 3 files changed, 117 insertions(+), 23 deletions(-)
>
> diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h
> index 9f6ec00bd02e..56f56fdd4969 100644
> --- a/arch/powerpc/net/bpf_jit.h
> +++ b/arch/powerpc/net/bpf_jit.h
> @@ -52,6 +52,13 @@
> EMIT(PPC_INST_BRANCH_COND | (((cond) & 0x3ff) << 16) | (offset & 0xfffc)); \
> } while (0)
>
> +/* When constant jump offset is known prior */
> +#define PPC_BCC_CONST_SHORT(cond, offset) \
> + do { \
> + BUILD_BUG_ON(offset < -0x8000 || offset > 0x7fff || (offset & 0x3)); \
> + EMIT(PPC_INST_BRANCH_COND | (((cond) & 0x3ff) << 16) | (offset & 0xfffc)); \
> + } while (0)
> +
> /*
> * Sign-extended 32-bit immediate load
> *
> @@ -73,6 +80,10 @@
> } } while (0)
>
> #ifdef CONFIG_PPC64
> +
> +/* for gpr non volatile registers BPG_REG_6 to 10 */
> +#define BPF_PPC_STACK_SAVE (6 * 8)
> +
> /* If dummy pass (!image), account for maximum possible instructions */
> #define PPC_LI64(d, i) do { \
> if (!image) \
> @@ -167,6 +178,7 @@ struct codegen_context {
> unsigned int alt_exit_addr;
> u64 arena_vm_start;
> u64 user_vm_start;
> + bool is_subprog;
> };
>
> #define bpf_to_ppc(r) (ctx->b2p[r])
> @@ -206,6 +218,7 @@ int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass
> struct codegen_context *ctx, int insn_idx,
> int jmp_off, int dst_reg, u32 code);
>
> +int bpf_jit_stack_tailcallinfo_offset(struct codegen_context *ctx);
> #endif
>
> #endif
> diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
> index d51c696221d7..93355ba5382a 100644
> --- a/arch/powerpc/net/bpf_jit_comp.c
> +++ b/arch/powerpc/net/bpf_jit_comp.c
> @@ -206,6 +206,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
> cgctx.stack_size = round_up(fp->aux->stack_depth, 16);
> cgctx.arena_vm_start = bpf_arena_get_kern_vm_start(fp->aux->arena);
> cgctx.user_vm_start = bpf_arena_get_user_vm_start(fp->aux->arena);
> + cgctx.is_subprog = bpf_is_subprog(fp);
>
> /* Scouting faux-generate pass 0 */
> if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false)) {
> @@ -435,6 +436,11 @@ void bpf_jit_free(struct bpf_prog *fp)
> bpf_prog_unlock_free(fp);
> }
>
> +bool bpf_jit_supports_subprog_tailcalls(void)
> +{
> + return IS_ENABLED(CONFIG_PPC64);
> +}
> +
> bool bpf_jit_supports_kfunc_call(void)
> {
> return true;
> @@ -600,15 +606,53 @@ static int invoke_bpf_mod_ret(u32 *image, u32 *ro_image, struct codegen_context
> return 0;
> }
>
> -static void bpf_trampoline_setup_tail_call_cnt(u32 *image, struct codegen_context *ctx,
> - int func_frame_offset, int r4_off)
> +/*

> + * Refer the label 'Generated stack layout' in this file for actual stack
> + * layout during trampoline invocation.

The above and the below comment seem to mean the same thing.
One is enough?

> + *
> + * Refer __arch_prepare_bpf_trampoline() for stack component details.

> + *
> + * The tailcall count/reference is present in caller's stack frame. Its required
> + * to copy the content of tail_call_info before calling the actual function
> + * to which the trampoline is attached.
> + */

Instead of:

"Its required to copy the content of tail_call_info before calling the
actual function to which the trampoline is attached."

use:

"The tail_call_info is saved at the same offset on the trampoline
frame for the traced function (BPF subprog/callee) to fetch it."

- Hari