BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next 0/3] Add BPF Exceptions support for RISC-V
@ 2026-06-21 14:42 Varun R Mallya
  2026-06-21 14:42 ` [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF Varun R Mallya
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Varun R Mallya @ 2026-06-21 14:42 UTC (permalink / raw)
  To: pjw, palmer, aou, ast, daniel, andrii, eddyz87, memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, pulehui,
	puranjay, linux-riscv, linux-kernel, bpf, linux-kselftest,
	varunrmallya

This patchset aims to add BPF exceptions supports for riscv64 by
implementing the arch_bpf_stack_walk function and then updating the
prologue and epilogue for BPF JIT on riscv. Also,
bpf_jit_supports_exceptions() returns true now so that the verifier does
not reject programs containing BPF exceptions on riscv64. 

On riscv the unwinder used by arch_bpf_stack_walk() is the frame-pointer
unwinder, so exception support is gated on CONFIG_FRAME_POINTER.

In the prologue and epilogue of the RISC-V JIT, I saved the
return address and then the frame-pointer according to [1]. Also,
according to [2], s0 to s11 are callee saved registers, which is why a
new array (rv_exception_csave_regs) has been created to save these
registers which contains all the required registers along with the frame
pointer as well as return address.

The following demonstrates that all the selftests for BPF exceptions
apart from the ones that mix bpf-to-bpf calls and tailcalls pass.
This patch was tested using vmtest.sh to run the selftests.

test_exceptions_success:PASS:exceptions__open 0 nsec
libbpf: prog 'exception_tail_call': BPF program load failed: -EINVAL
libbpf: prog 'exception_tail_call': -- BEGIN PROG LOAD LOG --
0: R1=ctx() R10=fp0
; volatile int ret = 0; @ exceptions.c:106
0: (b4) w2 = 0                        ; R2=0
1: (63) *(u32 *)(r10 -4) = r2         ; R2=0 R10=fp0
; ret = exception_tail_call_subprog(ctx); @ exceptions.c:108
2: (85) call pc+4
caller:
 R10=fp0
callee:
 frame1: R1=ctx() R2=0 R10=fp0
7: frame1: R1=ctx() R10=fp0
; int exception_tail_call_subprog(struct __sk_buff *ctx) @ exceptions.c:96
7: (bf) r6 = r1                       ; frame1: R1=ctx() R6=ctx()
; volatile int ret = 10; @ exceptions.c:98
8: (b4) w1 = 10                       ; frame1: R1=10
9: (63) *(u32 *)(r10 -4) = r1         ; frame1: R1=10 R10=fp0 fp-8=mmmm????
; asm volatile("r1 = %[ctx]\n\t" @ bpf_helpers.h:169
10: (18) r7 = 0xff60000080df1800      ; frame1: R7=map_ptr(map=jmp_table,ks=4,vs=4)
12: (bf) r1 = r6                      ; frame1: R1=ctx() R6=ctx()
13: (bf) r2 = r7                      ; frame1: R2=map_ptr(map=jmp_table,ks=4,vs=4) R7=map_ptr(map=jmp_table,ks=4,vs=4)
14: (b7) r3 = 0                       ; frame1: R3=0
15: (85) call bpf_tail_call#12
mixing of tail_calls and bpf-to-bpf calls is not supported
processed 11 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'exception_tail_call': failed to load: -EINVAL
libbpf: failed to load object 'exceptions'
libbpf: failed to load BPF skeleton 'exceptions': -EINVAL
test_exceptions_success:FAIL:exceptions__load unexpected error: -22 (errno 22)
tester_init:PASS:tester_log_buf 0 nsec
process_subtest:PASS:obj_open_mem 0 nsec
process_subtest:PASS:specs_alloc 0 nsec
tester_init:PASS:tester_log_buf 0 nsec
process_subtest:PASS:obj_open_mem 0 nsec
process_subtest:PASS:specs_alloc 0 nsec
#113/1   exceptions/reject_exception_cb_type_1:OK
#113/2   exceptions/reject_exception_cb_type_2:OK
#113/3   exceptions/reject_exception_cb_type_3:OK
#113/4   exceptions/reject_exception_cb_type_4:OK
#113/5   exceptions/reject_exception_cb_type_5:OK
#113/6   exceptions/reject_async_callback_throw:OK
#113/7   exceptions/reject_with_lock:OK
#113/8   exceptions/reject_subprog_with_lock:OK
#113/9   exceptions/reject_with_rcu_read_lock:OK
#113/10  exceptions/reject_subprog_with_rcu_read_lock:OK
#113/11  exceptions/reject_with_rbtree_add_throw:OK
#113/12  exceptions/reject_with_reference:OK
#113/13  exceptions/reject_global_subprog_throw_with_reference:OK
#113/14  exceptions/reject_with_cb_reference:OK
#113/15  exceptions/reject_with_cb:OK
#113/16  exceptions/reject_with_subprog_reference:OK
#113/17  exceptions/reject_throwing_exception_cb:OK
#113/18  exceptions/reject_exception_cb_call_global_func:OK
#113/19  exceptions/reject_exception_cb_call_static_func:OK
#113/20  exceptions/reject_multiple_exception_cb:OK
#113/21  exceptions/reject_exception_throw_cb:OK
#113/22  exceptions/reject_exception_throw_cb_diff:OK
#113/23  exceptions/reject_subprog_rcu_lock_throw:OK
#113/24  exceptions/reject_subprog_throw_preempt_lock:OK
#113/25  exceptions/reject_subprog_throw_irq_lock:OK
#113/26  exceptions/reject_set_exception_cb_bad_ret1:OK
#113/27  exceptions/reject_set_exception_cb_bad_ret2:OK
#113/28  exceptions/reject_out_of_range_global_throw:OK
#113/29  exceptions/check_assert_eq_int_min:OK
#113/30  exceptions/check_assert_eq_int_max:OK
#113/31  exceptions/check_assert_eq_zero:OK
#113/32  exceptions/check_assert_eq_llong_min:OK
#113/33  exceptions/check_assert_eq_llong_max:OK
#113/34  exceptions/check_assert_lt_pos:OK
#113/35  exceptions/check_assert_lt_zero:OK
#113/36  exceptions/check_assert_lt_neg:OK
#113/37  exceptions/check_assert_le_pos:OK
#113/38  exceptions/check_assert_le_zero:OK
#113/39  exceptions/check_assert_le_neg:OK
#113/40  exceptions/check_assert_gt_pos:OK
#113/41  exceptions/check_assert_gt_zero:OK
#113/42  exceptions/check_assert_gt_neg:OK
#113/43  exceptions/check_assert_ge_pos:OK
#113/44  exceptions/check_assert_ge_zero:OK
#113/45  exceptions/check_assert_ge_neg:OK
#113/46  exceptions/check_assert_range_s64:OK
#113/47  exceptions/check_assert_range_u64:OK
#113/48  exceptions/check_assert_single_range_s64:OK
#113/49  exceptions/check_assert_single_range_u64:OK
#113/50  exceptions/check_assert_generic:OK
#113/51  exceptions/check_assert_with_return:OK
#113     exceptions:FAIL

All error logs:
test_exceptions_success:PASS:exceptions__open 0 nsec
libbpf: prog 'exception_tail_call': BPF program load failed: -EINVAL
libbpf: prog 'exception_tail_call': -- BEGIN PROG LOAD LOG --
0: R1=ctx() R10=fp0
; volatile int ret = 0; @ exceptions.c:106
0: (b4) w2 = 0                        ; R2=0
1: (63) *(u32 *)(r10 -4) = r2         ; R2=0 R10=fp0
; ret = exception_tail_call_subprog(ctx); @ exceptions.c:108
2: (85) call pc+4
caller:
 R10=fp0
callee:
 frame1: R1=ctx() R2=0 R10=fp0
7: frame1: R1=ctx() R10=fp0
; int exception_tail_call_subprog(struct __sk_buff *ctx) @ exceptions.c:96
7: (bf) r6 = r1                       ; frame1: R1=ctx() R6=ctx()
; volatile int ret = 10; @ exceptions.c:98
8: (b4) w1 = 10                       ; frame1: R1=10
9: (63) *(u32 *)(r10 -4) = r1         ; frame1: R1=10 R10=fp0 fp-8=mmmm????
; asm volatile("r1 = %[ctx]\n\t" @ bpf_helpers.h:169
10: (18) r7 = 0xff60000080df1800      ; frame1: R7=map_ptr(map=jmp_table,ks=4,vs=4)
12: (bf) r1 = r6                      ; frame1: R1=ctx() R6=ctx()
13: (bf) r2 = r7                      ; frame1: R2=map_ptr(map=jmp_table,ks=4,vs=4) R7=map_ptr(map=jmp_table,ks=4,vs=4)
14: (b7) r3 = 0                       ; frame1: R3=0
15: (85) call bpf_tail_call#12
mixing of tail_calls and bpf-to-bpf calls is not supported
processed 11 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
-- END PROG LOAD LOG --
libbpf: prog 'exception_tail_call': failed to load: -EINVAL
libbpf: failed to load object 'exceptions'
libbpf: failed to load BPF skeleton 'exceptions': -EINVAL
test_exceptions_success:FAIL:exceptions__load unexpected error: -22 (errno 22)

[1]: https://riscv-non-isa.github.io/riscv-elf-psabi-doc/#_frame_pointer_convention
[2]: https://riscv-non-isa.github.io/riscv-elf-psabi-doc/#_integer_register_convention

Varun R Mallya (3):
  riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF
  riscv, bpf: Add support for BPF exceptions
  riscv, bpf: Remove BPF exceptions from BPF CI denylist

 arch/riscv/kernel/stacktrace.c               |  28 +++++
 arch/riscv/net/bpf_jit_comp64.c              | 102 +++++++++++++++++++
 tools/testing/selftests/bpf/DENYLIST.riscv64 |   1 -
 3 files changed, 130 insertions(+), 1 deletion(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF
  2026-06-21 14:42 [PATCH bpf-next 0/3] Add BPF Exceptions support for RISC-V Varun R Mallya
@ 2026-06-21 14:42 ` Varun R Mallya
  2026-06-21 14:56   ` sashiko-bot
  2026-06-22 14:13   ` Pu Lehui
  2026-06-21 14:42 ` [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions Varun R Mallya
  2026-06-21 14:42 ` [PATCH bpf-next 3/3] riscv, bpf: Remove BPF exceptions from BPF CI denylist Varun R Mallya
  2 siblings, 2 replies; 12+ messages in thread
