// SPDX-License-Identifier: GPL-2.0 #include #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(); }