| // SPDX-License-Identifier: GPL-2.0 |
| |
| #include <test_progs.h> |
| #include "struct_ops_assoc.skel.h" |
| #include "struct_ops_assoc_reuse.skel.h" |
| #include "struct_ops_assoc_in_timer.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); |
| } |
| |
| static void test_st_ops_assoc_reuse(void) |
| { |
| struct struct_ops_assoc_reuse *skel = NULL; |
| int err; |
| |
| skel = struct_ops_assoc_reuse__open_and_load(); |
| if (!ASSERT_OK_PTR(skel, "struct_ops_assoc_reuse__open")) |
| goto out; |
| |
| 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.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 = struct_ops_assoc_reuse__attach(skel); |
| if (!ASSERT_OK(err, "struct_ops_assoc__attach")) |
| goto out; |
| |
| /* 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_reuse__destroy(skel); |
| } |
| |
| static void test_st_ops_assoc_in_timer(void) |
| { |
| struct struct_ops_assoc_in_timer *skel = NULL; |
| int err; |
| |
| skel = struct_ops_assoc_in_timer__open_and_load(); |
| if (!ASSERT_OK_PTR(skel, "struct_ops_assoc_in_timer__open")) |
| goto out; |
| |
| err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog, |
| skel->maps.st_ops_map, NULL); |
| ASSERT_OK(err, "bpf_program__assoc_struct_ops"); |
| |
| err = struct_ops_assoc_in_timer__attach(skel); |
| if (!ASSERT_OK(err, "struct_ops_assoc__attach")) |
| goto out; |
| |
| /* |
| * Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks |
| * the return value. .test_1 will also schedule timer_cb that runs .test_1 again |
| * immediately. |
| */ |
| err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL); |
| ASSERT_OK(err, "bpf_prog_test_run_opts"); |
| |
| /* Check the return of the kfunc after timer_cb runs */ |
| while (!READ_ONCE(skel->bss->timer_cb_run)) |
| sched_yield(); |
| ASSERT_EQ(skel->bss->timer_test_1_ret, 1234, "skel->bss->timer_test_1_ret"); |
| ASSERT_EQ(skel->bss->test_err, 0, "skel->bss->test_err_a"); |
| out: |
| struct_ops_assoc_in_timer__destroy(skel); |
| } |
| |
| static void test_st_ops_assoc_in_timer_no_uref(void) |
| { |
| struct struct_ops_assoc_in_timer *skel = NULL; |
| struct bpf_link *link; |
| int err; |
| |
| skel = struct_ops_assoc_in_timer__open_and_load(); |
| if (!ASSERT_OK_PTR(skel, "struct_ops_assoc_in_timer__open")) |
| goto out; |
| |
| err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog, |
| skel->maps.st_ops_map, NULL); |
| ASSERT_OK(err, "bpf_program__assoc_struct_ops"); |
| |
| link = bpf_map__attach_struct_ops(skel->maps.st_ops_map); |
| if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops")) |
| goto out; |
| |
| /* |
| * Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks |
| * the return value. .test_1 will also schedule timer_cb that runs .test_1 again. |
| * timer_cb will run 500ms after syscall_prog runs, when the user space no longer |
| * holds a reference to st_ops_map. |
| */ |
| skel->bss->timer_ns = 500000000; |
| err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL); |
| ASSERT_OK(err, "bpf_prog_test_run_opts"); |
| |
| /* Detach and close struct_ops map to cause it to be freed */ |
| bpf_link__destroy(link); |
| close(bpf_program__fd(skel->progs.syscall_prog)); |
| close(bpf_map__fd(skel->maps.st_ops_map)); |
| |
| /* Check the return of the kfunc after timer_cb runs */ |
| while (!READ_ONCE(skel->bss->timer_cb_run)) |
| sched_yield(); |
| ASSERT_EQ(skel->bss->timer_test_1_ret, -1, "skel->bss->timer_test_1_ret"); |
| ASSERT_EQ(skel->bss->test_err, 0, "skel->bss->test_err_a"); |
| out: |
| struct_ops_assoc_in_timer__destroy(skel); |
| } |
| |
| void test_struct_ops_assoc(void) |
| { |
| if (test__start_subtest("st_ops_assoc")) |
| test_st_ops_assoc(); |
| if (test__start_subtest("st_ops_assoc_reuse")) |
| test_st_ops_assoc_reuse(); |
| if (test__start_subtest("st_ops_assoc_in_timer")) |
| test_st_ops_assoc_in_timer(); |
| if (test__start_subtest("st_ops_assoc_in_timer_no_uref")) |
| test_st_ops_assoc_in_timer_no_uref(); |
| } |