From: Varun R Mallya @ 2026-06-21 14:42 UTC (permalink / raw)
  To: pjw, palmer, aou, ast, daniel, andrii, eddyz87, memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, pulehui,
	puranjay, linux-riscv, linux-kernel, bpf, linux-kselftest,
	varunrmallya

This will be used by bpf_throw() to unwind till the program marked as
exception boundary and run the callback with the stack of the main
program.
This is required for supporting BPF exceptions on RISC-V.
This depends on the frame pointer unwinder, so it is only built under
CONFIG_FRAME_POINTER, else falls back to the weak no-op.

Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
---
 arch/riscv/kernel/stacktrace.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
index b41b6255751c..a4c2411f4038 100644
--- a/arch/riscv/kernel/stacktrace.c
+++ b/arch/riscv/kernel/stacktrace.c
@@ -5,6 +5,7 @@
  */
 
 #include <linux/export.h>
+#include <linux/filter.h>
 #include <linux/kallsyms.h>
 #include <linux/sched.h>
 #include <linux/sched/debug.h>
@@ -102,6 +103,33 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
 	}
 }
 
+void notrace arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp),
+				 void *cookie)
+{
+	unsigned long fp, sp, pc;
+
+	fp = (unsigned long)__builtin_frame_address(0);
+	sp = current_stack_pointer;
+	pc = (unsigned long)arch_bpf_stack_walk;
+
+	for (;;) {
+		struct stackframe *frame;
+
+		if (unlikely(!__kernel_text_address(pc)))
+			break;
+		/* pc belongs to the function whose frame pointer is fp */
+		if (!consume_fn(cookie, pc, sp, fp))
+			break;
+		if (unlikely(!fp_is_valid(fp, sp)))
+			break;
+
+		frame = (struct stackframe *)fp - 1;
+		sp = fp;
+		fp = READ_ONCE_TASK_STACK(current, frame->fp);
+		pc = READ_ONCE_TASK_STACK(current, frame->ra);
+	}
+}
+
 #else /* !CONFIG_FRAME_POINTER */
 
 void notrace walk_stackframe(struct task_struct *task,
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions
  2026-06-21 14:42 [PATCH bpf-next 0/3] Add BPF Exceptions support for RISC-V Varun R Mallya
  2026-06-21 14:42 ` [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF Varun R Mallya
@ 2026-06-21 14:42 ` Varun R Mallya
  2026-06-21 15:32   ` bot+bpf-ci
  2026-06-23  2:11   ` Pu Lehui
  2026-06-21 14:42 ` [PATCH bpf-next 3/3] riscv, bpf: Remove BPF exceptions from BPF CI denylist Varun R Mallya
  2 siblings, 2 replies; 12+ messages in thread
From: Varun R Mallya @ 2026-06-21 14:42 UTC (permalink / raw)
  To: pjw, palmer, aou, ast, daniel, andrii, eddyz87, memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, pulehui,
	puranjay, linux-riscv, linux-kernel, bpf, linux-kselftest,
	varunrmallya

Add the JIT support required for BPF exceptions (bpf_throw()) on riscv64.

Two kinds of program need special prologue/epilogue handling:

  - A program acting as an exception boundary must save the full set of
    riscv callee-saved GP registers (ra, s0-s11), not just the ones it
    happens to clobber, so that the exception callback can restore the
    state that was live at the boundary. ra and fp are stored first so
    the saved ra/fp pair forms a valid stackframe record for the
    unwinder.

  - The exception callback (exception_cb) does not allocate its own
    frame. It reuses the boundary program's frame, whose frame pointer
    is passed in a2, by setting SP = FP - stack_adjust. This lines the
    epilogue's loads up with the registers the boundary saved, so both
    paths restore the same order.

Wire up bpf_jit_support to be true only when CONFIG_FRAME_POINTER is
enabled.

Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
---
 arch/riscv/net/bpf_jit_comp64.c | 102 ++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index c03c1de16b79..eafac0655dab 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -56,6 +56,30 @@ static const int pt_regmap[] = {
 	[RV_REG_T0] = offsetof(struct pt_regs, t0),
 };
 
+/*
+ * Full set of RISC-V callee-saved GP registers (ra, s0-s11) saved by a program
+ * acting as an exception boundary, in the order they are stored on the stack.
+ * RA and FP come first so the saved ra/fp pair forms a valid stackframe record
+ * at [FP-8]/[FP-16] for the unwinder. The exception callback reuses the
+ * boundary program's frame and restores this same set in its epilogue, so both
+ * paths must agree on the contents and ordering of this list.
+ */
+static const int rv_exception_csave_regs[] = {
+	RV_REG_RA,
+	RV_REG_FP,
+	RV_REG_S1,
+	RV_REG_S2,
+	RV_REG_S3,
+	RV_REG_S4,
+	RV_REG_S5,
+	RV_REG_S6,
+	RV_REG_S7,
+	RV_REG_S8,
+	RV_REG_S9,
+	RV_REG_S10,
+	RV_REG_S11,
+};
+
 enum {
 	RV_CTX_F_SEEN_TAIL_CALL =	0,
 	RV_CTX_F_SEEN_CALL =		RV_REG_RA,
@@ -231,6 +255,22 @@ static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx)
 static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
 {
 	int stack_adjust = ctx->stack_size, store_offset = stack_adjust - 8;
+	struct bpf_prog_aux *aux = ctx->prog->aux;
+	int i;
+
+	if (aux->exception_boundary || aux->exception_cb) {
+		/*
+		 * An exception boundary saved the full callee-saved register
+		 * set and the exception callback restores it from the boundary's
+		 * frame. Both restore the same fixed set, in the same order it
+		 * was stored by bpf_jit_build_prologue().
+		 */
+		for (i = 0; i < ARRAY_SIZE(rv_exception_csave_regs); i++) {
+			emit_ld(rv_exception_csave_regs[i], store_offset, RV_REG_SP, ctx);
+			store_offset -= 8;
+		}
+		goto epilogue_tail;
+	}
 
 	if (seen_reg(RV_REG_RA, ctx)) {
 		emit_ld(RV_REG_RA, store_offset, RV_REG_SP, ctx);
@@ -267,6 +307,7 @@ static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
 		store_offset -= 8;
 	}
 
+epilogue_tail:
 	emit_addi(RV_REG_SP, RV_REG_SP, stack_adjust, ctx);
 	/* Set return value. */
 	if (!is_tail_call)
@@ -2002,11 +2043,61 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
 void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
 {
 	int i, stack_adjust = 0, store_offset, bpf_stack_adjust;
+	struct bpf_prog_aux *aux = ctx->prog->aux;
 
 	bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
 	if (bpf_stack_adjust)
 		mark_fp(ctx);
 
+	if (aux->exception_boundary || aux->exception_cb) {
+		/*
+		 * A program acting as an exception boundary saves the full set
+		 * of riscv callee saved registers (ra, s0-s11).
+		 */
+		stack_adjust = round_up(ARRAY_SIZE(rv_exception_csave_regs) * 8,
+					STACK_ALIGN);
+		stack_adjust += bpf_stack_adjust;
+		store_offset = stack_adjust - 8;
+
+		/* emit kcfi type preamble immediately before the first insn */
+		emit_kcfi(is_subprog ? cfi_bpf_subprog_hash : cfi_bpf_hash, ctx);
+
+		/* nops reserved for auipc+jalr pair */
+		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 (!aux->exception_cb && aux->exception_boundary) {
+			/*
+			 * Boundary program: allocate the frame and save the
+			 * full callee-saved set, capturing the caller's values.
+			 */
+			emit_addi(RV_REG_SP, RV_REG_SP, -stack_adjust, ctx);
+			for (i = 0; i < ARRAY_SIZE(rv_exception_csave_regs); i++) {
+				emit_sd(RV_REG_SP, store_offset,
+					rv_exception_csave_regs[i], ctx);
+				store_offset -= 8;
+			}
+			emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
+		} else {
+			/*
+			 * Exception callback, reuse the boundary program's
+			 * frame, whose frame pointer is passed in a2. Setting
+			 * SP = FP - stack_adjust lines the epilogue's loads up
+			 * with the registers the boundary saved.
+			 */
+			emit_mv(RV_REG_FP, RV_REG_A2, ctx);
+			emit_addi(RV_REG_SP, RV_REG_FP, -stack_adjust, ctx);
+		}
+
+		goto tail_setup;
+	}
+
 	if (seen_reg(RV_REG_RA, ctx))
 		stack_adjust += 8;
 	stack_adjust += 8; /* RV_REG_FP */
@@ -2082,6 +2173,7 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
 
 	emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
 
+tail_setup:
 	if (bpf_stack_adjust)
 		emit_addi(RV_REG_S5, RV_REG_SP, bpf_stack_adjust, ctx);
 
@@ -2157,3 +2249,13 @@ bool bpf_jit_supports_fsession(void)
 {
 	return true;
 }
+
+bool bpf_jit_supports_exceptions(void)
+{
+	/*
+	 * bpf_throw() unwinds by walking the frame-pointer chain from inside
+	 * the kernel back into the BPF frames (see arch_bpf_stack_walk()), so
+	 * exceptions require the frame-pointer unwinder to be enabled.
+	 */
+	return IS_ENABLED(CONFIG_FRAME_POINTER);
+}
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH bpf-next 3/3] riscv, bpf: Remove BPF exceptions from BPF CI denylist
  2026-06-21 14:42 [PATCH bpf-next 0/3] Add BPF Exceptions support for RISC-V Varun R Mallya
  2026-06-21 14:42 ` [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF Varun R Mallya
  2026-06-21 14:42 ` [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions Varun R Mallya
@ 2026-06-21 14:42 ` Varun R Mallya
  2026-06-23  2:13   ` Pu Lehui
  2 siblings, 1 reply; 12+ messages in thread
From: Varun R Mallya @ 2026-06-21 14:42 UTC (permalink / raw)
  To: pjw, palmer, aou, ast, daniel, andrii, eddyz87, memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, pulehui,
	puranjay, linux-riscv, linux-kernel, bpf, linux-kselftest,
	varunrmallya

This patch removes BPF exceptions from riscv64 denylist on
BPF selftests since support for exceptions has been added now.

Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
---
 tools/testing/selftests/bpf/DENYLIST.riscv64 | 1 -
 1 file changed, 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/DENYLIST.riscv64 b/tools/testing/selftests/bpf/DENYLIST.riscv64
index 4fc4dfdde293..9268351ce4c1 100644
--- a/tools/testing/selftests/bpf/DENYLIST.riscv64
+++ b/tools/testing/selftests/bpf/DENYLIST.riscv64
@@ -1,3 +1,2 @@
 # riscv64 deny list for BPF CI and local vmtest
-exceptions					# JIT does not support exceptions
 tailcalls/tailcall_bpf2bpf*			# JIT does not support mixing bpf2bpf and tailcalls
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF
  2026-06-21 14:42 ` [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF Varun R Mallya
@ 2026-06-21 14:56   ` sashiko-bot
  2026-06-28  6:36     ` Varun R Mallya
  2026-06-22 14:13   ` Pu Lehui
  1 sibling, 1 reply; 12+ messages in thread
From: sashiko-bot @ 2026-06-21 14:56 UTC (permalink / raw)
  To: Varun R Mallya; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing `ftrace_graph_ret_addr()` resolution in `arch_bpf_stack_walk()` breaks BPF exception unwinding when the ftrace function graph tracer is enabled.
--

commit 18f97d2c8c3fdcb52341a066f5cdbb33e67828f4
Author: Varun R Mallya <varunrmallya@gmail.com>

riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF
    
This commit implements arch_bpf_stack_walk() for RISC-V when
CONFIG_FRAME_POINTER is enabled. It allows unwinding the stack up to the
program marked as the exception boundary, which is required to support BPF
exceptions on this architecture.

> diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
> index b41b6255751cb..a4c2411f4038b 100644
> --- a/arch/riscv/kernel/stacktrace.c
> +++ b/arch/riscv/kernel/stacktrace.c

[ ... ]

> @@ -102,6 +103,33 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
>  	}
>  }
>  
> +void notrace arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp),
> +				 void *cookie)
> +{
> +	unsigned long fp, sp, pc;
> +
> +	fp = (unsigned long)__builtin_frame_address(0);
> +	sp = current_stack_pointer;
> +	pc = (unsigned long)arch_bpf_stack_walk;
> +
> +	for (;;) {
> +		struct stackframe *frame;
> +
> +		if (unlikely(!__kernel_text_address(pc)))
> +			break;
> +		/* pc belongs to the function whose frame pointer is fp */
> +		if (!consume_fn(cookie, pc, sp, fp))
> +			break;
> +		if (unlikely(!fp_is_valid(fp, sp)))
> +			break;
> +
> +		frame = (struct stackframe *)fp - 1;
> +		sp = fp;
> +		fp = READ_ONCE_TASK_STACK(current, frame->fp);
> +		pc = READ_ONCE_TASK_STACK(current, frame->ra);

[Severity: High]
Does this code need to resolve the ftrace trampoline address?

Looking at the generic unwinder walk_stackframe() in the same file, it uses
ftrace_graph_ret_addr() to recover the real instruction pointer when the
ftrace function graph tracer is active.

If a kernel function in the unwinding chain is traced by ftrace, frame->ra
will point to return_to_handler on the stack. Because return_to_handler is a
core kernel text address rather than a BPF program address, the stack walker
might silently skip the caller's frame and miss the BPF exception boundary.
This would leave the context uninitialized and trigger a kernel panic in
bpf_throw() when it validates the missing boundary.

Could pc be passed through ftrace_graph_ret_addr() here to ensure the real
return address is consumed?

> +	}
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260621144259.288135-1-varunrmallya@gmail.com?part=1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions
  2026-06-21 14:42 ` [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions Varun R Mallya
@ 2026-06-21 15:32   ` bot+bpf-ci
  2026-06-28  6:37     ` Varun R Mallya
  2026-06-23  2:11   ` Pu Lehui
  1 sibling, 1 reply; 12+ messages in thread
From: bot+bpf-ci @ 2026-06-21 15:32 UTC (permalink / raw)
  To: varunrmallya, pjw, palmer, aou, ast, daniel, andrii, eddyz87,
	memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, pulehui,
	puranjay, linux-riscv, linux-kernel, bpf, linux-kselftest,
	varunrmallya, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 3179 bytes --]

> diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
> index c03c1de16b79..eafac0655dab 100644
> --- a/arch/riscv/net/bpf_jit_comp64.c
> +++ b/arch/riscv/net/bpf_jit_comp64.c

[ ... ]

> @@ -2002,11 +2043,61 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
>  void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
>  {
>  	int i, stack_adjust = 0, store_offset, bpf_stack_adjust;
> +	struct bpf_prog_aux *aux = ctx->prog->aux;
>
>  	bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
>  	if (bpf_stack_adjust)
>  		mark_fp(ctx);
>
> +	if (aux->exception_boundary || aux->exception_cb) {
> +		/*
> +		 * A program acting as an exception boundary saves the full set
> +		 * of riscv callee saved registers (ra, s0-s11).
> +		 */
> +		stack_adjust = round_up(ARRAY_SIZE(rv_exception_csave_regs) * 8,
> +					STACK_ALIGN);
> +		stack_adjust += bpf_stack_adjust;
> +		store_offset = stack_adjust - 8;
> +
> +		/* emit kcfi type preamble immediately before the first insn */
> +		emit_kcfi(is_subprog ? cfi_bpf_subprog_hash : cfi_bpf_hash, ctx);
> +
> +		/* nops reserved for auipc+jalr pair */
> +		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);

This isn't a bug, but could the kcfi preamble, fentry-nops loop, and
TCC-init instruction be emitted once before the exception_boundary /
exception_cb branch instead of being duplicated? The same three-part
sequence appears again below in the non-exception path, and the two copies
must stay byte-for-byte identical since the tail-call epilogue skip offset
depends on this fixed layout.

> +
> +		if (!aux->exception_cb && aux->exception_boundary) {
> +			/*
> +			 * Boundary program: allocate the frame and save the
> +			 * full callee-saved set, capturing the caller's values.
> +			 */
> +			emit_addi(RV_REG_SP, RV_REG_SP, -stack_adjust, ctx);
> +			for (i = 0; i < ARRAY_SIZE(rv_exception_csave_regs); i++) {
> +				emit_sd(RV_REG_SP, store_offset,
> +					rv_exception_csave_regs[i], ctx);
> +				store_offset -= 8;
> +			}
> +			emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
> +		} else {
> +			/*
> +			 * Exception callback, reuse the boundary program's
> +			 * frame, whose frame pointer is passed in a2. Setting
> +			 * SP = FP - stack_adjust lines the epilogue's loads up
> +			 * with the registers the boundary saved.
> +			 */
> +			emit_mv(RV_REG_FP, RV_REG_A2, ctx);
> +			emit_addi(RV_REG_SP, RV_REG_FP, -stack_adjust, ctx);
> +		}
> +
> +		goto tail_setup;
> +	}
> +
>  	if (seen_reg(RV_REG_RA, ctx))
>  		stack_adjust += 8;
>  	stack_adjust += 8; /* RV_REG_FP */

[ ... ]


---
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/27908252575

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF
  2026-06-21 14:42 ` [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF Varun R Mallya
  2026-06-21 14:56   ` sashiko-bot
@ 2026-06-22 14:13   ` Pu Lehui
  1 sibling, 0 replies; 12+ messages in thread
From: Pu Lehui @ 2026-06-22 14:13 UTC (permalink / raw)
  To: Varun R Mallya, pjw, palmer, aou, ast, daniel, andrii, eddyz87,
	memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, puranjay,
	linux-riscv, linux-kernel, bpf, linux-kselftest

Hi Varun,

Thank you for implementing bpf exception for riscv64.

On 2026/6/21 22:42, Varun R Mallya wrote:
> This will be used by bpf_throw() to unwind till the program marked as
> exception boundary and run the callback with the stack of the main
> program.
> This is required for supporting BPF exceptions on RISC-V.
> This depends on the frame pointer unwinder, so it is only built under
> CONFIG_FRAME_POINTER, else falls back to the weak no-op.
> 
> Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
> ---
>   arch/riscv/kernel/stacktrace.c | 28 ++++++++++++++++++++++++++++
>   1 file changed, 28 insertions(+)
> 
> diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
> index b41b6255751c..a4c2411f4038 100644
> --- a/arch/riscv/kernel/stacktrace.c
> +++ b/arch/riscv/kernel/stacktrace.c
> @@ -5,6 +5,7 @@
>    */
>   
>   #include <linux/export.h>
> +#include <linux/filter.h>
>   #include <linux/kallsyms.h>
>   #include <linux/sched.h>
>   #include <linux/sched/debug.h>
> @@ -102,6 +103,33 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
>   	}
>   }
>   
> +void notrace arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp),
> +				 void *cookie)
> +{
> +	unsigned long fp, sp, pc;
> +
> +	fp = (unsigned long)__builtin_frame_address(0);
> +	sp = current_stack_pointer;
> +	pc = (unsigned long)arch_bpf_stack_walk;
> +
> +	for (;;) {
> +		struct stackframe *frame;
> +
> +		if (unlikely(!__kernel_text_address(pc)))
> +			break;
> +		/* pc belongs to the function whose frame pointer is fp */
> +		if (!consume_fn(cookie, pc, sp, fp))
> +			break;
> +		if (unlikely(!fp_is_valid(fp, sp)))
> +			break;
> +
> +		frame = (struct stackframe *)fp - 1;
> +		sp = fp;
> +		fp = READ_ONCE_TASK_STACK(current, frame->fp);
> +		pc = READ_ONCE_TASK_STACK(current, frame->ra);
> +	}
> +}
> +
>   #else /* !CONFIG_FRAME_POINTER */
>   
>   void notrace walk_stackframe(struct task_struct *task,

Reviewed-by: Pu Lehui <pulehui@huawei.com>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions
  2026-06-21 14:42 ` [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions Varun R Mallya
  2026-06-21 15:32   ` bot+bpf-ci
@ 2026-06-23  2:11   ` Pu Lehui
  2026-06-28  7:34     ` Varun R Mallya
  1 sibling, 1 reply; 12+ messages in thread
From: Pu Lehui @ 2026-06-23  2:11 UTC (permalink / raw)
  To: Varun R Mallya, pjw, palmer, aou, ast, daniel, andrii, eddyz87,
	memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, puranjay,
	linux-riscv, linux-kernel, bpf, linux-kselftest



On 2026/6/21 22:42, Varun R Mallya wrote:
> Add the JIT support required for BPF exceptions (bpf_throw()) on riscv64.
> 
> Two kinds of program need special prologue/epilogue handling:
> 
>    - A program acting as an exception boundary must save the full set of
>      riscv callee-saved GP registers (ra, s0-s11), not just the ones it
>      happens to clobber, so that the exception callback can restore the
>      state that was live at the boundary. ra and fp are stored first so
>      the saved ra/fp pair forms a valid stackframe record for the
>      unwinder.
> 
>    - The exception callback (exception_cb) does not allocate its own
>      frame. It reuses the boundary program's frame, whose frame pointer
>      is passed in a2, by setting SP = FP - stack_adjust. This lines the
>      epilogue's loads up with the registers the boundary saved, so both
>      paths restore the same order.
> 
> Wire up bpf_jit_support to be true only when CONFIG_FRAME_POINTER is
> enabled.
> 
> Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
> ---
>   arch/riscv/net/bpf_jit_comp64.c | 102 ++++++++++++++++++++++++++++++++
>   1 file changed, 102 insertions(+)
> 
> diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
> index c03c1de16b79..eafac0655dab 100644
> --- a/arch/riscv/net/bpf_jit_comp64.c
> +++ b/arch/riscv/net/bpf_jit_comp64.c
> @@ -56,6 +56,30 @@ static const int pt_regmap[] = {
>   	[RV_REG_T0] = offsetof(struct pt_regs, t0),
>   };
>   
> +/*
> + * Full set of RISC-V callee-saved GP registers (ra, s0-s11) saved by a program
> + * acting as an exception boundary, in the order they are stored on the stack.
> + * RA and FP come first so the saved ra/fp pair forms a valid stackframe record
> + * at [FP-8]/[FP-16] for the unwinder. The exception callback reuses the
> + * boundary program's frame and restores this same set in its epilogue, so both
> + * paths must agree on the contents and ordering of this list.
> + */
> +static const int rv_exception_csave_regs[] = {
> +	RV_REG_RA,
> +	RV_REG_FP,
> +	RV_REG_S1,
> +	RV_REG_S2,
> +	RV_REG_S3,
> +	RV_REG_S4,
> +	RV_REG_S5,
> +	RV_REG_S6,
> +	RV_REG_S7,
> +	RV_REG_S8,
> +	RV_REG_S9,
> +	RV_REG_S10,
> +	RV_REG_S11,
> +};
> +
>   enum {
>   	RV_CTX_F_SEEN_TAIL_CALL =	0,
>   	RV_CTX_F_SEEN_CALL =		RV_REG_RA,
> @@ -231,6 +255,22 @@ static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx)
>   static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
>   {
>   	int stack_adjust = ctx->stack_size, store_offset = stack_adjust - 8;
> +	struct bpf_prog_aux *aux = ctx->prog->aux;
> +	int i;
> +
> +	if (aux->exception_boundary || aux->exception_cb) {
> +		/*
> +		 * An exception boundary saved the full callee-saved register
> +		 * set and the exception callback restores it from the boundary's
> +		 * frame. Both restore the same fixed set, in the same order it
> +		 * was stored by bpf_jit_build_prologue().
> +		 */
> +		for (i = 0; i < ARRAY_SIZE(rv_exception_csave_regs); i++) {
> +			emit_ld(rv_exception_csave_regs[i], store_offset, RV_REG_SP, ctx);
> +			store_offset -= 8;
> +		}
> +		goto epilogue_tail;
> +	}
>   
>   	if (seen_reg(RV_REG_RA, ctx)) {
>   		emit_ld(RV_REG_RA, store_offset, RV_REG_SP, ctx);
> @@ -267,6 +307,7 @@ static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
>   		store_offset -= 8;
>   	}
>   
> +epilogue_tail:
>   	emit_addi(RV_REG_SP, RV_REG_SP, stack_adjust, ctx);
>   	/* Set return value. */
>   	if (!is_tail_call)
> @@ -2002,11 +2043,61 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
>   void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
>   {
>   	int i, stack_adjust = 0, store_offset, bpf_stack_adjust;
> +	struct bpf_prog_aux *aux = ctx->prog->aux;
>   
>   	bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
>   	if (bpf_stack_adjust)
>   		mark_fp(ctx);
>   
> +	if (aux->exception_boundary || aux->exception_cb) {
> +		/*
> +		 * A program acting as an exception boundary saves the full set
> +		 * of riscv callee saved registers (ra, s0-s11).
> +		 */
> +		stack_adjust = round_up(ARRAY_SIZE(rv_exception_csave_regs) * 8,
> +					STACK_ALIGN);
> +		stack_adjust += bpf_stack_adjust;
> +		store_offset = stack_adjust - 8;
> +
> +		/* emit kcfi type preamble immediately before the first insn */
> +		emit_kcfi(is_subprog ? cfi_bpf_subprog_hash : cfi_bpf_hash, ctx);
> +
> +		/* nops reserved for auipc+jalr pair */
> +		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);

We don't need to duplicate code. Please merge it.

> +
> +		if (!aux->exception_cb && aux->exception_boundary) {
> +			/*
> +			 * Boundary program: allocate the frame and save the
> +			 * full callee-saved set, capturing the caller's values.
> +			 */
> +			emit_addi(RV_REG_SP, RV_REG_SP, -stack_adjust, ctx);
> +			for (i = 0; i < ARRAY_SIZE(rv_exception_csave_regs); i++) {
> +				emit_sd(RV_REG_SP, store_offset,
> +					rv_exception_csave_regs[i], ctx);
> +				store_offset -= 8;
> +			}
> +			emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
> +		} else {
> +			/*
> +			 * Exception callback, reuse the boundary program's
> +			 * frame, whose frame pointer is passed in a2. Setting

something confused—why is it A2? I feel like I missed something.

> +			 * SP = FP - stack_adjust lines the epilogue's loads up
> +			 * with the registers the boundary saved.
> +			 */
> +			emit_mv(RV_REG_FP, RV_REG_A2, ctx);
> +			emit_addi(RV_REG_SP, RV_REG_FP, -stack_adjust, ctx);
> +		}
> +
> +		goto tail_setup;
> +	}
> +
>   	if (seen_reg(RV_REG_RA, ctx))
>   		stack_adjust += 8;
>   	stack_adjust += 8; /* RV_REG_FP */
> @@ -2082,6 +2173,7 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
>   
>   	emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
>   
> +tail_setup:
>   	if (bpf_stack_adjust)
>   		emit_addi(RV_REG_S5, RV_REG_SP, bpf_stack_adjust, ctx);
>   
> @@ -2157,3 +2249,13 @@ bool bpf_jit_supports_fsession(void)
>   {
>   	return true;
>   }
> +
> +bool bpf_jit_supports_exceptions(void)
> +{
> +	/*
> +	 * bpf_throw() unwinds by walking the frame-pointer chain from inside
> +	 * the kernel back into the BPF frames (see arch_bpf_stack_walk()), so
> +	 * exceptions require the frame-pointer unwinder to be enabled.
> +	 */
> +	return IS_ENABLED(CONFIG_FRAME_POINTER);

riscv select ARCH_WANT_FRAME_POINTERS, so this will always true

> +}

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 3/3] riscv, bpf: Remove BPF exceptions from BPF CI denylist
  2026-06-21 14:42 ` [PATCH bpf-next 3/3] riscv, bpf: Remove BPF exceptions from BPF CI denylist Varun R Mallya
@ 2026-06-23  2:13   ` Pu Lehui
  0 siblings, 0 replies; 12+ messages in thread
From: Pu Lehui @ 2026-06-23  2:13 UTC (permalink / raw)
  To: Varun R Mallya, pjw, palmer, aou, ast, daniel, andrii, eddyz87,
	memxor, bjorn
  Cc: alex, martin.lau, song, yonghong.song, jolsa, emil, puranjay,
	linux-riscv, linux-kernel, bpf, linux-kselftest


On 2026/6/21 22:42, Varun R Mallya wrote:
> This patch removes BPF exceptions from riscv64 denylist on
> BPF selftests since support for exceptions has been added now.
> 
> Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
> ---
>   tools/testing/selftests/bpf/DENYLIST.riscv64 | 1 -
>   1 file changed, 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/bpf/DENYLIST.riscv64 b/tools/testing/selftests/bpf/DENYLIST.riscv64
> index 4fc4dfdde293..9268351ce4c1 100644
> --- a/tools/testing/selftests/bpf/DENYLIST.riscv64
> +++ b/tools/testing/selftests/bpf/DENYLIST.riscv64
> @@ -1,3 +1,2 @@
>   # riscv64 deny list for BPF CI and local vmtest
> -exceptions					# JIT does not support exceptions
>   tailcalls/tailcall_bpf2bpf*			# JIT does not support mixing bpf2bpf and tailcalls

Reviewed-by: Pu Lehui <pulehui@huawei.com>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF
  2026-06-21 14:56   ` sashiko-bot
@ 2026-06-28  6:36     ` Varun R Mallya
  0 siblings, 0 replies; 12+ messages in thread
From: Varun R Mallya @ 2026-06-28  6:36 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: bpf

On Sun, Jun 21, 2026 at 02:56:58PM +0000, sashiko-bot@kernel.org wrote:
> [Severity: High]
> Does this code need to resolve the ftrace trampoline address?
Yes. I forgot to add that in there. 
> Looking at the generic unwinder walk_stackframe() in the same file, it uses
> ftrace_graph_ret_addr() to recover the real instruction pointer when the
> ftrace function graph tracer is active.
> 
> If a kernel function in the unwinding chain is traced by ftrace, frame->ra
> will point to return_to_handler on the stack. Because return_to_handler is a
> core kernel text address rather than a BPF program address, the stack walker
> might silently skip the caller's frame and miss the BPF exception boundary.
> This would leave the context uninitialized and trigger a kernel panic in
> bpf_throw() when it validates the missing boundary.
> 
> Could pc be passed through ftrace_graph_ret_addr() here to ensure the real
> return address is consumed?
Addressing in next version.
-Varun
> > +	}
> > +}
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/20260621144259.288135-1-varunrmallya@gmail.com?part=1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions
  2026-06-21 15:32   ` bot+bpf-ci
@ 2026-06-28  6:37     ` Varun R Mallya
  0 siblings, 0 replies; 12+ messages in thread
