Re: [PATCH bpf-next v6 6/7] riscv, bpf: Mixing bpf2bpf and tailcalls
From: Pu Lehui
Date: Wed Jul 08 2026 - 04:58:20 EST
On 2026/7/8 15:35, bot+bpf-ci@xxxxxxxxxx wrote:
commit 64161f8a9f1c78ecd88b27109e6ed8b53282f94b
Author: Pu Lehui <pulehui@xxxxxxxxxx>
Date: Wed Jul 8 06:44:35 2026 +0000
riscv, bpf: Mixing bpf2bpf and tailcalls
In the current RV64 JIT, if we just don't initialize the TCC in subprog,
the TCC can be propagated from the parent process to the subprocess, but
the updated TCC of the parent process cannot be restored when the
subprocess exits. Since the RV64 TCC is initialized before saving the
callee saved registers into the stack, we cannot use the callee saved
register to pass the TCC, otherwise the original value of the callee
saved register will be destroyed. So we implemented mixing bpf2bpf and
tailcalls similar to x86_64, i.e. using a non-callee saved register to
transfer the TCC between functions, and saving that register to the
stack to protect the TCC value. As for the tailcall hierarchy issue,
inspired by the s390's low-overhead approach, we store TCC from
RV_REG_TCC back to stack after calling bpf2bpf call or calling orig bpf
func in bpf trampoline.
Tests test_bpf.ko and test_verifier have passed, as well as the relative
testcases of test_progs*.
Signed-off-by: Pu Lehui <pulehui@xxxxxxxxxx>
This looks like a bug fix for TCC handling across bpf2bpf calls and
tailcalls. Should this include:
Fixes: 2353ecc6f91f ("bpf, riscv: add BPF JIT for RV64G")
diff --git a/arch/riscv/net/bpf_jit.h b/arch/riscv/net/bpf_jit.h
index da02717902442..419b9d795f2a7 100644
--- a/arch/riscv/net/bpf_jit.h
+++ b/arch/riscv/net/bpf_jit.h
@@ -81,6 +81,7 @@ struct rv_jit_context {
int ex_jmp_off;
unsigned long flags;
int stack_size;
+ int tcc_offset;
u64 arena_vm_start;
u64 user_vm_start;
};
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 7c6304e0b846a..823262ca47ebf 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
[ ... ]
@@ -2038,11 +2038,10 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
for (i = 0; i < RV_FENTRY_NINSNS; i++)
emit(rv_nop(), ctx);
- /* First instruction is always setting the tail-call-counter
- * (TCC) register. This instruction is skipped for tail calls.
- * Force using a 4-byte (non-compressed) instruction.
- */
- emit(rv_addi(RV_REG_TCC, RV_REG_ZERO, MAX_TAIL_CALL_CNT), ctx);
+ if (!is_subprog)
+ emit(rv_addi(RV_REG_TCC, RV_REG_ZERO, MAX_TAIL_CALL_CNT), ctx);
+
+ /* tailcall starts here, emit insn before it must be fixed */
emit_addi(RV_REG_SP, RV_REG_SP, -stack_adjust, ctx);
The TCC initialization is now conditionally emitted based on is_subprog.
Does this affect the RV_TAILCALL_OFFSET calculation?
Looking at the RV_TAILCALL_OFFSET macro definition:
#define RV_TAILCALL_OFFSET ((RV_FENTRY_NINSNS + 1) * 4)
This assumes a fixed number of instructions before the tailcall entry
point. When is_subprog is true, the rv_addi() instruction is not emitted,
which means the tailcall entry point moves forward by 4 bytes relative to
where RV_TAILCALL_OFFSET expects it to be.
The tailcall entry is used in emit_bpf_tail_call() when calculating the
jump target for the tail call. If RV_TAILCALL_OFFSET doesn't account for
the conditional emission, could this cause the wrong entry point to be
used when tail calling into subprograms?
This is not an issue, subprog can not be the tailcall callee.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/28923410500