Merge branch 'bpf-riscv-add-timed-may_goto-support'

Feng Jiang says:

====================
bpf, riscv: add timed may_goto support

This series adds RISC-V JIT support for the timed may_goto loop bound.

Patch 1 implements arch_bpf_timed_may_goto() and enables
bpf_jit_supports_timed_may_goto() so the verifier uses the timed
expansion path.

Patch 2 adds a test that checks R0-R5 are preserved across
arch_bpf_timed_may_goto() calls.

Patch 3 enables the verifier_may_goto_1, stream_cond_break, and
may_goto_interaction fastcall tests on riscv64.

Tested on riscv64 QEMU (rva23s64): may_goto programs load and JIT
correctly, and the 250ms timeout path works as expected.

Signed-off-by: Feng Jiang <jiangfeng@kylinos.cn>
Tested-by: Pu Lehui <pulehui@huawei.com>
Reviewed-by: Björn Töpel <bjorn@kernel.org>
Acked-by: Björn Töpel <bjorn@kernel.org>
---
Changes in v5:
- Use REG_S/REG_L/SZREG in arch_bpf_timed_may_goto assembly. (Pu Lehui)
- Add __arch_s390x to the timed_may_goto_preserves_regs test. (Pu Lehui)
- Switch the preserves-regs test to SEC("syscall") to fix
  bpf_prog_test_run() EINVAL.
- Link to v4: https://lore.kernel.org/r/20260722-riscv-bpf-timed-may-goto-v4-0-e117e6337bc7@kylinos.cn

Changes in v4:
- Add 'bpf-next' prefix to match the BPF kernel tree workflow.
- Add a test checking that R0-R5 are preserved across
  arch_bpf_timed_may_goto() calls. Use bpf_get_prandom_u32() to
  prevent the verifier from removing the checks via DCE.
- Rename may_goto_interaction_arm64() to may_goto_interaction().
- Wrap the arch_bpf_timed_may_goto address check to a single line.
- Link to v3: https://lore.kernel.org/r/20260715-riscv-bpf-timed-may-goto-v3-0-cf2a9c3d843f@kylinos.cn

Changes in v3:
- Set up the frame pointer in arch_bpf_timed_may_goto() so the function
  does not break stack unwinding under CONFIG_FRAME_POINTER.

Changes in v2:
- Fix BPF_REG_0 being clobbered after arch_bpf_timed_may_goto() calls.
- Enable the may_goto_interaction fastcall test on riscv64.

---
====================

Link: https://patch.msgid.link/20260723-riscv-bpf-timed-may-goto-v5-0-86acb54e5642@kylinos.cn
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Kumar Kartikeya Dwivedi 2026-07-24 22:20:10 +02:00
commit 159d4fbb6c
No known key found for this signature in database
GPG Key ID: 472D377B63542F83
6 changed files with 124 additions and 3 deletions

View File

@ -3,7 +3,7 @@
obj-$(CONFIG_BPF_JIT) += bpf_jit_core.o
ifeq ($(CONFIG_ARCH_RV64I),y)
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp64.o
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp64.o bpf_timed_may_goto.o
else
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp32.o
endif

View File

@ -1841,7 +1841,12 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
if (aux->tail_call_reachable && insn->src_reg == BPF_PSEUDO_CALL)
emit_sd(RV_REG_SP, ctx->tcc_offset, RV_REG_TCC, ctx);
if (insn->src_reg != BPF_PSEUDO_CALL)
/*
* arch_bpf_timed_may_goto() is emitted by the verifier and
* returns its result in BPF_REG_AX instead of BPF_REG_0, so
* skip the normal "move return register into R0".
*/
if (insn->src_reg != BPF_PSEUDO_CALL && addr != (u64)arch_bpf_timed_may_goto)
emit_mv(bpf_to_rv_reg(BPF_REG_0, ctx), RV_REG_A0, ctx);
break;
}
@ -2161,3 +2166,8 @@ bool bpf_jit_supports_subprog_tailcalls(void)
{
return true;
}
bool bpf_jit_supports_timed_may_goto(void)
{
return true;
}

View File

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2026 Feng Jiang <jiangfeng@kylinos.cn> */
#include <linux/linkage.h>
#include <asm/asm.h>
/*
* Trampoline for the BPF timed may_goto loop bound. Custom calling convention:
* - input: stack offset in BPF_REG_AX (t0)
* - output: updated count in BPF_REG_AX (t0)
*
* Calls bpf_check_timed_may_goto(ptr) with the standard RISC-V ABI, where
* ptr = BPF_REG_FP (s5) + BPF_REG_AX (t0). BPF R0-R5 (a5, a0-a4) are saved
* across the call; BPF_REG_FP (s5) is callee-saved and needs no saving.
*/
SYM_FUNC_START(arch_bpf_timed_may_goto)
addi sp, sp, -(8*SZREG)
REG_S ra, 7*SZREG(sp)
REG_S s0, 6*SZREG(sp)
addi s0, sp, 8*SZREG
/* Save BPF registers R0-R5 (a5, a0-a4) */
REG_S a5, 5*SZREG(sp)
REG_S a0, 4*SZREG(sp)
REG_S a1, 3*SZREG(sp)
REG_S a2, 2*SZREG(sp)
REG_S a3, 1*SZREG(sp)
REG_S a4, 0*SZREG(sp)
add a0, t0, s5
call bpf_check_timed_may_goto
mv t0, a0
/* Restore BPF registers R0-R5 */
REG_L a4, 0*SZREG(sp)
REG_L a3, 1*SZREG(sp)
REG_L a2, 2*SZREG(sp)
REG_L a1, 3*SZREG(sp)
REG_L a0, 4*SZREG(sp)
REG_L a5, 5*SZREG(sp)
REG_L s0, 6*SZREG(sp)
REG_L ra, 7*SZREG(sp)
addi sp, sp, 8*SZREG
ret
SYM_FUNC_END(arch_bpf_timed_may_goto)

