Skip to content

Commit 1dad391

Browse files
puranjaymohanAlexei Starovoitov
authored andcommitted
bpf, arm64: use bpf_prog_pack for memory management
Use bpf_jit_binary_pack_alloc for memory management of JIT binaries in ARM64 BPF JIT. The bpf_jit_binary_pack_alloc creates a pair of RW and RX buffers. The 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. Implement bpf_arch_text_copy() and bpf_arch_text_invalidate() for ARM64 JIT as these functions are required by bpf_jit_binary_pack allocator. Signed-off-by: Puranjay Mohan <puranjay12@gmail.com> Acked-by: Song Liu <song@kernel.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Link: https://lore.kernel.org/r/20240228141824.119877-3-puranjay12@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 451c3ca commit 1dad391

1 file changed

Lines changed: 115 additions & 24 deletions

File tree

arch/arm64/net/bpf_jit_comp.c

Lines changed: 115 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct jit_ctx {
7676
int *offset;
7777
int exentry_idx;
7878
__le32 *image;
79+
__le32 *ro_image;
7980
u32 stack_size;
8081
int fpb_offset;
8182
};
@@ -205,6 +206,14 @@ static void jit_fill_hole(void *area, unsigned int size)
205206
*ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT);
206207
}
207208

209+
int bpf_arch_text_invalidate(void *dst, size_t len)
210+
{
211+
if (!aarch64_insn_set(dst, AARCH64_BREAK_FAULT, len))
212+
return -EINVAL;
213+
214+
return 0;
215+
}
216+
208217
static inline int epilogue_offset(const struct jit_ctx *ctx)
209218
{
210219
int to = ctx->epilogue_offset;
@@ -746,7 +755,8 @@ static int add_exception_handler(const struct bpf_insn *insn,
746755
struct jit_ctx *ctx,
747756
int dst_reg)
748757
{
749-
off_t offset;
758+
off_t ins_offset;
759+
off_t fixup_offset;
750760
unsigned long pc;
751761
struct exception_table_entry *ex;
752762

@@ -763,12 +773,17 @@ static int add_exception_handler(const struct bpf_insn *insn,
763773
return -EINVAL;
764774

765775
ex = &ctx->prog->aux->extable[ctx->exentry_idx];
766-
pc = (unsigned long)&ctx->image[ctx->idx - 1];
776+
pc = (unsigned long)&ctx->ro_image[ctx->idx - 1];
767777

768-
offset = pc - (long)&ex->insn;
769-
if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN))
778+
/*
779+
* This is the relative offset of the instruction that may fault from
780+
* the exception table itself. This will be written to the exception
781+
* table and if this instruction faults, the destination register will
782+
* be set to '0' and the execution will jump to the next instruction.
783+
*/
784+
ins_offset = pc - (long)&ex->insn;
785+
if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN))
770786
return -ERANGE;
771-
ex->insn = offset;
772787

773788
/*
774789
* Since the extable follows the program, the fixup offset is always
@@ -777,12 +792,25 @@ static int add_exception_handler(const struct bpf_insn *insn,
777792
* bits. We don't need to worry about buildtime or runtime sort
778793
* modifying the upper bits because the table is already sorted, and
779794
* isn't part of the main exception table.
795+
*
796+
* The fixup_offset is set to the next instruction from the instruction
797+
* that may fault. The execution will jump to this after handling the
798+
* fault.
780799
*/
781-
offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE);
782-
if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset))
800+
fixup_offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE);
801+
if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset))
783802
return -ERANGE;
784803

785-
ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) |
804+
/*
805+
* The offsets above have been calculated using the RO buffer but we
806+
* need to use the R/W buffer for writes.
807+
* switch ex to rw buffer for writing.
808+
*/
809+
ex = (void *)ctx->image + ((void *)ex - (void *)ctx->ro_image);
810+
811+
ex->insn = ins_offset;
812+
813+
ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) |
786814
FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg);
787815

788816
ex->type = EX_TYPE_BPF;
@@ -1550,7 +1578,8 @@ static inline void bpf_flush_icache(void *start, void *end)
15501578

15511579
struct arm64_jit_data {
15521580
struct bpf_binary_header *header;
1553-
u8 *image;
1581+
u8 *ro_image;
1582+
struct bpf_binary_header *ro_header;
15541583
struct jit_ctx ctx;
15551584
};
15561585

@@ -1559,12 +1588,14 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
15591588
int image_size, prog_size, extable_size, extable_align, extable_offset;
15601589
struct bpf_prog *tmp, *orig_prog = prog;
15611590
struct bpf_binary_header *header;
1591+
struct bpf_binary_header *ro_header;
15621592
struct arm64_jit_data *jit_data;
15631593
bool was_classic = bpf_prog_was_classic(prog);
15641594
bool tmp_blinded = false;
15651595
bool extra_pass = false;
15661596
struct jit_ctx ctx;
15671597
u8 *image_ptr;
1598+
u8 *ro_image_ptr;
15681599

15691600
if (!prog->jit_requested)
15701601
return orig_prog;
@@ -1591,8 +1622,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
15911622
}
15921623
if (jit_data->ctx.offset) {
15931624
ctx = jit_data->ctx;
1594-
image_ptr = jit_data->image;
1625+
ro_image_ptr = jit_data->ro_image;
1626+
ro_header = jit_data->ro_header;
15951627
header = jit_data->header;
1628+
image_ptr = (void *)header + ((void *)ro_image_ptr
1629+
- (void *)ro_header);
15961630
extra_pass = true;
15971631
prog_size = sizeof(u32) * ctx.idx;
15981632
goto skip_init_ctx;
@@ -1637,63 +1671,81 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
16371671
/* also allocate space for plt target */
16381672
extable_offset = round_up(prog_size + PLT_TARGET_SIZE, extable_align);
16391673
image_size = extable_offset + extable_size;
1640-
header = bpf_jit_binary_alloc(image_size, &image_ptr,
1641-
sizeof(u32), jit_fill_hole);
1642-
if (header == NULL) {
1674+
ro_header = bpf_jit_binary_pack_alloc(image_size, &ro_image_ptr,
1675+
sizeof(u32), &header, &image_ptr,
1676+
jit_fill_hole);
1677+
if (!ro_header) {
16431678
prog = orig_prog;
16441679
goto out_off;
16451680
}
16461681

16471682
/* 2. Now, the actual pass. */
16481683

1684+
/*
1685+
* Use the image(RW) for writing the JITed instructions. But also save
1686+
* the ro_image(RX) for calculating the offsets in the image. The RW
1687+
* image will be later copied to the RX image from where the program
1688+
* will run. The bpf_jit_binary_pack_finalize() will do this copy in the
1689+
* final step.
1690+
*/
16491691
ctx.image = (__le32 *)image_ptr;
1692+
ctx.ro_image = (__le32 *)ro_image_ptr;
16501693
if (extable_size)
1651-
prog->aux->extable = (void *)image_ptr + extable_offset;
1694+
prog->aux->extable = (void *)ro_image_ptr + extable_offset;
16521695
skip_init_ctx:
16531696
ctx.idx = 0;
16541697
ctx.exentry_idx = 0;
16551698

16561699
build_prologue(&ctx, was_classic, prog->aux->exception_cb);
16571700

16581701
if (build_body(&ctx, extra_pass)) {
1659-
bpf_jit_binary_free(header);
16601702
prog = orig_prog;
1661-
goto out_off;
1703+
goto out_free_hdr;
16621704
}
16631705

16641706
build_epilogue(&ctx, prog->aux->exception_cb);
16651707
build_plt(&ctx);
16661708

16671709
/* 3. Extra pass to validate JITed code. */
16681710
if (validate_ctx(&ctx)) {
1669-
bpf_jit_binary_free(header);
16701711
prog = orig_prog;
1671-
goto out_off;
1712+
goto out_free_hdr;
16721713
}
16731714

16741715
/* And we're done. */
16751716
if (bpf_jit_enable > 1)
16761717
bpf_jit_dump(prog->len, prog_size, 2, ctx.image);
16771718

1678-
bpf_flush_icache(header, ctx.image + ctx.idx);
1679-
16801719
if (!prog->is_func || extra_pass) {
16811720
if (extra_pass && ctx.idx != jit_data->ctx.idx) {
16821721
pr_err_once("multi-func JIT bug %d != %d\n",
16831722
ctx.idx, jit_data->ctx.idx);
1684-
bpf_jit_binary_free(header);
16851723
prog->bpf_func = NULL;
16861724
prog->jited = 0;
16871725
prog->jited_len = 0;
1726+
goto out_free_hdr;
1727+
}
1728+
if (WARN_ON(bpf_jit_binary_pack_finalize(prog, ro_header,
1729+
header))) {
1730+
/* ro_header has been freed */
1731+
ro_header = NULL;
1732+
prog = orig_prog;
16881733
goto out_off;
16891734
}
1690-
bpf_jit_binary_lock_ro(header);
1735+
/*
1736+
* The instructions have now been copied to the ROX region from
1737+
* where they will execute. Now the data cache has to be cleaned to
1738+
* the PoU and the I-cache has to be invalidated for the VAs.
1739+
*/
1740+
bpf_flush_icache(ro_header, ctx.ro_image + ctx.idx);
16911741
} else {
16921742
jit_data->ctx = ctx;
1693-
jit_data->image = image_ptr;
1743+
jit_data->ro_image = ro_image_ptr;
16941744
jit_data->header = header;
1745+
jit_data->ro_header = ro_header;
16951746
}
1696-
prog->bpf_func = (void *)ctx.image;
1747+
1748+
prog->bpf_func = (void *)ctx.ro_image;
16971749
prog->jited = 1;
16981750
prog->jited_len = prog_size;
16991751

@@ -1714,13 +1766,28 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
17141766
bpf_jit_prog_release_other(prog, prog == orig_prog ?
17151767
tmp : orig_prog);
17161768
return prog;
1769+
1770+
out_free_hdr:
1771+
if (header) {
1772+
bpf_arch_text_copy(&ro_header->size, &header->size,
1773+
sizeof(header->size));
1774+
bpf_jit_binary_pack_free(ro_header, header);
1775+
}
1776+
goto out_off;
17171777
}
17181778

17191779
bool bpf_jit_supports_kfunc_call(void)
17201780
{
17211781
return true;
17221782
}
17231783

1784+
void *bpf_arch_text_copy(void *dst, void *src, size_t len)
1785+
{
1786+
if (!aarch64_insn_copy(dst, src, len))
1787+
return ERR_PTR(-EINVAL);
1788+
return dst;
1789+
}
1790+
17241791
u64 bpf_jit_alloc_exec_limit(void)
17251792
{
17261793
return VMALLOC_END - VMALLOC_START;
@@ -2359,3 +2426,27 @@ bool bpf_jit_supports_exceptions(void)
23592426
*/
23602427
return true;
23612428
}
2429+
2430+
void bpf_jit_free(struct bpf_prog *prog)
2431+
{
2432+
if (prog->jited) {
2433+
struct arm64_jit_data *jit_data = prog->aux->jit_data;
2434+
struct bpf_binary_header *hdr;
2435+
2436+
/*
2437+
* If we fail the final pass of JIT (from jit_subprogs),
2438+
* the program may not be finalized yet. Call finalize here
2439+
* before freeing it.
2440+
*/
2441+
if (jit_data) {
2442+
bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size,
2443+
sizeof(jit_data->header->size));
2444+
kfree(jit_data);
2445+
}
2446+
hdr = bpf_jit_binary_pack_hdr(prog);
2447+
bpf_jit_binary_pack_free(hdr, NULL);
2448+
WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
2449+
}
2450+
2451+
bpf_prog_unlock_free(prog);
2452+
}

0 commit comments

Comments
 (0)