Skip to content

Commit 09772d9

Browse files
borkmannAlexei Starovoitov
authored andcommitted
bpf: avoid retpoline for lookup/update/delete calls on maps
While some of the BPF map lookup helpers provide a ->map_gen_lookup() callback for inlining the map lookup altogether it is not available for every map, so the remaining ones have to call bpf_map_lookup_elem() helper which does a dispatch to map->ops->map_lookup_elem(). In times of retpolines, this will control and trap speculative execution rather than letting it do its work for the indirect call and will therefore cause a slowdown. Likewise, bpf_map_update_elem() and bpf_map_delete_elem() do not have an inlined version and need to call into their map->ops->map_update_elem() resp. map->ops->map_delete_elem() handlers. Before: # bpftool prog dump xlated id 1 0: (bf) r2 = r10 1: (07) r2 += -8 2: (7a) *(u64 *)(r2 +0) = 0 3: (18) r1 = map[id:1] 5: (85) call __htab_map_lookup_elem#232656 6: (15) if r0 == 0x0 goto pc+4 7: (71) r1 = *(u8 *)(r0 +35) 8: (55) if r1 != 0x0 goto pc+1 9: (72) *(u8 *)(r0 +35) = 1 10: (07) r0 += 56 11: (15) if r0 == 0x0 goto pc+4 12: (bf) r2 = r0 13: (18) r1 = map[id:1] 15: (85) call bpf_map_delete_elem#215008 <-- indirect call via 16: (95) exit helper After: # bpftool prog dump xlated id 1 0: (bf) r2 = r10 1: (07) r2 += -8 2: (7a) *(u64 *)(r2 +0) = 0 3: (18) r1 = map[id:1] 5: (85) call __htab_map_lookup_elem#233328 6: (15) if r0 == 0x0 goto pc+4 7: (71) r1 = *(u8 *)(r0 +35) 8: (55) if r1 != 0x0 goto pc+1 9: (72) *(u8 *)(r0 +35) = 1 10: (07) r0 += 56 11: (15) if r0 == 0x0 goto pc+4 12: (bf) r2 = r0 13: (18) r1 = map[id:1] 15: (85) call htab_lru_map_delete_elem#238240 <-- direct call 16: (95) exit In all three lookup/update/delete cases however we can use the actual address of the map callback directly if we find that there's only a single path with a map pointer leading to the helper call, meaning when the map pointer has not been poisoned from verifier side. Example code can be seen above for the delete case. Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Song Liu <songliubraving@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 4316b40 commit 09772d9

File tree

3 files changed

+61
-22
lines changed

3 files changed

+61
-22
lines changed

include/linux/filter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ struct xdp_buff;
301301

302302
/* Function call */
303303