View File

@ -64,6 +64,7 @@ SEC("syscall")
__arch_x86_64
__arch_arm64
__arch_s390x
__arch_riscv64
__success __retval(0)
__stderr("ERROR: Timeout detected for may_goto instruction")
__stderr("CPU: {{[0-9]+}} UID: 0 PID: {{[0-9]+}} Comm: {{.*}}")

View File

@ -660,6 +660,7 @@ __naked void may_goto_interaction_x86_64(void)
SEC("raw_tp")
__arch_arm64
__arch_riscv64
__log_level(4) __msg("stack depth 24")
/* may_goto counter at -24 */
__xlated("0: *(u64 *)(r10 -24) =")
@ -679,7 +680,7 @@ __xlated("10: *(u64 *)(r10 -24) = r12")
__xlated("11: *(u64 *)(r10 -8) = r1")
__xlated("12: exit")
__success
__naked void may_goto_interaction_arm64(void)
__naked void may_goto_interaction(void)
{
asm volatile (
"r1 = 1;"

View File

@ -11,6 +11,7 @@ __description("may_goto 0")
__arch_x86_64
__arch_s390x
__arch_arm64
__arch_riscv64
__xlated("0: r0 = 1")
__xlated("1: exit")
__success
@ -31,6 +32,7 @@ __description("batch 2 of may_goto 0")
__arch_x86_64
__arch_s390x
__arch_arm64
__arch_riscv64
__xlated("0: r0 = 1")
__xlated("1: exit")
__success
@ -53,6 +55,7 @@ __description("may_goto batch with offsets 2/1/0")
__arch_x86_64
__arch_s390x
__arch_arm64
__arch_riscv64
__xlated("0: r0 = 1")
__xlated("1: exit")
__success
@ -79,6 +82,7 @@ __description("may_goto batch with offsets 2/0")
__arch_x86_64
__arch_s390x
__arch_arm64
__arch_riscv64
__xlated("0: *(u64 *)(r10 -16) = 65535")
__xlated("1: *(u64 *)(r10 -8) = 0")
__xlated("2: r12 = *(u64 *)(r10 -16)")
@ -106,4 +110,62 @@ __naked void may_goto_batch_2(void)
: __clobber_all);
}
/*
* Use bpf_get_prandom_u32() to prevent DCE from removing the checks.
* retval: 0=all ok, 1-6=R0-R5 clobbered.
*/
SEC("syscall")
__description("timed may_goto preserves R0-R5")
__arch_x86_64
__arch_s390x
__arch_arm64
__arch_riscv64
__success
__retval(0)
__naked void timed_may_goto_preserves_regs(void)
{
asm volatile (
"call %[bpf_get_prandom_u32];"
"r6 = r0;"
"r0 = 0x1111;"
"r0 += r6;"
"r1 = 0x2222;"
"r1 += r6;"
"r2 = 0x3333;"
"r2 += r6;"
"r3 = 0x4444;"
"r3 += r6;"
"r4 = 0x5555;"
"r4 += r6;"
"r5 = 0x6666;"
"r5 += r6;"
".8byte %[may_goto];"
".8byte %[loop];"
"r0 -= r6;"
"r1 -= r6;"
"r2 -= r6;"
"r3 -= r6;"
"r4 -= r6;"
"r5 -= r6;"
"if r0 != 0x1111 goto 1f;"
"if r1 != 0x2222 goto 2f;"
"if r2 != 0x3333 goto 3f;"
"if r3 != 0x4444 goto 4f;"
"if r4 != 0x5555 goto 5f;"
"if r5 != 0x6666 goto 6f;"
"r0 = 0;"
"exit;"
"1: r0 = 1; exit;"
"2: r0 = 2; exit;"
"3: r0 = 3; exit;"
"4: r0 = 4; exit;"
"5: r0 = 5; exit;"
"6: r0 = 6; exit;"
:
: __imm(bpf_get_prandom_u32),
__imm_insn(may_goto, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 1, 0)),
__imm_insn(loop, BPF_RAW_INSN(BPF_JMP | BPF_JA, 0, 0, -2, 0))
: __clobber_all);
}
char _license[] SEC("license") = "GPL";