Skip to content

Commit d503a04

Browse files
Alexei StarovoitovMartin KaFai Lau
authored andcommitted
bpf: Add support for certain atomics in bpf_arena to x86 JIT
Support atomics in bpf_arena that can be JITed as a single x86 instruction. Instructions that are JITed as loops are not supported at the moment, since they require more complex extable and loop logic. JITs can choose to do smarter things with bpf_jit_supports_insn(). Like arm64 may decide to support all bpf atomics instructions when emit_lse_atomic is available and none in ll_sc mode. bpf_jit_supports_percpu_insn(), bpf_jit_supports_ptr_xchg() and other such callbacks can be replaced with bpf_jit_supports_insn() in the future. Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin KaFai Lau <[email protected]>
1 parent bb761fc commit d503a04

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

arch/x86/net/bpf_jit_comp.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,54 @@ static int emit_atomic(u8 **pprog, u8 atomic_op,
11721172
return 0;
11731173
}
11741174

1175+
static int emit_atomic_index(u8 **pprog, u8 atomic_op, u32 size,
1176+
u32 dst_reg, u32 src_reg, u32 index_reg, int off)
1177+
{
1178+
u8 *prog = *pprog;
1179+
1180+
EMIT1(0xF0); /* lock prefix */
1181+
switch (size) {
1182+
case BPF_W:
1183+
EMIT1(add_3mod(0x40, dst_reg, src_reg, index_reg));
1184+
break;
1185+
case BPF_DW:
1186+
EMIT1(add_3mod(0x48, dst_reg, src_reg, index_reg));
1187+
break;
1188+
default:
1189+
pr_err("bpf_jit: 1 and 2 byte atomics are not supported\n");
1190+
return -EFAULT;
1191+
}
1192+
1193+
/* emit opcode */
1194+
switch (atomic_op) {
1195+
case BPF_ADD:
1196+
case BPF_AND:
1197+
case BPF_OR:
1198+
case BPF_XOR:
1199+
/* lock *(u32/u64*)(dst_reg + idx_reg + off) <op>= src_reg */
1200+
EMIT1(simple_alu_opcodes[atomic_op]);
1201+
break;
1202+
case BPF_ADD | BPF_FETCH:
1203+
/* src_reg = atomic_fetch_add(dst_reg + idx_reg + off, src_reg); */
1204+
EMIT2(0x0F, 0xC1);
1205+
break;
1206+
case BPF_XCHG:
1207+
/* src_reg = atomic_xchg(dst_reg + idx_reg + off, src_reg); */
1208+
EMIT1(0x87);
1209+
break;
1210+
case BPF_CMPXCHG:
1211+
/* r0 = atomic_cmpxchg(dst_reg + idx_reg + off, r0, src_reg); */
1212+
EMIT2(0x0F, 0xB1);
1213+
break;
1214+
default:
1215+
pr_err("bpf_jit: unknown atomic opcode %02x\n", atomic_op);
1216+
return -EFAULT;
1217+
}
1218+
emit_insn_suffix_SIB(&prog, dst_reg, src_reg, index_reg, off);
1219+
*pprog = prog;
1220+
return 0;
1221+
}
1222+
11751223
#define DONT_CLEAR 1
11761224

11771225
bool ex_handler_bpf(const struct exception_table_entry *x, struct pt_regs *regs)
@@ -1982,6 +2030,15 @@ st: if (is_imm8(insn->off))
19822030
return err;
19832031
break;
19842032

2033+
case BPF_STX | BPF_PROBE_ATOMIC | BPF_W:
2034+
case BPF_STX | BPF_PROBE_ATOMIC | BPF_DW:
2035+
start_of_ldx = prog;
2036+
err = emit_atomic_index(&prog, insn->imm, BPF_SIZE(insn->code),
2037+
dst_reg, src_reg, X86_REG_R12, insn->off);
2038+
if (err)
2039+
return err;
2040+
goto populate_extable;
2041+
19852042
/* call */
19862043
case BPF_JMP | BPF_CALL: {
19872044
int offs;
@@ -3486,6 +3543,21 @@ bool bpf_jit_supports_arena(void)
34863543
return true;
34873544
}
34883545

3546+
bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
3547+
{
3548+
if (!in_arena)
3549+
return true;
3550+
switch (insn->code) {
3551+
case BPF_STX | BPF_ATOMIC | BPF_W:
3552+
case BPF_STX | BPF_ATOMIC | BPF_DW:
3553+
if (insn->imm == (BPF_AND | BPF_FETCH) ||
3554+
insn->imm == (BPF_OR | BPF_FETCH) ||
3555+
insn->imm == (BPF_XOR | BPF_FETCH))
3556+
return false;
3557+
}
3558+
return true;
3559+
}
3560+
34893561
bool bpf_jit_supports_ptr_xchg(void)
34903562
{
34913563
return true;

include/linux/filter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ struct ctl_table_header;
7575
/* unused opcode to mark special load instruction. Same as BPF_MSH */
7676
#define BPF_PROBE_MEM32 0xa0
7777

78+
/* unused opcode to mark special atomic instruction */
79+
#define BPF_PROBE_ATOMIC 0xe0
80+
7881
/* unused opcode to mark call to interpreter with arguments */
7982
#define BPF_CALL_ARGS 0xe0
8083

@@ -997,6 +1000,7 @@ bool bpf_jit_supports_far_kfunc_call(void);
9971000
bool bpf_jit_supports_exceptions(void);
9981001
bool bpf_jit_supports_ptr_xchg(void);
9991002
bool bpf_jit_supports_arena(void);
1003+
bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena);
10001004
void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie);
10011005
bool bpf_helper_changes_pkt_data(void *func);
10021006

kernel/bpf/core.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2965,6 +2965,11 @@ bool __weak bpf_jit_supports_arena(void)
29652965
return false;
29662966
}
29672967

2968+
bool __weak bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
2969+
{
2970+
return false;
2971+
}
2972+
29682973
/* Return TRUE if the JIT backend satisfies the following two conditions:
29692974
* 1) JIT backend supports atomic_xchg() on pointer-sized words.
29702975
* 2) Under the specific arch, the implementation of xchg() is the same

kernel/bpf/verifier.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6970,6 +6970,9 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
69706970
return err;
69716971
}
69726972

6973+
static int save_aux_ptr_type(struct bpf_verifier_env *env, enum bpf_reg_type type,
6974+
bool allow_trust_missmatch);
6975+
69736976
static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_insn *insn)
69746977
{
69756978
int load_reg;
@@ -7030,7 +7033,7 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
70307033
is_pkt_reg(env, insn->dst_reg) ||
70317034
is_flow_key_reg(env, insn->dst_reg) ||
70327035
is_sk_reg(env, insn->dst_reg) ||
7033-
is_arena_reg(env, insn->dst_reg)) {
7036+
(is_arena_reg(env, insn->dst_reg) && !bpf_jit_supports_insn(insn, true))) {
70347037
verbose(env, "BPF_ATOMIC stores into R%d %s is not allowed\n",
70357038
insn->dst_reg,
70367039
reg_type_str(env, reg_state(env, insn->dst_reg)->type));
@@ -7066,6 +7069,11 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
70667069
if (err)
70677070
return err;
70687071

7072+
if (is_arena_reg(env, insn->dst_reg)) {
7073+
err = save_aux_ptr_type(env, PTR_TO_ARENA, false);
7074+
if (err)
7075+
return err;
7076+
}
70697077
/* Check whether we can write into the same memory. */
70707078
err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
70717079
BPF_SIZE(insn->code), BPF_WRITE, -1, true, false);
@@ -18955,6 +18963,12 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
1895518963
insn->code == (BPF_ST | BPF_MEM | BPF_W) ||
1895618964
insn->code == (BPF_ST | BPF_MEM | BPF_DW)) {
1895718965
type = BPF_WRITE;
18966+
} else if ((insn->code == (BPF_STX | BPF_ATOMIC | BPF_W) ||
18967+
insn->code == (BPF_STX | BPF_ATOMIC | BPF_DW)) &&
18968+
env->insn_aux_data[i + delta].ptr_type == PTR_TO_ARENA) {
18969+
insn->code = BPF_STX | BPF_PROBE_ATOMIC | BPF_SIZE(insn->code);
18970+
env->prog->aux->num_exentries++;
18971+
continue;
1895818972
} else {
1895918973
continue;
1896018974
}
@@ -19226,6 +19240,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
1922619240
BPF_CLASS(insn->code) == BPF_ST) &&
1922719241
BPF_MODE(insn->code) == BPF_PROBE_MEM32)
1922819242
num_exentries++;
19243+
if (BPF_CLASS(insn->code) == BPF_STX &&
19244+
BPF_MODE(insn->code) == BPF_PROBE_ATOMIC)
19245+
num_exentries++;
1922919246
}
1923019247
func[i]->aux->num_exentries = num_exentries;
1923119248
func[i]->aux->tail_call_reachable = env->subprog_info[i].tail_call_reachable;

0 commit comments

Comments
 (0)