Skip to content

Commit 8afd317

Browse files
andrea-parriAlexei Starovoitov
authored andcommitted
bpf, riscv64: Support load-acquire and store-release instructions
Support BPF load-acquire (BPF_LOAD_ACQ) and store-release (BPF_STORE_REL) instructions in the riscv64 JIT compiler. For example, consider the following 64-bit load-acquire (assuming little-endian): db 10 00 00 00 01 00 00 r1 = load_acquire((u64 *)(r1 + 0x0)) 95 00 00 00 00 00 00 00 exit opcode (0xdb): BPF_ATOMIC | BPF_DW | BPF_STX imm (0x00000100): BPF_LOAD_ACQ The JIT compiler will emit an LD instruction followed by a FENCE R,RW instruction for the above, e.g.: ld x7,0(x6) fence r,rw Similarly, consider the following 16-bit store-release: cb 21 00 00 10 01 00 00 store_release((u16 *)(r1 + 0x0), w2) 95 00 00 00 00 00 00 00 exit opcode (0xcb): BPF_ATOMIC | BPF_H | BPF_STX imm (0x00000110): BPF_STORE_REL A FENCE RW,W instruction followed by an SH instruction will be emitted, e.g.: fence rw,w sh x2,0(x4) 8-bit and 16-bit load-acquires are zero-extending (cf., LBU, LHU). The verifier always rejects misaligned load-acquires/store-releases (even if BPF_F_ANY_ALIGNMENT is set), so the emitted load and store instructions are guaranteed to be single-copy atomic. Introduce primitives to emit the relevant (and the most common/used in the kernel) fences, i.e. fences with R -> RW, RW -> W and RW -> RW. Rename emit_atomic() to emit_atomic_rmw() to make it clear that it only handles RMW atomics, and replace its is64 parameter to allow to perform the required checks on the opsize (BPF_SIZE(code)). Acked-by: Björn Töpel <[email protected]> Tested-by: Björn Töpel <[email protected]> # QEMU/RVA23 Signed-off-by: Andrea Parri <[email protected]> Co-developed-by: Peilin Ye <[email protected]> Signed-off-by: Peilin Ye <[email protected]> Reviewed-by: Pu Lehui <[email protected]> Link: https://lore.kernel.org/r/3059c560e537ad43ed19055d2ebbd970c698095a.1746588351.git.yepeilin@google.com Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 118ae46 commit 8afd317

File tree

2 files changed

+85
-5
lines changed

2 files changed

+85
-5
lines changed

arch/riscv/net/bpf_jit.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,21 @@ static inline u32 rv_fence(u8 pred, u8 succ)
608608
return rv_i_insn(imm11_0, 0, 0, 0, 0xf);
609609
}
610610

611+
static inline void emit_fence_r_rw(struct rv_jit_context *ctx)
612+
{
613+
emit(rv_fence(0x2, 0x3), ctx);
614+
}
615+
616+
static inline void emit_fence_rw_w(struct rv_jit_context *ctx)
617+
{
618+
emit(rv_fence(0x3, 0x1), ctx);
619+
}
620+
621+
static inline void emit_fence_rw_rw(struct rv_jit_context *ctx)
622+
{
623+
emit(rv_fence(0x3, 0x3), ctx);
624+
}
625+
611626
static inline u32 rv_nop(void)
612627
{
613628
return rv_i_insn(0, 0, 0, 0, 0x13);

arch/riscv/net/bpf_jit_comp64.c

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -607,11 +607,65 @@ static void emit_store_64(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx)
607607
emit_sd(RV_REG_T1, 0, rs, ctx);
608608
}
609609

