diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2026-01-31 13:51:04 -0800 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-01-31 13:51:12 -0800 |
| commit | 4bebb99140c7b6ee6d894c40557f938804fa8693 (patch) | |
| tree | fb974d4bed19b12af274bc4568e45f1ef0e7bc5a | |
| parent | f0b5b3d6b56f8717e255406366d81bbcd3631660 (diff) | |
| parent | 7f10da2133b18b0f1bc02d58671883537e212279 (diff) | |
Merge branch 'bpf-arm64-add-fsession-support'
Leon Hwang says:
====================
Similar to commit 98770bd4e6df ("bpf,x86: add fsession support for x86_64"),
add fsession support on arm64.
Patch #1 adds bpf_jit_supports_fsession() to prevent fsession loading
on architectures that do not implement fsession support.
Patch #2 implements fsession support in the arm64 BPF JIT trampoline.
Patch #3 enables the relevant selftests on arm64, including get_func_ip,
and get_func_args.
All enabled tests pass on arm64:
cd tools/testing/selftests/bpf
./test_progs -t fsession
#136/1 fsession_test/fsession_test:OK
#136/2 fsession_test/fsession_reattach:OK
#136/3 fsession_test/fsession_cookie:OK
#136 fsession_test:OK
Summary: 1/3 PASSED, 0 SKIPPED, 0 FAILED
./test_progs -t get_func
#138 get_func_args_test:OK
#139 get_func_ip_test:OK
Summary: 2/0 PASSED, 0 SKIPPED, 0 FAILED
Changes:
v4 -> v5:
* Address comment from Alexei:
* Rename helper bpf_link_prog_session_cookie() to
bpf_prog_calls_session_cookie().
* v4: https://lore.kernel.org/bpf/20260129154953.66915-1-leon.hwang@linux.dev/
v3 -> v4:
* Add a log when !bpf_jit_supports_fsession() in patch #1 (per AI).
* v3: https://lore.kernel.org/bpf/20260129142536.48637-1-leon.hwang@linux.dev/
v2 -> v3:
* Fix typo in subject and patch message of patch #1 (per AI and Chris).
* Collect Acked-by, and Tested-by from Puranjay, thanks.
* v2: https://lore.kernel.org/bpf/20260128150112.8873-1-leon.hwang@linux.dev/
v1 -> v2:
* Add bpf_jit_supports_fsession().
* v1: https://lore.kernel.org/bpf/20260127163344.92819-1-leon.hwang@linux.dev/
====================
Link: https://patch.msgid.link/20260131144950.16294-1-leon.hwang@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
| -rw-r--r-- | arch/arm64/net/bpf_jit_comp.c | 71 | ||||
| -rw-r--r-- | arch/x86/net/bpf_jit_comp.c | 5 | ||||
| -rw-r--r-- | include/linux/bpf.h | 7 | ||||
| -rw-r--r-- | include/linux/filter.h | 1 | ||||
| -rw-r--r-- | kernel/bpf/core.c | 5 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 5 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/prog_tests/fsession_test.c | 32 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/get_func_args_test.c | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/get_func_ip_test.c | 2 |
9 files changed, 110 insertions, 20 deletions
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 0c4d44bcfbf4..2dc5037694ba 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -2510,6 +2510,12 @@ static bool is_struct_ops_tramp(const struct bpf_tramp_links *fentry_links) fentry_links->links[0]->link.type == BPF_LINK_TYPE_STRUCT_OPS; } +static void store_func_meta(struct jit_ctx *ctx, u64 func_meta, int func_meta_off) +{ + emit_a64_mov_i64(A64_R(10), func_meta, ctx); + emit(A64_STR64I(A64_R(10), A64_SP, func_meta_off), ctx); +} + /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). * * bpf prog and function entry before bpf trampoline hooked: @@ -2533,7 +2539,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, int regs_off; int retval_off; int bargs_off; - int nfuncargs_off; + int func_meta_off; int ip_off; int run_ctx_off; int oargs_off; @@ -2544,6 +2550,9 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, bool save_ret; __le32 **branches = NULL; bool is_struct_ops = is_struct_ops_tramp(fentry); + int cookie_off, cookie_cnt, cookie_bargs_off; + int fsession_cnt = bpf_fsession_cnt(tlinks); + u64 func_meta; /* trampoline stack layout: * [ parent ip ] @@ -2562,10 +2571,14 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, * [ ... ] * SP + bargs_off [ arg reg 1 ] for bpf * - * SP + nfuncargs_off [ arg regs count ] + * SP + func_meta_off [ regs count, etc ] * * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag * + * [ stack cookie N ] + * [ ... ] + * SP + cookie_off [ stack cookie 1 ] + * * SP + run_ctx_off [ bpf_tramp_run_ctx ] * * [ stack arg N ] @@ -2582,13 +2595,18 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, /* room for bpf_tramp_run_ctx */ stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); + cookie_off = stack_size; + /* room for session cookies */ + cookie_cnt = bpf_fsession_cookie_cnt(tlinks); + stack_size += cookie_cnt * 8; + ip_off = stack_size; /* room for IP address argument */ if (flags & BPF_TRAMP_F_IP_ARG) stack_size += 8; - nfuncargs_off = stack_size; - /* room for args count */ + func_meta_off = stack_size; + /* room for function metadata, such as regs count */ stack_size += 8; bargs_off = stack_size; @@ -2646,9 +2664,9 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx); } - /* save arg regs count*/ - emit(A64_MOVZ(1, A64_R(10), nfuncargs, 0), ctx); - emit(A64_STR64I(A64_R(10), A64_SP, nfuncargs_off), ctx); + /* save function metadata */ + func_meta = nfuncargs; + store_func_meta(ctx, func_meta, func_meta_off); /* save args for bpf */ save_args(ctx, bargs_off, oargs_off, m, a, false); @@ -2666,10 +2684,27 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, emit_call((const u64)__bpf_tramp_enter, ctx); } - for (i = 0; i < fentry->nr_links; i++) + if (fsession_cnt) { + /* clear all the session cookies' value */ + emit(A64_MOVZ(1, A64_R(10), 0, 0), ctx); + for (int i = 0; i < cookie_cnt; i++) + emit(A64_STR64I(A64_R(10), A64_SP, cookie_off + 8 * i), ctx); + /* clear the return value to make sure fentry always gets 0 */ + emit(A64_STR64I(A64_R(10), A64_SP, retval_off), ctx); + } + + cookie_bargs_off = (bargs_off - cookie_off) / 8; + for (i = 0; i < fentry->nr_links; i++) { + if (bpf_prog_calls_session_cookie(fentry->links[i])) { + u64 meta = func_meta | (cookie_bargs_off << BPF_TRAMP_COOKIE_INDEX_SHIFT); + + store_func_meta(ctx, meta, func_meta_off); + cookie_bargs_off--; + } invoke_bpf_prog(ctx, fentry->links[i], bargs_off, retval_off, run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET); + } if (fmod_ret->nr_links) { branches = kcalloc(fmod_ret->nr_links, sizeof(__le32 *), @@ -2701,9 +2736,22 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, *branches[i] = cpu_to_le32(A64_CBNZ(1, A64_R(10), offset)); } - for (i = 0; i < fexit->nr_links; i++) + /* set the "is_return" flag for fsession */ + func_meta |= (1ULL << BPF_TRAMP_IS_RETURN_SHIFT); + if (fsession_cnt) + store_func_meta(ctx, func_meta, func_meta_off); + + cookie_bargs_off = (bargs_off - cookie_off) / 8; + for (i = 0; i < fexit->nr_links; i++) { + if (bpf_prog_calls_session_cookie(fexit->links[i])) { + u64 meta = func_meta | (cookie_bargs_off << BPF_TRAMP_COOKIE_INDEX_SHIFT); + + store_func_meta(ctx, meta, func_meta_off); + cookie_bargs_off--; + } invoke_bpf_prog(ctx, fexit->links[i], bargs_off, retval_off, run_ctx_off, false); + } if (flags & BPF_TRAMP_F_CALL_ORIG) { im->ip_epilogue = ctx->ro_image + ctx->idx; @@ -2753,6 +2801,11 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, return ctx->idx; } +bool bpf_jit_supports_fsession(void) +{ + return true; +} + int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, struct bpf_tramp_links *tlinks, void *func_addr) { diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 5a075e06cf45..070ba80e39d7 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -4112,3 +4112,8 @@ bool bpf_jit_supports_timed_may_goto(void) { return true; } + +bool bpf_jit_supports_fsession(void) +{ + return true; +} diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 3b0ceb759075..cd9b96434904 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2196,13 +2196,18 @@ static inline int bpf_fsession_cnt(struct bpf_tramp_links *links) return cnt; } +static inline bool bpf_prog_calls_session_cookie(struct bpf_tramp_link *link) +{ + return link->link.prog->call_session_cookie; +} + static inline int bpf_fsession_cookie_cnt(struct bpf_tramp_links *links) { struct bpf_tramp_links fentries = links[BPF_TRAMP_FENTRY]; int cnt = 0; for (int i = 0; i < links[BPF_TRAMP_FENTRY].nr_links; i++) { - if (fentries.links[i]->link.prog->call_session_cookie) + if (bpf_prog_calls_session_cookie(fentries.links[i])) cnt++; } diff --git a/include/linux/filter.h b/include/linux/filter.h index fd54fed8f95f..4e1cb4f91f49 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1167,6 +1167,7 @@ bool bpf_jit_supports_arena(void); bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena); bool bpf_jit_supports_private_stack(void); bool bpf_jit_supports_timed_may_goto(void); +bool bpf_jit_supports_fsession(void); u64 bpf_arch_uaddress_limit(void); void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie); u64 arch_bpf_timed_may_goto(void); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 5ebece600aeb..dc906dfdff94 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -3144,6 +3144,11 @@ bool __weak bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena) return false; } +bool __weak bpf_jit_supports_fsession(void) +{ + return false; +} + u64 __weak bpf_arch_uaddress_limit(void) { #if defined(CONFIG_64BIT) && defined(CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 256cc5c1a7df..6b62b6d57175 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -24828,6 +24828,11 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, case BPF_TRACE_FENTRY: case BPF_TRACE_FEXIT: case BPF_TRACE_FSESSION: + if (prog->expected_attach_type == BPF_TRACE_FSESSION && + !bpf_jit_supports_fsession()) { + bpf_log(log, "JIT does not support fsession\n"); + return -EOPNOTSUPP; + } if (!btf_type_is_func(t)) { bpf_log(log, "attach_btf_id %u is not a function\n", btf_id); diff --git a/tools/testing/selftests/bpf/prog_tests/fsession_test.c b/tools/testing/selftests/bpf/prog_tests/fsession_test.c index 0c4b428e1cee..a299aeb8cc2e 100644 --- a/tools/testing/selftests/bpf/prog_tests/fsession_test.c +++ b/tools/testing/selftests/bpf/prog_tests/fsession_test.c @@ -29,8 +29,16 @@ static void test_fsession_basic(void) struct fsession_test *skel = NULL; int err; - skel = fsession_test__open_and_load(); - if (!ASSERT_OK_PTR(skel, "fsession_test__open_and_load")) + skel = fsession_test__open(); + if (!ASSERT_OK_PTR(skel, "fsession_test__open")) + return; + + err = fsession_test__load(skel); + if (err == -EOPNOTSUPP) { + test__skip(); + goto cleanup; + } + if (!ASSERT_OK(err, "fsession_test__load")) goto cleanup; err = fsession_test__attach(skel); @@ -47,8 +55,16 @@ static void test_fsession_reattach(void) struct fsession_test *skel = NULL; int err; - skel = fsession_test__open_and_load(); - if (!ASSERT_OK_PTR(skel, "fsession_test__open_and_load")) + skel = fsession_test__open(); + if (!ASSERT_OK_PTR(skel, "fsession_test__open")) + return; + + err = fsession_test__load(skel); + if (err == -EOPNOTSUPP) { + test__skip(); + goto cleanup; + } + if (!ASSERT_OK(err, "fsession_test__load")) goto cleanup; /* first attach */ @@ -94,6 +110,10 @@ static void test_fsession_cookie(void) bpf_program__set_autoload(skel->progs.test6, false); err = fsession_test__load(skel); + if (err == -EOPNOTSUPP) { + test__skip(); + goto cleanup; + } if (!ASSERT_OK(err, "fsession_test__load")) goto cleanup; @@ -111,10 +131,6 @@ cleanup: void test_fsession_test(void) { -#if !defined(__x86_64__) - test__skip(); - return; -#endif if (test__start_subtest("fsession_test")) test_fsession_basic(); if (test__start_subtest("fsession_reattach")) diff --git a/tools/testing/selftests/bpf/progs/get_func_args_test.c b/tools/testing/selftests/bpf/progs/get_func_args_test.c index 0a3236a7a109..180ba5098ca1 100644 --- a/tools/testing/selftests/bpf/progs/get_func_args_test.c +++ b/tools/testing/selftests/bpf/progs/get_func_args_test.c @@ -167,7 +167,7 @@ int BPF_PROG(tp_test2) } __u64 test7_result = 0; -#ifdef __TARGET_ARCH_x86 +#if defined(bpf_target_x86) || defined(bpf_target_arm64) SEC("fsession/bpf_fentry_test1") int BPF_PROG(test7) { diff --git a/tools/testing/selftests/bpf/progs/get_func_ip_test.c b/tools/testing/selftests/bpf/progs/get_func_ip_test.c index 65f7e1f182bf..43ff836a8ed8 100644 --- a/tools/testing/selftests/bpf/progs/get_func_ip_test.c +++ b/tools/testing/selftests/bpf/progs/get_func_ip_test.c @@ -106,7 +106,7 @@ int BPF_URETPROBE(test8, int ret) __u64 test9_entry_result = 0; __u64 test9_exit_result = 0; -#ifdef __TARGET_ARCH_x86 +#if defined(bpf_target_x86) || defined(bpf_target_arm64) SEC("fsession/bpf_fentry_test1") int BPF_PROG(test9, int a) { |
