Use the LoongArch common memory access instructions with the barrier dbar to support the BPF load-acquire and store-release instructions. With this patch, the following testcases passed on LoongArch if the macro CAN_USE_LOAD_ACQ_STORE_REL is usable in bpf selftests: sudo ./test_progs -t verifier_load_acquire sudo ./test_progs -t verifier_store_release sudo ./test_progs -t verifier_precision/bpf_load_acquire sudo ./test_progs -t verifier_precision/bpf_store_release sudo ./test_progs -t compute_live_registers/atomic_load_acq_store_rel Signed-off-by: Tiezhu Yang --- arch/loongarch/net/bpf_jit.c | 101 ++++++++++++++++++++++++++++++++++- arch/loongarch/net/bpf_jit.h | 12 +++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index c9a32f124f5e..f18a2858123f 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -344,6 +344,102 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) #undef jmp_offset } +static int emit_atomic_ld_st(const struct bpf_insn *insn, struct jit_ctx *ctx) +{ + const u8 t1 = LOONGARCH_GPR_T1; + const u8 src = regmap[insn->src_reg]; + const u8 dst = regmap[insn->dst_reg]; + const s16 off = insn->off; + const s32 imm = insn->imm; + + switch (imm) { + /* dst_reg = load_acquire(src_reg + off16) */ + case BPF_LOAD_ACQ: + switch (BPF_SIZE(insn->code)) { + case BPF_B: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldb, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxb, dst, src, t1); + } + emit_zext_8(ctx, dst); + break; + case BPF_H: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldh, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxh, dst, src, t1); + } + emit_zext_16(ctx, dst); + break; + case BPF_W: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldw, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxw, dst, src, t1); + } + emit_zext_32(ctx, dst, true); + break; + case BPF_DW: + if (is_signed_imm12(off)) { + emit_insn(ctx, ldd, dst, src, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, ldxd, dst, src, t1); + } + break; + } + emit_insn(ctx, dbar, 0b10100); + break; + /* store_release(dst_reg + off16, src_reg) */ + case BPF_STORE_REL: + emit_insn(ctx, dbar, 0b10010); + switch (BPF_SIZE(insn->code)) { + case BPF_B: + if (is_signed_imm12(off)) { + emit_insn(ctx, stb, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxb, src, dst, t1); + } + break; + case BPF_H: + if (is_signed_imm12(off)) { + emit_insn(ctx, sth, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxh, src, dst, t1); + } + break; + case BPF_W: + if (is_signed_imm12(off)) { + emit_insn(ctx, stw, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxw, src, dst, t1); + } + break; + case BPF_DW: + if (is_signed_imm12(off)) { + emit_insn(ctx, std, src, dst, off); + } else { + move_imm(ctx, t1, off, false); + emit_insn(ctx, stxd, src, dst, t1); + } + break; + } + break; + default: + pr_err_once("bpf-jit: invalid atomic load/store opcode %02x\n", imm); + return -EINVAL; + } + + return 0; +} + static int emit_atomic_rmw(const struct bpf_insn *insn, struct jit_ctx *ctx) { const u8 t1 = LOONGARCH_GPR_T1; @@ -1326,7 +1422,10 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext case BPF_STX | BPF_ATOMIC | BPF_H: case BPF_STX | BPF_ATOMIC | BPF_W: case BPF_STX | BPF_ATOMIC | BPF_DW: - ret = emit_atomic_rmw(insn, ctx); + if (bpf_atomic_is_load_store(insn)) + ret = emit_atomic_ld_st(insn, ctx); + else + ret = emit_atomic_rmw(insn, ctx); if (ret) return ret; break; diff --git a/arch/loongarch/net/bpf_jit.h b/arch/loongarch/net/bpf_jit.h index a8e29be35fa8..9150de94ac60 100644 --- a/arch/loongarch/net/bpf_jit.h +++ b/arch/loongarch/net/bpf_jit.h @@ -72,6 +72,18 @@ static inline int epilogue_offset(const struct jit_ctx *ctx) return (to - from); } +/* Zero-extend 8 bits into 64 bits */ +static inline void emit_zext_8(struct jit_ctx *ctx, enum loongarch_gpr reg) +{ + emit_insn(ctx, bstrinsd, reg, LOONGARCH_GPR_ZERO, 63, 8); +} + +/* Zero-extend 16 bits into 64 bits */ +static inline void emit_zext_16(struct jit_ctx *ctx, enum loongarch_gpr reg) +{ + emit_insn(ctx, bstrinsd, reg, LOONGARCH_GPR_ZERO, 63, 16); +} + /* Zero-extend 32 bits into 64 bits */ static inline void emit_zext_32(struct jit_ctx *ctx, enum loongarch_gpr reg, bool is32) { -- 2.42.0