s390/bpf: Implement exceptions
Subsystems (5)
Ilya Leoshkevich
maintainercover s390/bpf: Implement exceptions
Hi,
this series implements exceptions in the s390x JIT. Patch 1 is a small refactoring, patch 2 is the implementation, and patch 3 enables the tests in the CI.
Best regards,
Ilya
Ilya Leoshkevich (3):
s390/bpf: Change seen_reg to a mask
s390/bpf: Implement exceptions
selftests/bpf: Remove exceptions tests from DENYLIST.s390x
arch/s390/net/bpf_jit_comp.c | 85 +++++++++++++++++----- tools/testing/selftests/bpf/DENYLIST.s390x | 1 - 2 files changed, 68 insertions(+), 18 deletions(-)
Signature · 1 line
2.45.2
Ilya Leoshkevich
maintainerPATCH 1/3 s390/bpf: Change seen_reg to a mask
Using a mask instead of an array saves a small amount of memory and allows marking multiple registers as seen with a simple "or". Another positive side-effect is that it speeds up verification with jitterbug.
--- arch/s390/net/bpf_jit_comp.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
arch/s390/net/bpf_jit_comp.c +16 -16
@@ -35,7 +35,7 @@struct bpf_jit {u32 seen;/* Flags to remember seen eBPF instructions */ -u32 seen_reg[16 ];/* Array to remember which registers are used */ +u16 seen_regs;/* Mask to remember which registers are used */ u32 *addrs;/* Array with relative instruction addresses */ u8 *prg_buf;/* Start of program */ int size;/* Size of program and literal pool */
@@ -120,8 +120,8 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) {u32 r1 = reg2hex[b1]; -if (r1 >=6 && r1 <=15 && !jit->seen_reg[r1]) - jit->seen_reg[r1] =1 ; +if (r1 >=6 && r1 <=15 ) + jit->seen_regs |= (1 << r1); }#define REG_SET_SEEN (b1) \
@@ -129,8 +129,6 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1)reg_set_seen (jit, b1); \ }) -#define REG_SEEN (b1) jit->seen_reg[reg2hex[(b1)]] -/* * EMIT macros for code generation */
@@ -438,12 +436,12 @@ static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re, u32 stack_depth)/* * Return first seen register (from start) */ -static int get_start (struct bpf_jit *jit,int start) +static int get_start (u16 seen_regs,int start) {int i;for (i = start; i <=15 ; i++) { -if (jit->seen_reg[i]) +if (seen_regs & (1 << i))return i; }return 0 ;
@@ -452,15 +450,15 @@ static int get_start(struct bpf_jit *jit, int start)/* * Return last seen register (from start) (gap >= 2) */ -static int get_end (struct bpf_jit *jit,int start) +static int get_end (u16 seen_regs,int start) {int i;for (i = start; i <15 ; i++) { -if (!jit->seen_reg[i] && !jit->seen_reg[i +1 ]) +if (!(seen_regs & (3 << i)))return i -1 ; } -return jit->seen_reg[15 ] ?15 :14 ; +return (seen_regs & (1 <<15 )) ?15 :14 ; }#define REGS_SAVE 1
@@ -469,8 +467,10 @@ static int get_end(struct bpf_jit *jit, int start)* Save and restore clobbered registers (6-15) on stack. * We save/restore registers in chunks with gap >= 2 registers. */ -static void save_restore_regs (struct bpf_jit *jit,int op,u32 stack_depth) +static void save_restore_regs (struct bpf_jit *jit,int op,u32 stack_depth, +u16 extra_regs) { +u16 seen_regs = jit->seen_regs | extra_regs;const int last =15 , save_restore_size =6 ;int re =6 , rs;
@@ -484,10 +484,10 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth) } do { - rs = get_start(jit, re); + rs = get_start(seen_regs, re); if (!rs) break; - re = get_end(jit, rs + 1); + re = get_end(seen_regs, rs + 1); if (op == REGS_SAVE) save_regs(jit, rs, re); else
@@ -573,7 +573,7 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp,/* Tail calls have to skip above initialization */ jit->tail_call_start = jit->prg;/* Save registers */ -save_restore_regs (jit,REGS_SAVE , stack_depth); +save_restore_regs (jit,REGS_SAVE , stack_depth,0 );/* Setup literal pool */ if (is_first_pass (jit) || (jit->seen &SEEN_LITERAL )) { if (!is_first_pass(jit) &&
@@ -649,7 +649,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth)/* Load exit code: lgr %r2,%b0 */ EMIT4 (0xb9040000 ,REG_2 ,BPF_REG_0 );/* Restore registers */ -save_restore_regs (jit,REGS_RESTORE , stack_depth); +save_restore_regs (jit,REGS_RESTORE , stack_depth,0 );if (nospec_uses_trampoline ()) { jit->r14_thunk_ip = jit->prg;/* Generate __s390_indirect_jump_r14 thunk */
@@ -1847,7 +1847,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,/* * Restore registers before calling function */ -save_restore_regs (jit,REGS_RESTORE , stack_depth); +save_restore_regs (jit,REGS_RESTORE , stack_depth,0 );/* * goto *(prog->bpf_func + tail_call_start);
Signature · 1 line
2.45.2
Ilya Leoshkevich
maintainerPATCH 2/3 s390/bpf: Implement exceptions
In reply to Ilya LeoshkevichImplement the following three pieces required from the JIT:
- A "top-level" BPF prog (exception_boundary) must save all non-volatile registers, and not only the ones that it clobbers.
- A "handler" BPF prog (exception_cb) must switch stack to that of exception_boundary, and restore the registers that exception_boundary saved.
- arch_bpf_stack_walk() must unwind the stack and provide the results in a way that satisfies both bpf_throw() and exception_cb.
--- arch/s390/net/bpf_jit_comp.c | 55 ++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-)
arch/s390/net/bpf_jit_comp.c +53 -2
@@ -31,6 +31,7 @@#include <asm/nospec-branch.h> #include <asm/set_memory.h> #include <asm/text-patching.h> +#include <asm/unwind.h> #include "bpf_jit.h" struct bpf_jit {
@@ -62,6 +63,8 @@ struct bpf_jit {#define SEEN_FUNC BIT(2)/* calls C functions */ #define SEEN_STACK (SEEN_FUNC | SEEN_MEM) +#define NVREGS 0xffc0/* %r6-%r15 */ +/* * s390 registers */
@@ -572,8 +575,21 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, }/* Tail calls have to skip above initialization */ jit->tail_call_start = jit->prg; -/* Save registers */ -save_restore_regs (jit,REGS_SAVE , stack_depth,0 ); +if (fp->aux->exception_cb) { +/* +* Switch stack, the new address is in the 2nd parameter. +* +* Arrange the restoration of %r6-%r15 in the epilogue. +* Do not restore them now, the prog does not need them. +*/ +/* lgr %r15,%r3 */ +EMIT4 (0xb9040000 ,REG_15 ,REG_3 ); + jit->seen_regs |=NVREGS ; + }else { +/* Save registers */ +save_restore_regs (jit,REGS_SAVE , stack_depth, + fp->aux->exception_boundary ?NVREGS :0 ); + }/* Setup literal pool */ if (is_first_pass (jit) || (jit->seen &SEEN_LITERAL )) { if (!is_first_pass(jit) &&
@@ -2909,3 +2925,38 @@ bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)*/ return true; } + +bool bpf_jit_supports_exceptions (void ) +{ +/* +* Exceptions require unwinding support, which is always available, +* because the kernel is always built with backchain. +*/ +return true; +} + +void arch_bpf_stack_walk (bool (*consume_fn)(void *,u64 ,u64 ,u64 ), +void *cookie) +{ +unsigned long addr, prev_addr =0 ; +struct unwind_state state; + +unwind_for_each_frame (&state,NULL ,NULL ,0 ) { + addr =unwind_get_return_address (&state); +if (!addr) +break ; +/* +* addr is a return address and state.sp is the value of %r15 +* at this address. exception_cb needs %r15 at entry to the +* function containing addr, so take the next state.sp. +* +* There is no bp, and the exception_cb prog does not need one +* to perform a quasi-longjmp. The common code requires a +* non-zero bp, so pass sp there as well. +*/ +if (prev_addr && !consume_fn (cookie, prev_addr, state.sp, + state.sp)) +break ; + prev_addr = addr; + } +}
Signature · 1 line
2.45.2
Ilya Leoshkevich
maintainerPATCH 3/3 selftests/bpf: Remove exceptions tests from DENYLIST.s390x
In reply to Ilya LeoshkevichNow that the s390x JIT supports exceptions, remove the respective tests from the denylist.
--- tools/testing/selftests/bpf/DENYLIST.s390x | 1 - 1 file changed, 1 deletion(-)
tools/testing/selftests/bpf/DENYLIST.s390x +0 -1
@@ -1,6 +1,5 @@ # TEMPORARY # Alphabetical order -exceptions # JIT does not support calling kfunc bpf_throw (exceptions) get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace) stacktrace_build_id # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2 (?) verifier_iterating_callbacks
Signature · 1 line
2.45.2
This series was applied to bpf/bpf-next.git (master) by Daniel Borkmann <[email protected]>:
Hello:
This series was applied to bpf/bpf-next.git (master) by Daniel Borkmann <[email protected]>:
Quoted 10 lines · On Wed, 3 Jul 2024 02:48:46 +0200 you wrote:
Hi,
this series implements exceptions in the s390x JIT. Patch 1 is a small refactoring, patch 2 is the implementation, and patch 3 enables the tests in the CI.
Best regards,
Ilya[...]
Here is the summary with links:
- [bpf-next,1/3] s390/bpf: Change seen_reg to a mask
https://git.kernel.org/bpf/bpf-next/c/7ba4f43e16de - [bpf-next,2/3] s390/bpf: Implement exceptions https://git.kernel.org/bpf/bpf-next/c/fa7bd4b000a7 - [bpf-next,3/3] selftests/bpf: Remove exceptions tests from DENYLIST.s390x https://git.kernel.org/bpf/bpf-next/c/02480fe8a6a6
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html