veristat used bpf_program__clone() to verify each program in isolation. Replace it with BPF_PROG_LOAD_STRATEGY_MANUAL plus bpf_program__load()/bpf_program__unload(). Unlike clone(), which skips binding RODATA maps to avoid mutating shared object state, bpf_program__load() does bind them - but each program is unloaded right after verification, dropping the binding along with it. Also, add relevant test case to struct_ops_arena tests. Suggested-by: Andrii Nakryiko Signed-off-by: Andrey Grodzovsky --- .../bpf/prog_tests/test_struct_ops_arena.c | 48 +++++++++++++++++-- tools/testing/selftests/bpf/veristat.c | 20 ++++---- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c index e7580b9f0fc2..a8bd10bab5dd 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c @@ -192,9 +192,9 @@ static void arena_autobind(void) } /* - * The same binding through bpf_object__prepare() and bpf_program__clone(), the - * way veristat loads a program: the arena takes slot 0 of a continuous - * fd_array, next to the module BTF of the kfunc the trigger calls. + * The same binding through bpf_object__prepare() and bpf_program__clone(), + * the arena takes slot 0 of a continuous fd_array, next to the module BTF + * of the kfunc the trigger calls. */ static void arena_autobind_clone(void) { @@ -225,6 +225,46 @@ static void arena_autobind_clone(void) struct_ops_arena_autobind__destroy(skel); } +/* + * The same binding through BPF_PROG_LOAD_STRATEGY_MANUAL: every program is + * loaded with bpf_program__load() after bpf_object__prepare() and unloaded + * again, the way veristat loads programs. + */ +static void arena_autobind_manual(void) +{ + struct struct_ops_arena_autobind *skel; + struct bpf_map_info info = {}; + __u32 len = sizeof(info); + struct bpf_program *prog; + int err; + + skel = struct_ops_arena_autobind__open(); + if (!ASSERT_OK_PTR(skel, "struct_ops_arena_autobind__open")) + return; + + bpf_object__for_each_program(prog, skel->obj) { + err = bpf_program__set_load_strategy(prog, BPF_PROG_LOAD_STRATEGY_MANUAL); + if (!ASSERT_OK(err, "set_load_strategy")) + goto out; + } + err = bpf_object__prepare(skel->obj); + if (!ASSERT_OK(err, "prepare")) + goto out; + err = bpf_map_get_info_by_fd(bpf_map__fd(skel->maps.arena), &info, &len); + if (!ASSERT_OK(err, "arena_info")) + goto out; + bpf_object__for_each_program(prog, skel->obj) { + err = bpf_program__load(prog); + if (!ASSERT_OK(err, bpf_program__name(prog))) + continue; + assert_prog_map(bpf_program__fd(prog), info.id, bpf_program__name(prog)); + bpf_program__unload(prog); + ASSERT_LT(bpf_program__fd(prog), 0, "unloaded"); + } +out: + struct_ops_arena_autobind__destroy(skel); +} + /* * An arena exclusive to a program is bound to that program all the same: the * kernel checks an exclusive map against the digest of the final program, so @@ -315,6 +355,8 @@ void serial_test_struct_ops_arena(void) arena_autobind(); if (test__start_subtest("arena_autobind_clone")) arena_autobind_clone(); + if (test__start_subtest("arena_autobind_manual")) + arena_autobind_manual(); if (test__start_subtest("arena_autobind_excl")) arena_autobind_excl(); #else diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 9cfc9b4b41c1..0cd19b59037f 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -1678,7 +1678,6 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf const char *base_filename = basename(strdupa(filename)); const char *prog_name = bpf_program__name(prog); long mem_peak_a, mem_peak_b, mem_peak = -1; - LIBBPF_OPTS(bpf_prog_load_opts, opts); char *buf; int buf_sz, log_level; struct verif_stats *stats; @@ -1726,18 +1725,15 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf if (env.force_reg_invariants) bpf_program__add_flags(prog, BPF_F_TEST_REG_INVARIANTS); - opts.log_buf = buf; - opts.log_size = buf_sz; - opts.log_level = log_level; + bpf_program__set_log_buf(prog, buf, buf_sz); + bpf_program__set_log_level(prog, log_level); cgroup_err = reset_stat_cgroup(); mem_peak_a = cgroup_memory_peak(); - fd = bpf_program__clone(prog, &opts); - if (fd < 0) { - err = fd; - if (env.verbose) - fprintf(stderr, "Failed to load program %s %d\n", prog_name, err); - } + err = bpf_program__load(prog); + fd = err == 0 ? bpf_program__fd(prog) : -1; + if (err && env.verbose) + fprintf(stderr, "Failed to load program %s %d\n", prog_name, err); mem_peak_b = cgroup_memory_peak(); if (!cgroup_err && mem_peak_a >= 0 && mem_peak_b >= 0) mem_peak = mem_peak_b - mem_peak_a; @@ -1774,7 +1770,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf if (verif_log_buf != buf) free(buf); if (fd > 0) - close(fd); + bpf_program__unload(prog); return 0; } @@ -2297,7 +2293,7 @@ static int process_obj(const char *filename) env.files_processed++; bpf_object__for_each_program(prog, obj) { - bpf_program__set_autoload(prog, true); + bpf_program__set_load_strategy(prog, BPF_PROG_LOAD_STRATEGY_MANUAL); prog_cnt++; } -- 2.34.1