Skip to content

Commit 7bdbf74

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
bpf: add special internal-only MOV instruction to resolve per-CPU addrs
Add a new BPF instruction for resolving absolute addresses of per-CPU data from their per-CPU offsets. This instruction is internal-only and users are not allowed to use them directly. They will only be used for internal inlining optimizations for now between BPF verifier and BPF JITs. We use a special BPF_MOV | BPF_ALU64 | BPF_X form with insn->off field set to BPF_ADDR_PERCPU = -1. I used negative offset value to distinguish them from positive ones used by user-exposed instructions. Such instruction performs a resolution of a per-CPU offset stored in a register to a valid kernel address which can be dereferenced. It is useful in any use case where absolute address of a per-CPU data has to be resolved (e.g., in inlining bpf_map_lookup_elem()). BPF disassembler is also taught to recognize them to support dumping final BPF assembly code (non-JIT'ed version). Add arch-specific way for BPF JITs to mark support for this instructions. This patch also adds support for these instructions in x86-64 BPF JIT. Signed-off-by: Andrii Nakryiko <[email protected]> Acked-by: John Fastabend <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 2e11424 commit 7bdbf74

File tree

4 files changed

+55
-0
lines changed

4 files changed

+55
-0
lines changed

arch/x86/net/bpf_jit_comp.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,17 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
13821382
maybe_emit_mod(&prog, AUX_REG, dst_reg, true);
13831383
EMIT3(0x0F, 0x44, add_2reg(0xC0, AUX_REG, dst_reg));
13841384
break;
1385+
} else if (insn_is_mov_percpu_addr(insn)) {
1386+
u32 off = (u32)(unsigned long)&this_cpu_off;
1387+
1388+
/* mov <dst>, <src> (if necessary) */
1389+
EMIT_mov(dst_reg, src_reg);
1390+
1391+
/* add <dst>, gs:[<off>] */
1392+
EMIT2(0x65, add_1mod(0x48, dst_reg));
1393+
EMIT3(0x03, add_1reg(0x04, dst_reg), 0x25);
1394+
EMIT(off, 4);
1395+
break;
13851396
}
13861397
fallthrough;
13871398
case BPF_ALU | BPF_MOV | BPF_X:
@@ -3365,6 +3376,11 @@ bool bpf_jit_supports_subprog_tailcalls(void)
33653376
return true;
33663377
}
33673378

3379+
bool bpf_jit_supports_percpu_insn(void)
3380+
{
3381+
return true;
3382+
}
3383+
33683384
void bpf_jit_free(struct bpf_prog *prog)
33693385
{
33703386
if (prog->jited) {

include/linux/filter.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,25 @@ struct ctl_table_header;
178178
.off = 0, \
179179
.imm = 0 })
180180

181+
/* Special (internal-only) form of mov, used to resolve per-CPU addrs:
182+
* dst_reg = src_reg + <percpu_base_off>
183+
* BPF_ADDR_PERCPU is used as a special insn->off value.
184+
*/
185+
#define BPF_ADDR_PERCPU (-1)
186+
187+
#define BPF_MOV64_PERCPU_REG(DST, SRC) \
188+
((struct bpf_insn) { \
189+
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
190+
.dst_reg = DST, \
191+
.src_reg = SRC, \
192+
.off = BPF_ADDR_PERCPU, \
193+
.imm = 0 })
194+
195+
static inline bool insn_is_mov_percpu_addr(const struct bpf_insn *insn)
196+
{
197+
return insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) && insn->off == BPF_ADDR_PERCPU;
198+
}
199+
181200
/* Short form of mov, dst_reg = imm32 */
182201

183202
#define BPF_MOV64_IMM(DST, IMM) \
@@ -972,6 +991,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog);
972991
void bpf_jit_compile(struct bpf_prog *prog);
973992
bool bpf_jit_needs_zext(void);
974993
bool bpf_jit_supports_subprog_tailcalls(void);
994+
bool bpf_jit_supports_percpu_insn(void);
975995
bool bpf_jit_supports_kfunc_call(void);
976996
bool bpf_jit_supports_far_kfunc_call(void);
977997
bool bpf_jit_supports_exceptions(void);

kernel/bpf/core.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2945,6 +2945,11 @@ bool __weak bpf_jit_supports_subprog_tailcalls(void)
29452945
return false;
29462946
}
29472947

2948+
bool __weak bpf_jit_supports_percpu_insn(void)
2949+
{
2950+
return false;
2951+
}
2952+
29482953
bool __weak bpf_jit_supports_kfunc_call(void)
29492954
{
29502955
return false;

kernel/bpf/disasm.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,17 @@ static bool is_addr_space_cast(const struct bpf_insn *insn)
172172
insn->off == BPF_ADDR_SPACE_CAST;
173173
}
174174

175+
/* Special (internal-only) form of mov, used to resolve per-CPU addrs:
176+
* dst_reg = src_reg + <percpu_base_off>
177+
* BPF_ADDR_PERCPU is used as a special insn->off value.
178+
*/
179+
#define BPF_ADDR_PERCPU (-1)
180+
181+
static inline bool is_mov_percpu_addr(const struct bpf_insn *insn)
182+
{
183+
return insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) && insn->off == BPF_ADDR_PERCPU;
184+
}
185+
175186
void print_bpf_insn(const struct bpf_insn_cbs *cbs,
176187
const struct bpf_insn *insn,
177188
bool allow_ptr_leaks)
@@ -194,6 +205,9 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
194205
verbose(cbs->private_data, "(%02x) r%d = addr_space_cast(r%d, %d, %d)\n",
195206
insn->code, insn->dst_reg,
196207
insn->src_reg, ((u32)insn->imm) >> 16, (u16)insn->imm);
208+
} else if (is_mov_percpu_addr(insn)) {
209+
verbose(cbs->private_data, "(%02x) r%d = &(void __percpu *)(r%d)\n",
210+
insn->code, insn->dst_reg, insn->src_reg);
197211
} else if (BPF_SRC(insn->code) == BPF_X) {
198212
verbose(cbs->private_data, "(%02x) %c%d %s %s%c%d\n",
199213
insn->code, class == BPF_ALU ? 'w' : 'r',

0 commit comments

Comments
 (0)