From: Varun R Mallya @ 2026-06-28  6:37 UTC (permalink / raw)
  To: bot+bpf-ci
  Cc: pjw, palmer, aou, ast, daniel, andrii, eddyz87, memxor, bjorn,
	alex, martin.lau, song, yonghong.song, jolsa, emil, pulehui,
	puranjay, linux-riscv, linux-kernel, bpf, linux-kselftest,
	martin.lau, clm, ihor.solodrai

On Sun, Jun 21, 2026 at 03:32:32PM +0000, bot+bpf-ci@kernel.org wrote:
> 
> This isn't a bug, but could the kcfi preamble, fentry-nops loop, and
> TCC-init instruction be emitted once before the exception_boundary /
> exception_cb branch instead of being duplicated? The same three-part
> sequence appears again below in the non-exception path, and the two copies
> must stay byte-for-byte identical since the tail-call epilogue skip offset
> depends on this fixed layout.

Making this part common in the next version. Sensible change I missed.
- Varun

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions
  2026-06-23  2:11   ` Pu Lehui
@ 2026-06-28  7:34     ` Varun R Mallya
  0 siblings, 0 replies; 12+ messages in thread
From: Varun R Mallya @ 2026-06-28  7:34 UTC (permalink / raw)
  To: Pu Lehui
  Cc: pjw, palmer, aou, ast, daniel, andrii, eddyz87, memxor, bjorn,
	alex, martin.lau, song, yonghong.song, jolsa, emil, puranjay,
	linux-riscv, linux-kernel, bpf, linux-kselftest

