Re: [PATCH v3] LoongArch: BPF: Fix stack arguments for indirect trampolines

From: Hengqi Chen

Date: Mon Oct 26 2026 - 10:43:20 EST


On Mon, Oct 19, 2026 at 9:33 AM Tiezhu Yang <yangtiezhu@xxxxxxxxxxx> wrote:
>
> From: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx>
>
> LoongArch passes arguments beyond a0-a7 at the caller stack pointer. The
> trampoline store_args() helper always reads those arguments at FP + 16,
> which is correct for a regular fentry trampoline: its prologue leaves FP
> 16 bytes below the stack pointer at trampoline entry to hold the saved
> RA/FP frame record of the parent function.
>
> A struct_ops indirect trampoline is entered through a function pointer and
> only saves its own RA and FP before setting FP to the entry stack pointer.
> Its stack arguments therefore start at FP, not FP + 16. As a result, every
> stack-passed struct_ops argument is currently read two slots late.
>
> Select the source offset based on whether the trampoline is indirect. This
> also prepares the stack-passed arena argument path to consume the actual
> pointer slot.
>
> With this patch, the BPF selftest "./test_progs -t struct_ops_multi_args"
> passed on LoongArch.
>
> Fixes: c9ebe2016de9 ("LoongArch: BPF: Support up to 12 function arguments for trampoline")
> Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx>
> Signed-off-by: Tiezhu Yang <yangtiezhu@xxxxxxxxxxx>
> ---

Acked-by: Hengqi Chen <hengqi.chen@xxxxxxxxx>

> v3:
> - Reword the log and comment to clarify that the 16-byte frame record
> is saved for the parent function, not the traced function. (bot+bpf-ci)
> - Remove a stray blank line after the opening brace of store_args(). (bot+bpf-ci)
> - Reorder the comment sentences to match the true/false branches of
> is_struct_ops for better readability.
>
> Here is the original link:
> https://lore.kernel.org/bpf/20260821233516.3426127-8-memxor@xxxxxxxxx/
>
> This version is based on the series:
> https://lore.kernel.org/loongarch/20261016081006.26069-1-yangtiezhu@xxxxxxxxxxx/
>
> arch/loongarch/net/bpf_jit.c | 13 +++++++++----
> 1 file changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
> index 1a7c8effc37b..c6784602f029 100644
> --- a/arch/loongarch/net/bpf_jit.c
> +++ b/arch/loongarch/net/bpf_jit.c
> @@ -1725,17 +1725,22 @@ int bpf_arch_text_invalidate(void *dst, size_t len)
> return ret;
> }
>
> -static void store_args(struct jit_ctx *ctx, int nr_arg_slots, int args_off)
> +static void store_args(struct jit_ctx *ctx, int nr_arg_slots, int args_off, bool is_struct_ops)
> {
> + /*
> + * Indirect struct_ops trampolines place incoming stack arguments directly at FP.
> + * Regular fentry trampolines need to skip the 16-byte RA/FP frame record saved
> + * for the parent function.
> + */
> + int stack_args_off = is_struct_ops ? 0 : 16;
> int i;
>
> for (i = 0; i < nr_arg_slots; i++) {
> if (i < LOONGARCH_MAX_REG_ARGS)
> emit_insn(ctx, std, LOONGARCH_GPR_A0 + i, LOONGARCH_GPR_FP, -args_off);
> else {
> - /* Skip slots for T0 and FP of traced function */
> emit_insn(ctx, ldd, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP,
> - 16 + (i - LOONGARCH_MAX_REG_ARGS) * 8);
> + stack_args_off + (i - LOONGARCH_MAX_REG_ARGS) * 8);
> emit_insn(ctx, std, LOONGARCH_GPR_T1, LOONGARCH_GPR_FP, -args_off);
> }
> args_off -= 8;
> @@ -2068,7 +2073,7 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
> func_meta = nr_arg_slots;
> emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, func_meta);
>
> - store_args(ctx, nr_arg_slots, args_off);
> + store_args(ctx, nr_arg_slots, args_off, is_struct_ops);
>
> if (bpf_fsession_cnt(tnodes)) {
> /* clear all session cookies' value */
> --
> 2.42.0
>