304+
#define BPF_CAST_CALL(x) \
305+
((u64 (*)(u64, u64, u64, u64, u64))(x))
306+
304307
#define BPF_EMIT_CALL(FUNC) \
305308
((struct bpf_insn) { \
306309
.code = BPF_JMP | BPF_CALL, \

kernel/bpf/hashtab.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,9 @@ static u32 htab_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
503503
struct bpf_insn *insn = insn_buf;
504504
const int ret = BPF_REG_0;
505505

506-
*insn++ = BPF_EMIT_CALL((u64 (*)(u64, u64, u64, u64, u64))__htab_map_lookup_elem);
506+
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
507+
(void *(*)(struct bpf_map *map, void *key))NULL));
508+
*insn++ = BPF_EMIT_CALL(BPF_CAST_CALL(__htab_map_lookup_elem));
507509
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 1);
508510
*insn++ = BPF_ALU64_IMM(BPF_ADD, ret,
509511
offsetof(struct htab_elem, key) +
@@ -530,7 +532,9 @@ static u32 htab_lru_map_gen_lookup(struct bpf_map *map,
530532
const int ret = BPF_REG_0;
531533
const int ref_reg = BPF_REG_1;
532534

533-
*insn++ = BPF_EMIT_CALL((u64 (*)(u64, u64, u64, u64, u64))__htab_map_lookup_elem);
535+
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
536+
(void *(*)(struct bpf_map *map, void *key))NULL));
537+
*insn++ = BPF_EMIT_CALL(BPF_CAST_CALL(__htab_map_lookup_elem));
534538
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 4);
535539
*insn++ = BPF_LDX_MEM(BPF_B, ref_reg, ret,
536540
offsetof(struct htab_elem, lru_node) +
@@ -1369,7 +1373,9 @@ static u32 htab_of_map_gen_lookup(struct bpf_map *map,
13691373
struct bpf_insn *insn = insn_buf;
13701374
const int ret = BPF_REG_0;
13711375

1372-
*insn++ = BPF_EMIT_CALL((u64 (*)(u64, u64, u64, u64, u64))__htab_map_lookup_elem);
1376+
BUILD_BUG_ON(!__same_type(&__htab_map_lookup_elem,
1377+
(void *(*)(struct bpf_map *map, void *key))NULL));
1378+
*insn++ = BPF_EMIT_CALL(BPF_CAST_CALL(__htab_map_lookup_elem));
13731379
*insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 2);
13741380
*insn++ = BPF_ALU64_IMM(BPF_ADD, ret,
13751381
offsetof(struct htab_elem, key) +

kernel/bpf/verifier.c

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2421,8 +2421,11 @@ record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
24212421
struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
24222422

24232423
if (func_id != BPF_FUNC_tail_call &&
2424-
func_id != BPF_FUNC_map_lookup_elem)
2424+
func_id != BPF_FUNC_map_lookup_elem &&
2425+
func_id != BPF_FUNC_map_update_elem &&
2426+
func_id != BPF_FUNC_map_delete_elem)
24252427
return 0;
2428+
24262429
if (meta->map_ptr == NULL) {
24272430
verbose(env, "kernel subsystem misconfigured verifier\n");
24282431
return -EINVAL;
@@ -5586,6 +5589,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
55865589
struct bpf_insn *insn = prog->insnsi;
55875590
const struct bpf_func_proto *fn;
55885591
const int insn_cnt = prog->len;
5592+
const struct bpf_map_ops *ops;
55895593
struct bpf_insn_aux_data *aux;
55905594
struct bpf_insn insn_buf[16];
55915595
struct bpf_prog *new_prog;
@@ -5715,35 +5719,61 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
57155719
}
57165720

57175721
/* BPF_EMIT_CALL() assumptions in some of the map_gen_lookup
5718-
* handlers are currently limited to 64 bit only.
5722+
* and other inlining handlers are currently limited to 64 bit
5723+
* only.
57195724
*/
57205725
if (prog->jit_requested && BITS_PER_LONG == 64 &&
5721-
insn->imm == BPF_FUNC_map_lookup_elem) {
5726+
(insn->imm == BPF_FUNC_map_lookup_elem ||
5727+
insn->imm == BPF_FUNC_map_update_elem ||
5728+
insn->imm == BPF_FUNC_map_delete_elem)) {
57225729
aux = &env->insn_aux_data[i + delta];
57235730
if (bpf_map_ptr_poisoned(aux))
57245731
goto patch_call_imm;
57255732

57265733
map_ptr = BPF_MAP_PTR(aux->map_state);
5727-
if (!map_ptr->ops->map_gen_lookup)
5728-
goto patch_call_imm;
5734+
ops = map_ptr->ops;
5735+
if (insn->imm == BPF_FUNC_map_lookup_elem &&
5736+
ops->map_gen_lookup) {
5737+
cnt = ops->map_gen_lookup(map_ptr, insn_buf);
5738+
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
5739+
verbose(env, "bpf verifier is misconfigured\n");
5740+
return -EINVAL;
5741+
}
57295742

5730-
cnt = map_ptr->ops->map_gen_lookup(map_ptr, insn_buf);
5731-
if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {
5732-
verbose(env, "bpf verifier is misconfigured\n");
5733-
return -EINVAL;
5734-
}
5743+
new_prog = bpf_patch_insn_data(env, i + delta,
5744+
insn_buf, cnt);
5745+
if (!new_prog)
5746+
return -ENOMEM;
57355747

5736-
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
5737-
cnt);
5738-
if (!new_prog)
5739-
return -ENOMEM;
5748+
delta += cnt - 1;
5749+
env->prog = prog = new_prog;
5750+
insn = new_prog->insnsi + i + delta;
5751+
continue;
5752+
}
57405753

5741-
delta += cnt - 1;
5754+
BUILD_BUG_ON(!__same_type(ops->map_lookup_elem,
5755+
(void *(*)(struct bpf_map *map, void *key))NULL));
5756+
BUILD_BUG_ON(!__same_type(ops->map_delete_elem,
5757+
(int (*)(struct bpf_map *map, void *key))NULL));
5758+
BUILD_BUG_ON(!__same_type(ops->map_update_elem,
5759+
(int (*)(struct bpf_map *map, void *key, void *value,
5760+
u64 flags))NULL));
5761+
switch (insn->imm) {
5762+
case BPF_FUNC_map_lookup_elem:
5763+
insn->imm = BPF_CAST_CALL(ops->map_lookup_elem) -
5764+
__bpf_call_base;
5765+
continue;
5766+
case BPF_FUNC_map_update_elem:
5767+
insn->imm = BPF_CAST_CALL(ops->map_update_elem) -
5768+
__bpf_call_base;
5769+
continue;
5770+
case BPF_FUNC_map_delete_elem:
5771+
insn->imm = BPF_CAST_CALL(ops->map_delete_elem) -
5772+
__bpf_call_base;
5773+
continue;
5774+
}
57425775

5743-
/* keep walking new program and skip insns we just inserted */
5744-
env->prog = prog = new_prog;
5745-
insn = new_prog->insnsi + i + delta;
5746-
continue;
5776+
goto patch_call_imm;
57475777
}
57485778

57495779
if (insn->imm == BPF_FUNC_redirect_map) {

0 commit comments

Comments
 (0)