On Tue, Jun 23, 2026 at 10:11:57AM +0800, Pu Lehui wrote:
> 
> We don't need to duplicate code. Please merge it.

Making this change in the next version!

> > +
> > +		if (!aux->exception_cb && aux->exception_boundary) {
> > +			/*
> > +			 * Boundary program: allocate the frame and save the
> > +			 * full callee-saved set, capturing the caller's values.
> > +			 */
> > +			emit_addi(RV_REG_SP, RV_REG_SP, -stack_adjust, ctx);
> > +			for (i = 0; i < ARRAY_SIZE(rv_exception_csave_regs); i++) {
> > +				emit_sd(RV_REG_SP, store_offset,
> > +					rv_exception_csave_regs[i], ctx);
> > +				store_offset -= 8;
> > +			}
> > +			emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
> > +		} else {
> > +			/*
> > +			 * Exception callback, reuse the boundary program's
> > +			 * frame, whose frame pointer is passed in a2. Setting
> 
> something confused—why is it A2? I feel like I missed something.

bpf_throw() invokes the exception callback as 
bpf_exception_cb(cookie, sp, bp, 0, 0) , whose 3rd argument (which,
according to RISC-V's calling convention resides in A2) is the boundary
prog's frame pointer. Since this else branch handles
the callback, it expects A2 to have the frame pointer. The arm
implementation does something very similar with emit(A64_MOV(1, A64_FP, A64_R(2)), ctx)
where A64_R(2) is the third arg.

> > +			 * SP = FP - stack_adjust lines the epilogue's loads up
> > +			 * with the registers the boundary saved.
> > +			 */
> > +			emit_mv(RV_REG_FP, RV_REG_A2, ctx);
> > +			emit_addi(RV_REG_SP, RV_REG_FP, -stack_adjust, ctx);
> > +		}
> > +
> > +		goto tail_setup;
> > +	}
> > +
> >   	if (seen_reg(RV_REG_RA, ctx))
> >   		stack_adjust += 8;
> >   	stack_adjust += 8; /* RV_REG_FP */
> > @@ -2082,6 +2173,7 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
> >   	emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
> > +tail_setup:
> >   	if (bpf_stack_adjust)
> >   		emit_addi(RV_REG_S5, RV_REG_SP, bpf_stack_adjust, ctx);
> > @@ -2157,3 +2249,13 @@ bool bpf_jit_supports_fsession(void)
> >   {
> >   	return true;
> >   }
> > +
> > +bool bpf_jit_supports_exceptions(void)
> > +{
> > +	/*
> > +	 * bpf_throw() unwinds by walking the frame-pointer chain from inside
> > +	 * the kernel back into the BPF frames (see arch_bpf_stack_walk()), so
> > +	 * exceptions require the frame-pointer unwinder to be enabled.
> > +	 */
> > +	return IS_ENABLED(CONFIG_FRAME_POINTER);
> 
> riscv select ARCH_WANT_FRAME_POINTERS, so this will always true

I checked that the kernel compiled even when I turned
CONFIG_FRAME_POINTER explicitly off, so not gating this
would be a mistake, right ? ARCH_WANT_FRAME_POINTERS makes
CONFIG_FRAME_POINTER user selectable and makes it default to
true, but it's not always true. What does force it is PERF_EVENTS=y but if
that too is turned off, then CONFIG_FRAME_POINTER can also be turned
off.

Thanks for the review!!
- Varun
> > +}

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-06-28  7:34 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-21 14:42 [PATCH bpf-next 0/3] Add BPF Exceptions support for RISC-V Varun R Mallya
2026-06-21 14:42 ` [PATCH bpf-next 1/3] riscv: stacktrace: Implement arch_bpf_stack_walk() for BPF Varun R Mallya
2026-06-21 14:56   ` sashiko-bot
2026-06-28  6:36     ` Varun R Mallya
2026-06-22 14:13   ` Pu Lehui
2026-06-21 14:42 ` [PATCH bpf-next 2/3] riscv, bpf: Add support for BPF exceptions Varun R Mallya
2026-06-21 15:32   ` bot+bpf-ci
2026-06-28  6:37     ` Varun R Mallya
2026-06-23  2:11   ` Pu Lehui
2026-06-28  7:34     ` Varun R Mallya
2026-06-21 14:42 ` [PATCH bpf-next 3/3] riscv, bpf: Remove BPF exceptions from BPF CI denylist Varun R Mallya
2026-06-23  2:13   ` Pu Lehui

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox