Skip to content

Commit bfc54e8

Browse files
peilin-yeKernel Patches Daemon
authored andcommitted
bpf, arm64: Support load-acquire and store-release instructions
Support BPF load-acquire (BPF_LOAD_ACQ) and store-release (BPF_STORE_REL) instructions in the arm64 JIT compiler. For example (assuming little-endian): db 10 00 00 00 01 00 00 r0 = 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 would emit an LDAR instruction for the above, e.g.: ldar x7, [x0] 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 An STLRH instruction would be emitted, e.g.: stlrh w1, [x0] For a complete mapping: load-acquire 8-bit LDARB (BPF_LOAD_ACQ) 16-bit LDARH 32-bit LDAR (32-bit) 64-bit LDAR (64-bit) store-release 8-bit STLRB (BPF_STORE_REL) 16-bit STLRH 32-bit STLR (32-bit) 64-bit STLR (64-bit) Arena accesses are supported. bpf_jit_supports_insn(..., /*in_arena=*/true) always returns true for BPF_LOAD_ACQ and BPF_STORE_REL instructions, as they don't depend on ARM64_HAS_LSE_ATOMICS. Signed-off-by: Peilin Ye <[email protected]> Acked-by: Xu Kuohai <[email protected]>
1 parent da99b2a commit bfc54e8

File tree

2 files changed

+105
-6
lines changed

2 files changed

+105
-6
lines changed

arch/arm64/net/bpf_jit.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,26 @@
119119
aarch64_insn_gen_load_store_ex(Rt, Rn, Rs, A64_SIZE(sf), \
120120
AARCH64_INSN_LDST_STORE_REL_EX)
121121

122+
/* Load-acquire & store-release */
123+
#define A64_LDAR(Rt, Rn, size) \
124+
aarch64_insn_gen_load_acq_store_rel(Rt, Rn, AARCH64_INSN_SIZE_##size, \
125+
AARCH64_INSN_LDST_LOAD_ACQ)
126+
#define A64_STLR(Rt, Rn, size) \
127+
aarch64_insn_gen_load_acq_store_rel(Rt, Rn, AARCH64_INSN_SIZE_##size, \
128+
AARCH64_INSN_LDST_STORE_REL)
129+
130+
/* Rt = [Rn] (load acquire) */
131+
#define A64_LDARB(Wt, Xn) A64_LDAR(Wt, Xn, 8)
132+
#define A64_LDARH(Wt, Xn) A64_LDAR(Wt, Xn, 16)
133+
#define A64_LDAR32(Wt, Xn) A64_LDAR(Wt, Xn, 32)
134+
#define A64_LDAR64(Xt, Xn) A64_LDAR(Xt, Xn, 64)
135+
136+
/* [Rn] = Rt (store release) */
137+
#define A64_STLRB(Wt, Xn) A64_STLR(Wt, Xn, 8)
138+
#define A64_STLRH(Wt, Xn) A64_STLR(Wt, Xn, 16)
139+
#define A64_STLR32(Wt, Xn) A64_STLR(Wt, Xn, 32)
140+
#define A64_STLR64(Xt, Xn) A64_STLR(Xt, Xn, 64)
141+
122142
/*
123143
* LSE atomics
124144
*

arch/arm64/net/bpf_jit_comp.c

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,82 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
647647
return 0;
648648
}
649649

650+
static int emit_atomic_load_store(const struct bpf_insn *insn,
651+
struct jit_ctx *ctx)
652+
{
653+
const s32 imm = insn->imm;
654+
const s16 off = insn->off;
655+
const u8 code = insn->code;
656+
const bool arena = BPF_MODE(code) == BPF_PROBE_ATOMIC;
657+
const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
658+
const u8 dst = bpf2a64[insn->dst_reg];
659+
const u8 src = bpf2a64[insn->src_reg];
660+
const u8 tmp = bpf2a64[TMP_REG_1];
661+
u8 reg;
662+
663+
switch (imm) {
664+
case BPF_LOAD_ACQ:
665+
reg = src;
666+
break;
667+
case BPF_STORE_REL:
668+
reg = dst;
669+
break;
670+
default:
671+
pr_err_once("unknown atomic load/store op code %02x\n", imm);
672+
return -EINVAL;
673+
}
674+
675+
if (off) {
676+
emit_a64_add_i(1, tmp, reg, tmp, off, ctx);
677+
reg = tmp;
678+
}
679+
if (arena) {
680+
emit(A64_ADD(1, tmp, reg, arena_vm_base), ctx);
681+
reg = tmp;
682+
}
683+
684+
switch (imm) {
685+
case BPF_LOAD_ACQ:
686+
switch (BPF_SIZE(code)) {
687+
case BPF_B:
688+
emit(A64_LDARB(dst, reg), ctx);
689+
break;
690+
case BPF_H:
691+
emit(A64_LDARH(dst, reg), ctx);
692+
break;
693+
case BPF_W:
694+
emit(A64_LDAR32(dst, reg), ctx);
695+
break;
696+
case BPF_DW:
697+
emit(A64_LDAR64(dst, reg), ctx);
698+
break;
699+
}
700+
break;
701+
case BPF_STORE_REL:
702+
switch (BPF_SIZE(code)) {
703+
case BPF_B:
704+
emit(A64_STLRB(src, reg), ctx);
705+
break;
706+
case BPF_H:
707+
emit(A64_STLRH(src, reg), ctx);
708+
break;
709+
case BPF_W:
710+
emit(A64_STLR32(src, reg), ctx);
711+
break;
712+
case BPF_DW:
713+
emit(A64_STLR64(src, reg), ctx);
714+
break;
715+
}
716+
break;
717+
default:
718+
pr_err_once("unexpected atomic load/store op code %02x\n",
719+
imm);
720+
return -EINVAL;
721+
}
722+
723+
return 0;
724+
}
725+
650726
#ifdef CONFIG_ARM64_LSE_ATOMICS
651727
static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx)
652728
{
@@ -1641,11 +1717,17 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
16411717
return ret;
16421718
break;
16431719

1720+
case BPF_STX | BPF_ATOMIC | BPF_B:
1721+
case BPF_STX | BPF_ATOMIC | BPF_H:
16441722
case BPF_STX | BPF_ATOMIC | BPF_W:
16451723
case BPF_STX | BPF_ATOMIC | BPF_DW:
1724+
case BPF_STX | BPF_PROBE_ATOMIC | BPF_B:
1725+
case BPF_STX | BPF_PROBE_ATOMIC | BPF_H:
16461726
case BPF_STX | BPF_PROBE_ATOMIC | BPF_W:
16471727
case BPF_STX | BPF_PROBE_ATOMIC | BPF_DW:
1648-
if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS))
1728+
if (bpf_atomic_is_load_store(insn))
1729+
ret = emit_atomic_load_store(insn, ctx);
1730+
else if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS))
16491731
ret = emit_lse_atomic(insn, ctx);
16501732
else
16511733
ret = emit_ll_sc_atomic(insn, ctx);
@@ -2667,13 +2749,10 @@ bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
26672749
if (!in_arena)
26682750
return true;
26692751
switch (insn->code) {
2670-
case BPF_STX | BPF_ATOMIC | BPF_B:
2671-
case BPF_STX | BPF_ATOMIC | BPF_H:
26722752
case BPF_STX | BPF_ATOMIC | BPF_W:
26732753
case BPF_STX | BPF_ATOMIC | BPF_DW:
2674-
if (bpf_atomic_is_load_store(insn))
2675-
return false;
2676-
if (!cpus_have_cap(ARM64_HAS_LSE_ATOMICS))
2754+
if (!bpf_atomic_is_load_store(insn) &&
2755+
!cpus_have_cap(ARM64_HAS_LSE_ATOMICS))
26772756
return false;
26782757
}
26792758
return true;

0 commit comments

Comments
 (0)