Skip to content

Commit 4ab17e7

Browse files
chenhengqichenhuacai
authored andcommitted
LoongArch: BPF: Use BPF prog pack allocator
Use bpf_jit_binary_pack_alloc() for BPF JIT binaries. The BPF prog pack allocator creates a pair of RW and RX buffers. The BPF JIT writes the program into the RW buffer. When the JIT is done, the program is copied to the final RX buffer with bpf_jit_binary_pack_finalize(). Acked-by: Tiezhu Yang <yangtiezhu@loongson.cn> Tested-by: Vincent Li <vincent.mc.li@gmail.com> Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
1 parent f5db714 commit 4ab17e7

2 files changed

Lines changed: 83 additions & 37 deletions

File tree

arch/loongarch/net/bpf_jit.c

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,10 @@ static int add_exception_handler(const struct bpf_insn *insn,
461461
int dst_reg)
462462
{
463463
unsigned long pc;
464-
off_t offset;
464+
off_t ins_offset, fixup_offset;
465465
struct exception_table_entry *ex;
466466

467-
if (!ctx->image || !ctx->prog->aux->extable)
467+
if (!ctx->image || !ctx->ro_image || !ctx->prog->aux->extable)
468468
return 0;
469469

470470
if (BPF_MODE(insn->code) != BPF_PROBE_MEM &&
@@ -475,28 +475,42 @@ static int add_exception_handler(const struct bpf_insn *insn,
475475
return -EINVAL;
476476

477477
ex = &ctx->prog->aux->extable[ctx->num_exentries];
478-
pc = (unsigned long)&ctx->image[ctx->idx - 1];
478+
pc = (unsigned long)&ctx->ro_image[ctx->idx - 1];
479479

480-
offset = pc - (long)&ex->insn;
481-
if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN))
480+
/*
481+
* This is the relative offset of the instruction that may fault from
482+
* the exception table itself. This will be written to the exception
483+
* table and if this instruction faults, the destination register will
484+
* be set to '0' and the execution will jump to the next instruction.
485+
*/
486+
ins_offset = pc - (long)&ex->insn;
487+
if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN))
482488
return -ERANGE;
483489

484-
ex->insn = offset;
485-
486490
/*
487491
* Since the extable follows the program, the fixup offset is always
488492
* negative and limited to BPF_JIT_REGION_SIZE. Store a positive value
489493
* to keep things simple, and put the destination register in the upper
490494
* bits. We don't need to worry about buildtime or runtime sort
491495
* modifying the upper bits because the table is already sorted, and
492496
* isn't part of the main exception table.
497+
*
498+
* The fixup_offset is set to the next instruction from the instruction
499+
* that may fault. The execution will jump to this after handling the fault.
493500
*/
494-
offset = (long)&ex->fixup - (pc + LOONGARCH_INSN_SIZE);
495-
if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset))
501+
fixup_offset = (long)&ex->fixup - (pc + LOONGARCH_INSN_SIZE);
502+
if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset))
496503
return -ERANGE;
497504

505+
/*
506+
* The offsets above have been calculated using the RO buffer but we
507+
* need to use the R/W buffer for writes. Switch ex to rw buffer for writing.
508+
*/
509+
ex = (void *)ctx->image + ((void *)ex - (void *)ctx->ro_image);
510+
ex->insn = ins_offset;
511+
ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) |
512+
FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg);
498513
ex->type = EX_TYPE_BPF;
499-
ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg);
500514

501515
ctx->num_exentries++;
502516

@@ -1829,11 +1843,12 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
18291843
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
18301844
{
18311845
bool tmp_blinded = false, extra_pass = false;
1832-
u8 *image_ptr;
1846+
u8 *image_ptr, *ro_image_ptr;
18331847
int image_size, prog_size, extable_size;
18341848
struct jit_ctx ctx;
18351849
struct jit_data *jit_data;
18361850
struct bpf_binary_header *header;
1851+
struct bpf_binary_header *ro_header;
18371852
struct bpf_prog *tmp, *orig_prog = prog;
18381853

18391854
/*
@@ -1868,8 +1883,10 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
18681883
}
18691884
if (jit_data->ctx.offset) {
18701885
ctx = jit_data->ctx;
1871-
image_ptr = jit_data->image;
1886+
ro_header = jit_data->ro_header;
1887+
ro_image_ptr = (void *)ctx.ro_image;
18721888
header = jit_data->header;
1889+
image_ptr = (void *)header + ((void *)ro_image_ptr - (void *)ro_header);
18731890
extra_pass = true;
18741891
prog_size = sizeof(u32) * ctx.idx;
18751892
goto skip_init_ctx;
@@ -1903,66 +1920,73 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
19031920
prog_size = sizeof(u32) * ctx.idx;
19041921
image_size = prog_size + extable_size;
19051922
/* Now we know the size of the structure to make */
1906-
header = bpf_jit_binary_alloc(image_size, &image_ptr,
1907-
sizeof(u32), jit_fill_hole);
1908-
if (header == NULL) {
1923+
ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr, sizeof(u32),
1924+
&header, &image_ptr, jit_fill_hole);
1925+
if (!ro_header) {
19091926
prog = orig_prog;
19101927
goto out_offset;
19111928
}
19121929

19131930
/* 2. Now, the actual pass to generate final JIT code */
1931+
/*
1932+
* Use the image (RW) for writing the JITed instructions. But also save
1933+
* the ro_image (RX) for calculating the offsets in the image. The RW
1934+
* image will be later copied to the RX image from where the program will
1935+
* run. The bpf_jit_binary_pack_finalize() will do this copy in the final
1936+
* step.
1937+
*/
19141938
ctx.image = (union loongarch_instruction *)image_ptr;
1939+
ctx.ro_image = (union loongarch_instruction *)ro_image_ptr;
19151940
if (extable_size)
1916-
prog->aux->extable = (void *)image_ptr + prog_size;
1941+
prog->aux->extable = (void *)ro_image_ptr + prog_size;
19171942

19181943
skip_init_ctx:
19191944
ctx.idx = 0;
19201945
ctx.num_exentries = 0;
19211946

19221947
build_prologue(&ctx);
19231948
if (build_body(&ctx, extra_pass)) {
1924-
bpf_jit_binary_free(header);
19251949
prog = orig_prog;
1926-
goto out_offset;
1950+
goto out_free;
19271951
}
19281952
build_epilogue(&ctx);
19291953

19301954
/* 3. Extra pass to validate JITed code */
19311955
if (validate_ctx(&ctx)) {
1932-
bpf_jit_binary_free(header);
19331956
prog = orig_prog;
1934-
goto out_offset;
1957+
goto out_free;
19351958
}
19361959

19371960
/* And we're done */
19381961
if (bpf_jit_enable > 1)
19391962
bpf_jit_dump(prog->len, prog_size, 2, ctx.image);
19401963

1941-
/* Update the icache */
1942-
flush_icache_range((unsigned long)header, (unsigned long)(ctx.image + ctx.idx));
1943-
19441964
if (!prog->is_func || extra_pass) {
1945-
int err;
1946-
19471965
if (extra_pass && ctx.idx != jit_data->ctx.idx) {
19481966
pr_err_once("multi-func JIT bug %d != %d\n",
19491967
ctx.idx, jit_data->ctx.idx);
19501968
goto out_free;
19511969
}
1952-
err = bpf_jit_binary_lock_ro(header);
1953-
if (err) {
1954-
pr_err_once("bpf_jit_binary_lock_ro() returned %d\n",
1955-
err);
1970+
if (WARN_ON(bpf_jit_binary_pack_finalize(ro_header, header))) {
1971+
/* ro_header has been freed */
1972+
ro_header = NULL;
1973+
prog = orig_prog;
19561974
goto out_free;
19571975
}
1976+
/*
1977+
* The instructions have now been copied to the ROX region from
1978+
* where they will execute. Now the data cache has to be cleaned
1979+
* to the PoU and the I-cache has to be invalidated for the VAs.
1980+
*/
1981+
bpf_flush_icache(ro_header, ctx.ro_image + ctx.idx);
19581982
} else {
19591983
jit_data->ctx = ctx;
1960-
jit_data->image = image_ptr;
19611984
jit_data->header = header;
1985+
jit_data->ro_header = ro_header;
19621986
}
19631987
prog->jited = 1;
19641988
prog->jited_len = prog_size;
1965-
prog->bpf_func = (void *)ctx.image;
1989+
prog->bpf_func = (void *)ctx.ro_image;
19661990

19671991
if (!prog->is_func || extra_pass) {
19681992
int i;
@@ -1982,17 +2006,39 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
19822006
if (tmp_blinded)
19832007
bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog);
19842008

1985-
19862009
return prog;
19872010

19882011
out_free:
1989-
bpf_jit_binary_free(header);
1990-
prog->bpf_func = NULL;
1991-
prog->jited = 0;
1992-
prog->jited_len = 0;
2012+
if (header) {
2013+
bpf_arch_text_copy(&ro_header->size, &header->size, sizeof(header->size));
2014+
bpf_jit_binary_pack_free(ro_header, header);
2015+
}
19932016
goto out_offset;
19942017
}
19952018

2019+
void bpf_jit_free(struct bpf_prog *prog)
2020+
{
2021+
if (prog->jited) {
2022+
struct jit_data *jit_data = prog->aux->jit_data;
2023+
struct bpf_binary_header *hdr;
2024+
2025+
/*
2026+
* If we fail the final pass of JIT (from jit_subprogs), the
2027+
* program may not be finalized yet. Call finalize here before
2028+
* freeing it.
2029+
*/
2030+
if (jit_data) {
2031+
bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header);
2032+
kfree(jit_data);
2033+
}
2034+
hdr = bpf_jit_binary_pack_hdr(prog);
2035+
bpf_jit_binary_pack_free(hdr, NULL);
2036+
WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
2037+
}
2038+
2039+
bpf_prog_unlock_free(prog);
2040+
}
2041+
19962042
bool bpf_jit_bypass_spec_v1(void)
19972043
{
19982044
return true;

arch/loongarch/net/bpf_jit.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct jit_ctx {
2424

2525
struct jit_data {
2626
struct bpf_binary_header *header;
27-
u8 *image;
27+
struct bpf_binary_header *ro_header;
2828
struct jit_ctx ctx;
2929
};
3030

0 commit comments

Comments
 (0)