public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
From: Mykyta Yatsenko <mykyta.yatsenko5@gmail.com>
To: Emil Tsalapatis <emil@etsalapatis.com>,
	bpf@vger.kernel.org, ast@kernel.org, andrii@kernel.org,
	daniel@iogearbox.net, kafai@meta.com, kernel-team@meta.com,
	eddyz87@gmail.com
Cc: Mykyta Yatsenko <yatsenko@meta.com>
Subject: Re: [PATCH bpf-next v2 2/2] selftests/bpf: Use bpf_program__clone() in veristat
Date: Mon, 23 Feb 2026 18:39:37 +0000	[thread overview]
Message-ID: <874in7cniu.fsf@gmail.com> (raw)
In-Reply-To: <DGMJ4DNC556M.16VOPU824MANQ@etsalapatis.com>

"Emil Tsalapatis" <emil@etsalapatis.com> writes:

> On Fri Feb 20, 2026 at 2:18 PM EST, Mykyta Yatsenko wrote:
>> From: Mykyta Yatsenko <yatsenko@meta.com>
>>
>> Replace veristat's per-program object re-opening with
>> bpf_program__clone().
>>
>> Previously, veristat opened a separate bpf_object for every program in a
>> multi-program object file, iterated all programs to enable only the
>> target one, and then loaded the entire object.
>>
>> Use bpf_object__prepare() once, then call bpf_program__clone() for each
>> program individually. This lets veristat load programs one at a time
>> from a single prepared object.
>>
>> The caller now owns the returned fd and closes it after collecting stats.
>> Remove the special single-program fast path and the per-file early exit
>> in handle_verif_mode() so all files are always processed.
>>
>> Split fixup_obj() into fixup_obj_maps() for object-wide map fixups that
>> must run before bpf_object__prepare(), and fixup_obj() for per-program
>> fixups (struct_ops masking, freplace type guessing) that run before each
>> bpf_program__clone() call.
>>
>> Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
>> ---
>>  tools/testing/selftests/bpf/veristat.c | 104 ++++++++++++++-------------------
>>  1 file changed, 44 insertions(+), 60 deletions(-)
>>
>> diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
>> index 1be1e353d40a..7d7eb56e04d0 100644
>> --- a/tools/testing/selftests/bpf/veristat.c
>> +++ b/tools/testing/selftests/bpf/veristat.c
>> @@ -1236,7 +1236,7 @@ static void mask_unrelated_struct_ops_progs(struct bpf_object *obj,
>>  	}
>>  }
>>  
>> -static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const char *filename)
>> +static void fixup_obj_maps(struct bpf_object *obj)
>>  {
>>  	struct bpf_map *map;
>>  
>> @@ -1251,15 +1251,23 @@ static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const ch
>>  		case BPF_MAP_TYPE_INODE_STORAGE:
>>  		case BPF_MAP_TYPE_CGROUP_STORAGE:
>>  		case BPF_MAP_TYPE_CGRP_STORAGE:
>> -			break;
>>  		case BPF_MAP_TYPE_STRUCT_OPS:
>> -			mask_unrelated_struct_ops_progs(obj, map, prog);
>>  			break;
>>  		default:
>>  			if (bpf_map__max_entries(map) == 0)
>>  				bpf_map__set_max_entries(map, 1);
>>  		}
>>  	}
>> +}
>> +
>> +static void fixup_obj(struct bpf_object *obj, struct bpf_program *prog, const char *filename)
>> +{
>> +	struct bpf_map *map;
>> +
>> +	bpf_object__for_each_map(map, obj) {
>> +		if (bpf_map__type(map) == BPF_MAP_TYPE_STRUCT_OPS)
>> +			mask_unrelated_struct_ops_progs(obj, map, prog);
>> +	}
>>  
>>  	/* SEC(freplace) programs can't be loaded with veristat as is,
>>  	 * but we can try guessing their target program's expected type by
>> @@ -1608,6 +1616,7 @@ 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;
>> @@ -1647,9 +1656,6 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>>  	}
>>  	verif_log_buf[0] = '\0';
>>  
>> -	bpf_program__set_log_buf(prog, buf, buf_sz);
>> -	bpf_program__set_log_level(prog, log_level);
>> -
>>  	/* increase chances of successful BPF object loading */
>>  	fixup_obj(obj, prog, base_filename);
>>  
>> @@ -1658,15 +1664,21 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>>  	if (env.force_reg_invariants)
>>  		bpf_program__set_flags(prog, bpf_program__flags(prog) | BPF_F_TEST_REG_INVARIANTS);
>>  
>> -	err = bpf_object__prepare(obj);
>> -	if (!err) {
>> -		cgroup_err = reset_stat_cgroup();
>> -		mem_peak_a = cgroup_memory_peak();
>> -		err = bpf_object__load(obj);
>> -		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;
>> +	opts.log_buf = buf;
>> +	opts.log_size = buf_sz;
>> +	opts.log_level = log_level;
>> +
>> +	cgroup_err = reset_stat_cgroup();
>> +	mem_peak_a = cgroup_memory_peak();
>> +	fd = bpf_program__clone(prog, &opts);
>> +	if (fd < 0) {
>> +		err = fd;
>> +		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;
>> +
>
> AFAICT the meaning of mem_peak changes with this patch, right? Before it was the diff between 
> prepare() and load(), with the patch as it is now it is max(prepare,
> load) since mem_peak_a is reset right above. If we do not automatically
> load OBJ_PREPARED programs (see the review for patch 1/2) we can keep
> the old behavior by storing mem_peak_a after the first prepare and
> reading mem_peak_b after clone+load.
>
I'm not sure if I understand why the meaning of mem_peak changed. Before
it was:
   bpf_object__prepare()
   peak_reset()
   a = mem_peak()
   bpf_object__load();
   mem_peak = mem_peak() - a;

and now:
   bpf_object__prepare()
   ...
   peak_reset()
   a = mem_peak()
   bpf_program__clone();
   mem_peak = mem_peak() - a;

I understand this code is supposed to measure the memory overhead for
the loading a single program.

Could you please elaborate what the issue is? Maybe some example?
>>  	env.progs_processed++;
>>  
>>  	stats->file_name = strdup(base_filename);
>> @@ -1678,7 +1690,6 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
>>  	stats->stats[MEMORY_PEAK] = mem_peak < 0 ? -1 : mem_peak / (1024 * 1024);
>>  
>>  	memset(&info, 0, info_len);
>> -	fd = bpf_program__fd(prog);
>>  	if (fd > 0 && bpf_prog_get_info_by_fd(fd, &info, &info_len) == 0) {
>>  		stats->stats[JITED_SIZE] = info.jited_prog_len;
>>  		if (env.dump_mode & DUMP_JITED)
>> @@ -1699,7 +1710,8 @@ 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);
>>  	return 0;
>>  }
>>  
>> @@ -2182,8 +2194,8 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i
>>  static int process_obj(const char *filename)
>>  {
>>  	const char *base_filename = basename(strdupa(filename));
>> -	struct bpf_object *obj = NULL, *tobj;
>> -	struct bpf_program *prog, *tprog, *lprog;
>> +	struct bpf_object *obj = NULL;
>> +	struct bpf_program *prog;
>>  	libbpf_print_fn_t old_libbpf_print_fn;
>>  	LIBBPF_OPTS(bpf_object_open_opts, opts);
>>  	int err = 0, prog_cnt = 0;
>> @@ -2222,51 +2234,26 @@ static int process_obj(const char *filename)
>>  	env.files_processed++;
>>  
>>  	bpf_object__for_each_program(prog, obj) {
>> +		bpf_program__set_autoload(prog, true);
>>  		prog_cnt++;
>>  	}
>>  
>> -	if (prog_cnt == 1) {
>> -		prog = bpf_object__next_program(obj, NULL);
>> -		bpf_program__set_autoload(prog, true);
>> -		err = set_global_vars(obj, env.presets, env.npresets);
>> -		if (err) {
>> -			fprintf(stderr, "Failed to set global variables %d\n", err);
>> -			goto cleanup;
>> -		}
>> -		process_prog(filename, obj, prog);
>> +	fixup_obj_maps(obj);
>> +
>> +	err = set_global_vars(obj, env.presets, env.npresets);
>> +	if (err) {
>> +		fprintf(stderr, "Failed to set global variables %d\n", err);
>>  		goto cleanup;
>>  	}
>>  
>> -	bpf_object__for_each_program(prog, obj) {
>> -		const char *prog_name = bpf_program__name(prog);
>> -
>> -		tobj = bpf_object__open_file(filename, &opts);
>> -		if (!tobj) {
>> -			err = -errno;
>> -			fprintf(stderr, "Failed to open '%s': %d\n", filename, err);
>> -			goto cleanup;
>> -		}
>> -
>> -		err = set_global_vars(tobj, env.presets, env.npresets);
>> -		if (err) {
>> -			fprintf(stderr, "Failed to set global variables %d\n", err);
>> -			goto cleanup;
>> -		}
>> -
>> -		lprog = NULL;
>> -		bpf_object__for_each_program(tprog, tobj) {
>> -			const char *tprog_name = bpf_program__name(tprog);
>> -
>> -			if (strcmp(prog_name, tprog_name) == 0) {
>> -				bpf_program__set_autoload(tprog, true);
>> -				lprog = tprog;
>> -			} else {
>> -				bpf_program__set_autoload(tprog, false);
>> -			}
>> -		}
>> +	err = bpf_object__prepare(obj);
>> +	if (err) {
>> +		fprintf(stderr, "Failed to prepare BPF object for loading %d\n", err);
>> +		goto cleanup;
>> +	}
>>  
>> -		process_prog(filename, tobj, lprog);
>> -		bpf_object__close(tobj);
>> +	bpf_object__for_each_program(prog, obj) {
>> +		process_prog(filename, obj, prog);
>>  	}
>>  
>>  cleanup:
>> @@ -3264,17 +3251,14 @@ static int handle_verif_mode(void)
>>  	create_stat_cgroup();
>>  	for (i = 0; i < env.filename_cnt; i++) {
>>  		err = process_obj(env.filenames[i]);
>> -		if (err) {
>> +		if (err)
>>  			fprintf(stderr, "Failed to process '%s': %d\n", env.filenames[i], err);
>> -			goto out;
>> -		}
>
> This means we now do not stop on the first failure - this I assume is
> deliberate since it is an improvement over existing behavior imo, but
> still pointing it out in case it isn't.
>
>>  	}
>>  
>>  	qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats);
>>  
>>  	output_prog_stats();
>>  
>> -out:
>>  	destroy_stat_cgroup();
>>  	return err;
>>  }

  reply	other threads:[~2026-02-23 18:39 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-20 19:18 [PATCH bpf-next v2 0/2] libbpf: Add bpf_program__clone() for individual program loading Mykyta Yatsenko
2026-02-20 19:18 ` [PATCH bpf-next v2 1/2] libbpf: Introduce bpf_program__clone() Mykyta Yatsenko
2026-02-23 17:25   ` Emil Tsalapatis
2026-02-23 17:59     ` Mykyta Yatsenko
2026-02-23 18:04       ` Emil Tsalapatis
2026-02-24 19:28   ` Eduard Zingerman
2026-02-24 19:32     ` Eduard Zingerman
2026-02-24 20:47     ` Mykyta Yatsenko
2026-03-06 17:22   ` [External] " Andrey Grodzovsky
2026-03-10  0:08     ` Mykyta Yatsenko
2026-03-11 13:35       ` Andrey Grodzovsky
2026-03-11 22:52     ` Andrii Nakryiko
2026-03-16 14:23       ` Andrey Grodzovsky
2026-03-11 23:03   ` Andrii Nakryiko
2026-02-20 19:18 ` [PATCH bpf-next v2 2/2] selftests/bpf: Use bpf_program__clone() in veristat Mykyta Yatsenko
2026-02-23 17:49   ` Emil Tsalapatis
2026-02-23 18:39     ` Mykyta Yatsenko [this message]
2026-02-23 18:54       ` Emil Tsalapatis
2026-02-24  2:03   ` Eduard Zingerman
2026-02-24 12:20     ` Mykyta Yatsenko
2026-02-24 19:08       ` Eduard Zingerman
2026-02-24 19:12         ` Mykyta Yatsenko
2026-02-24 19:16           ` Eduard Zingerman
2026-02-20 22:48 ` [PATCH bpf-next v2 0/2] libbpf: Add bpf_program__clone() for individual program loading Alexei Starovoitov
2026-02-23 13:57   ` Mykyta Yatsenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=874in7cniu.fsf@gmail.com \
    --to=mykyta.yatsenko5@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=kafai@meta.com \
    --cc=kernel-team@meta.com \
    --cc=yatsenko@meta.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox