]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests/bpf: Test BPF_PROG_ASSOC_STRUCT_OPS command
authorAmery Hung <ameryhung@gmail.com>
Wed, 3 Dec 2025 23:37:46 +0000 (15:37 -0800)
committerAndrii Nakryiko <andrii@kernel.org>
Sat, 6 Dec 2025 00:17:58 +0000 (16:17 -0800)
Test BPF_PROG_ASSOC_STRUCT_OPS command that associates a BPF program
with a struct_ops. The test follows the same logic in commit
ba7000f1c360 ("selftests/bpf: Test multi_st_ops and calling kfuncs from
different programs"), but instead of using map id to identify a specific
struct_ops, this test uses the new BPF command to associate a struct_ops
with a program.

The test consists of two sets of almost identical struct_ops maps and BPF
programs associated with the map. Their only difference is the unique
value returned by bpf_testmod_multi_st_ops::test_1().

The test first loads the programs and associates them with struct_ops
maps. Then, it exercises the BPF programs. They will in turn call kfunc
bpf_kfunc_multi_st_ops_test_1_prog_arg() to trigger test_1() of the
associated struct_ops map, and then check if the right unique value is
returned.

Signed-off-by: Amery Hung <ameryhung@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20251203233748.668365-5-ameryhung@gmail.com
tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/struct_ops_assoc.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h

diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
new file mode 100644 (file)
index 0000000..1e24a49
--- /dev/null
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "struct_ops_assoc.skel.h"
+
+static void test_st_ops_assoc(void)
+{
+       struct struct_ops_assoc *skel = NULL;
+       int err, pid;
+
+       skel = struct_ops_assoc__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "struct_ops_assoc__open"))
+               goto out;
+
+       /* cannot explicitly associate struct_ops program */
+       err = bpf_program__assoc_struct_ops(skel->progs.test_1_a,
+                                           skel->maps.st_ops_map_a, NULL);
+       ASSERT_ERR(err, "bpf_program__assoc_struct_ops(test_1_a, st_ops_map_a)");
+
+       err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog_a,
+                                           skel->maps.st_ops_map_a, NULL);
+       ASSERT_OK(err, "bpf_program__assoc_struct_ops(syscall_prog_a, st_ops_map_a)");
+
+       err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_a,
+                                           skel->maps.st_ops_map_a, NULL);
+       ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_a, st_ops_map_a)");
+
+       err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog_b,
+                                           skel->maps.st_ops_map_b, NULL);
+       ASSERT_OK(err, "bpf_program__assoc_struct_ops(syscall_prog_b, st_ops_map_b)");
+
+       err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_b,
+                                           skel->maps.st_ops_map_b, NULL);
+       ASSERT_OK(err, "bpf_program__assoc_struct_ops(sys_enter_prog_b, st_ops_map_b)");
+
+       /* sys_enter_prog_a already associated with map_a */
+       err = bpf_program__assoc_struct_ops(skel->progs.sys_enter_prog_a,
+                                           skel->maps.st_ops_map_b, NULL);
+       ASSERT_ERR(err, "bpf_program__assoc_struct_ops(sys_enter_prog_a, st_ops_map_b)");
+
+       err = struct_ops_assoc__attach(skel);
+       if (!ASSERT_OK(err, "struct_ops_assoc__attach"))
+               goto out;
+
+       /* run tracing prog that calls .test_1 and checks return */
+       pid = getpid();
+       skel->bss->test_pid = pid;
+       sys_gettid();
+       skel->bss->test_pid = 0;
+
+       ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
+       ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+
+       /* run syscall_prog that calls .test_1 and checks return */
+       err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog_a), NULL);
+       ASSERT_OK(err, "bpf_prog_test_run_opts");
+
+       err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog_b), NULL);
+       ASSERT_OK(err, "bpf_prog_test_run_opts");
+
+       ASSERT_EQ(skel->bss->test_err_a, 0, "skel->bss->test_err_a");
+       ASSERT_EQ(skel->bss->test_err_b, 0, "skel->bss->test_err_b");
+
+out:
+       struct_ops_assoc__destroy(skel);
+}
+
+void test_struct_ops_assoc(void)
+{
+       if (test__start_subtest("st_ops_assoc"))
+               test_st_ops_assoc();
+}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
new file mode 100644 (file)
index 0000000..8f10979
--- /dev/null
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+#include "../test_kmods/bpf_testmod.h"
+#include "../test_kmods/bpf_testmod_kfunc.h"
+
+char _license[] SEC("license") = "GPL";
+
+int test_pid;
+
+/* Programs associated with st_ops_map_a */
+
+#define MAP_A_MAGIC 1234
+int test_err_a;
+
+SEC("struct_ops")
+int BPF_PROG(test_1_a, struct st_ops_args *args)
+{
+       return MAP_A_MAGIC;
+}
+
+SEC("tp_btf/sys_enter")
+int BPF_PROG(sys_enter_prog_a, struct pt_regs *regs, long id)
+{
+       struct st_ops_args args = {};
+       struct task_struct *task;
+       int ret;
+
+       task = bpf_get_current_task_btf();
+       if (!test_pid || task->pid != test_pid)
+               return 0;
+
+       ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+       if (ret != MAP_A_MAGIC)
+               test_err_a++;
+
+       return 0;
+}
+
+SEC("syscall")
+int syscall_prog_a(void *ctx)
+{
+       struct st_ops_args args = {};
+       int ret;
+
+       ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+       if (ret != MAP_A_MAGIC)
+               test_err_a++;
+
+       return 0;
+}
+
+SEC(".struct_ops.link")
+struct bpf_testmod_multi_st_ops st_ops_map_a = {
+       .test_1 = (void *)test_1_a,
+};
+
+/* Programs associated with st_ops_map_b */
+
+#define MAP_B_MAGIC 5678
+int test_err_b;
+
+SEC("struct_ops")
+int BPF_PROG(test_1_b, struct st_ops_args *args)
+{
+       return MAP_B_MAGIC;
+}
+
+SEC("tp_btf/sys_enter")
+int BPF_PROG(sys_enter_prog_b, struct pt_regs *regs, long id)
+{
+       struct st_ops_args args = {};
+       struct task_struct *task;
+       int ret;
+
+       task = bpf_get_current_task_btf();
+       if (!test_pid || task->pid != test_pid)
+               return 0;
+
+       ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+       if (ret != MAP_B_MAGIC)
+               test_err_b++;
+
+       return 0;
+}
+
+SEC("syscall")
+int syscall_prog_b(void *ctx)
+{
+       struct st_ops_args args = {};
+       int ret;
+
+       ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+       if (ret != MAP_B_MAGIC)
+               test_err_b++;
+
+       return 0;
+}
+
+SEC(".struct_ops.link")
+struct bpf_testmod_multi_st_ops st_ops_map_b = {
+       .test_1 = (void *)test_1_b,
+};
index 1669a7eeda260373b1121f0e3fde274df25081ba..90c4b1a51de637172e246251358beb36ddd1bda0 100644 (file)
@@ -1134,6 +1134,7 @@ __bpf_kfunc int bpf_kfunc_st_ops_inc10(struct st_ops_args *args)
 }
 
 __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id);
+__bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux_prog);
 
 BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids)
 BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
@@ -1176,6 +1177,7 @@ BTF_ID_FLAGS(func, bpf_kfunc_st_ops_test_epilogue, KF_TRUSTED_ARGS | KF_SLEEPABL
 BTF_ID_FLAGS(func, bpf_kfunc_st_ops_test_pro_epilogue, KF_TRUSTED_ARGS | KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_kfunc_st_ops_inc10, KF_TRUSTED_ARGS)
 BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1, KF_TRUSTED_ARGS)
+BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_impl, KF_TRUSTED_ARGS)
 BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
 
 static int bpf_testmod_ops_init(struct btf *btf)
@@ -1637,6 +1639,7 @@ static struct bpf_testmod_multi_st_ops *multi_st_ops_find_nolock(u32 id)
        return NULL;
 }
 
+/* Call test_1() of the struct_ops map identified by the id */
 int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id)
 {
        struct bpf_testmod_multi_st_ops *st_ops;
@@ -1652,6 +1655,20 @@ int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id)
        return ret;
 }
 
+/* Call test_1() of the associated struct_ops map */
+int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog)
+{
+       struct bpf_prog_aux *prog_aux = (struct bpf_prog_aux *)aux__prog;
+       struct bpf_testmod_multi_st_ops *st_ops;
+       int ret = -1;
+
+       st_ops = (struct bpf_testmod_multi_st_ops *)bpf_prog_get_assoc_struct_ops(prog_aux);
+       if (st_ops)
+               ret = st_ops->test_1(args);
+
+       return ret;
+}
+
 static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
 {
        struct bpf_testmod_multi_st_ops *st_ops =
index 4df6fa6a92cba719abc375f4f51b2bc54e73351b..2357a0340ffe81a3b9a7ea186d1bc910dabb2c2d 100644 (file)
@@ -162,5 +162,6 @@ struct task_struct *bpf_kfunc_ret_rcu_test(void) __ksym;
 int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size) __ksym;
 
 int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __ksym;
+int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog) __ksym;
 
 #endif /* _BPF_TESTMOD_KFUNC_H */