610-
static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64,
611-
struct rv_jit_context *ctx)
610+
static int emit_atomic_ld_st(u8 rd, u8 rs, s16 off, s32 imm, u8 code, struct rv_jit_context *ctx)
611+
{
612+
switch (imm) {
613+
/* dst_reg = load_acquire(src_reg + off16) */
614+
case BPF_LOAD_ACQ:
615+
switch (BPF_SIZE(code)) {
616+
case BPF_B:
617+
emit_load_8(false, rd, off, rs, ctx);
618+
break;
619+
case BPF_H:
620+
emit_load_16(false, rd, off, rs, ctx);
621+
break;
622+
case BPF_W:
623+
emit_load_32(false, rd, off, rs, ctx);
624+
break;
625+
case BPF_DW:
626+
emit_load_64(false, rd, off, rs, ctx);
627+
break;
628+
}
629+
emit_fence_r_rw(ctx);
630+
break;
631+
/* store_release(dst_reg + off16, src_reg) */
632+
case BPF_STORE_REL:
633+
emit_fence_rw_w(ctx);
634+
switch (BPF_SIZE(code)) {
635+
case BPF_B:
636+
emit_store_8(rd, off, rs, ctx);
637+
break;
638+
case BPF_H:
639+
emit_store_16(rd, off, rs, ctx);
640+
break;
641+
case BPF_W:
642+
emit_store_32(rd, off, rs, ctx);
643+
break;
644+
case BPF_DW:
645+
emit_store_64(rd, off, rs, ctx);
646+
break;
647+
}
648+
break;
649+
default:
650+
pr_err_once("bpf-jit: invalid atomic load/store opcode %02x\n", imm);
651+
return -EINVAL;
652+
}
653+
654+
return 0;
655+
}
656+
657+
static int emit_atomic_rmw(u8 rd, u8 rs, s16 off, s32 imm, u8 code,
658+
struct rv_jit_context *ctx)
612659
{
613660
u8 r0;
614661
int jmp_offset;
662+
bool is64;
663+
664+
if (BPF_SIZE(code) != BPF_W && BPF_SIZE(code) != BPF_DW) {
665+
pr_err_once("bpf-jit: 1- and 2-byte RMW atomics are not supported\n");
666+
return -EINVAL;
667+
}
668+
is64 = BPF_SIZE(code) == BPF_DW;
615669

616670
if (off) {
617671
if (is_12b_int(off)) {
@@ -688,9 +742,14 @@ static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64,
688742
rv_sc_w(RV_REG_T3, rs, rd, 0, 1), ctx);
689743
jmp_offset = ninsns_rvoff(-6);
690744
emit(rv_bne(RV_REG_T3, 0, jmp_offset >> 1), ctx);
691-
emit(rv_fence(0x3, 0x3), ctx);
745+
emit_fence_rw_rw(ctx);
692746
break;
747+
default:
748+
pr_err_once("bpf-jit: invalid atomic RMW opcode %02x\n", imm);
749+
return -EINVAL;
693750
}
751+
752+
return 0;
694753
}
695754

696755
#define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0)
@@ -1962,10 +2021,16 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
19622021
case BPF_STX | BPF_MEM | BPF_DW:
19632022
emit_store_64(rd, off, rs, ctx);
19642023
break;
2024+
case BPF_STX | BPF_ATOMIC | BPF_B:
2025+
case BPF_STX | BPF_ATOMIC | BPF_H:
19652026
case BPF_STX | BPF_ATOMIC | BPF_W:
19662027
case BPF_STX | BPF_ATOMIC | BPF_DW:
1967-
emit_atomic(rd, rs, off, imm,
1968-
BPF_SIZE(code) == BPF_DW, ctx);
2028+
if (bpf_atomic_is_load_store(insn))
2029+
ret = emit_atomic_ld_st(rd, rs, off, imm, code, ctx);
2030+
else
2031+
ret = emit_atomic_rmw(rd, rs, off, imm, code, ctx);
2032+
if (ret)
2033+
return ret;
19692034
break;
19702035

19712036
case BPF_STX | BPF_PROBE_MEM32 | BPF_B:

0 commit comments

Comments
 (0)