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 1/2] libbpf: Introduce bpf_program__clone()
Date: Mon, 23 Feb 2026 17:59:03 +0000	[thread overview]
Message-ID: <877bs3cpeg.fsf@gmail.com> (raw)
In-Reply-To: <DGMIM8SH2Q43.1I9LNNV1X34KB@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>
>>
>> Add bpf_program__clone() API that loads a single BPF program from a
>> prepared BPF object into the kernel, returning a file descriptor owned
>> by the caller.
>>
>> After bpf_object__prepare(), callers can use bpf_program__clone() to
>> load individual programs with custom bpf_prog_load_opts, instead of
>> loading all programs at once via bpf_object__load(). Non-zero fields in
>> opts override the defaults derived from the program and object
>> internals; passing NULL opts populates everything automatically.
>>
>> Internally, bpf_program__clone() resolves BTF-based attach targets
>> (attach_btf_id, attach_btf_obj_fd) and the sleepable flag, fills
>> func/line info, fd_array, license, and kern_version from the
>> prepared object before calling bpf_prog_load().
>>
>> Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
>> ---
>>  tools/lib/bpf/libbpf.c   | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
>>  tools/lib/bpf/libbpf.h   | 17 +++++++++++++
>>  tools/lib/bpf/libbpf.map |  1 +
>>  3 files changed, 82 insertions(+)
>>
>
> The code looks in order, one issue below.
>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 0c8bf0b5cce4..4b084bda3f47 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -9793,6 +9793,70 @@ __u32 bpf_program__line_info_cnt(const struct bpf_program *prog)
>>  	return prog->line_info_cnt;
>>  }
>>  
>> +int bpf_program__clone(struct bpf_program *prog, const struct bpf_prog_load_opts *opts)
>> +{
>> +	LIBBPF_OPTS(bpf_prog_load_opts, attr);
>> +	struct bpf_prog_load_opts *pattr = &attr;
>> +	struct bpf_object *obj;
>> +	int err, fd;
>> +
>> +	if (!prog)
>> +		return libbpf_err(-EINVAL);
>> +
>> +	if (!OPTS_VALID(opts, bpf_prog_load_opts))
>> +		return libbpf_err(-EINVAL);
>> +
>> +	obj = prog->obj;
>> +	if (obj->state < OBJ_PREPARED)
>> +		return libbpf_err(-EINVAL);
>> +
>> +	/* Copy caller opts, fall back to prog/object defaults */
>> +	OPTS_SET(pattr, expected_attach_type,
>> +		 OPTS_GET(opts, expected_attach_type, 0) ?: prog->expected_attach_type);
>> +	OPTS_SET(pattr, attach_btf_id, OPTS_GET(opts, attach_btf_id, 0) ?: prog->attach_btf_id);
>> +	OPTS_SET(pattr, attach_btf_obj_fd,
>> +		 OPTS_GET(opts, attach_btf_obj_fd, 0) ?: prog->attach_btf_obj_fd);
>> +	OPTS_SET(pattr, attach_prog_fd, OPTS_GET(opts, attach_prog_fd, 0) ?: prog->attach_prog_fd);
>> +	OPTS_SET(pattr, prog_flags, OPTS_GET(opts, prog_flags, 0) ?: prog->prog_flags);
>> +	OPTS_SET(pattr, prog_ifindex, OPTS_GET(opts, prog_ifindex, 0) ?: prog->prog_ifindex);
>> +	OPTS_SET(pattr, kern_version, OPTS_GET(opts, kern_version, 0) ?: obj->kern_version);
>> +	OPTS_SET(pattr, fd_array, OPTS_GET(opts, fd_array, NULL) ?: obj->fd_array);
>> +	OPTS_SET(pattr, token_fd, OPTS_GET(opts, token_fd, 0) ?: obj->token_fd);
>> +	if (attr.token_fd)
>> +		attr.prog_flags |= BPF_F_TOKEN_FD;
>> +
>> +	/* BTF func/line info */
>> +	if (obj->btf && btf__fd(obj->btf) >= 0) {
>> +		OPTS_SET(pattr, prog_btf_fd, OPTS_GET(opts, prog_btf_fd, 0) ?: btf__fd(obj->btf));
>> +		OPTS_SET(pattr, func_info, OPTS_GET(opts, func_info, NULL) ?: prog->func_info);
>> +		OPTS_SET(pattr, func_info_cnt,
>> +			 OPTS_GET(opts, func_info_cnt, 0) ?: prog->func_info_cnt);
>> +		OPTS_SET(pattr, func_info_rec_size,
>> +			 OPTS_GET(opts, func_info_rec_size, 0) ?: prog->func_info_rec_size);
>> +		OPTS_SET(pattr, line_info, OPTS_GET(opts, line_info, NULL) ?: prog->line_info);
>> +		OPTS_SET(pattr, line_info_cnt,
>> +			 OPTS_GET(opts, line_info_cnt, 0) ?: prog->line_info_cnt);
>> +		OPTS_SET(pattr, line_info_rec_size,
>> +			 OPTS_GET(opts, line_info_rec_size, 0) ?: prog->line_info_rec_size);
>> +	}
>> +
>> +	OPTS_SET(pattr, log_buf, OPTS_GET(opts, log_buf, NULL));
>> +	OPTS_SET(pattr, log_size, OPTS_GET(opts, log_size, 0));
>> +	OPTS_SET(pattr, log_level, OPTS_GET(opts, log_level, 0));
>> +
>
> Can we make it so that cloning  prepared but not loaded programs does
> not load them? The name of the method itself implies the new instance is
> identical to the old one, which is not the case - we're currently
> loading the cloned program even if the original is not loaded. I don't 
> see why for OBJ_PREPARED progrmas this shouldn't be explicitly done by the 
> caller with bpf_prog_load() instead.
Mekes sense, but there are few problems:
we won't be cloning a program, but rather it's
attributes (struct bpf_prog_load_opts); I don't think we can do true
cloning with returning a new struct bpf_program.

So the best we can do is to change to something like
bpf_program__clone_attrs() (or bpf_program__load_attrs()) then in
veristat do:

attrs = bpf_program__clone_attrs(prog)
bpf_prog_load(bpf_program__insn(prog), attrs)

Let's see what option maintainers prefer.
>
> If we do make it so that the cloned program's obj->state is identical to the
> original's let's also add a test that checks that.
>
>> +	/* Resolve BTF attach targets, set sleepable/XDP flags, etc. */
>> +	if (prog->sec_def && prog->sec_def->prog_prepare_load_fn) {
>> +		err = prog->sec_def->prog_prepare_load_fn(prog, pattr, prog->sec_def->cookie);
>> +		if (err)
>> +			return libbpf_err(err);
>> +	}
>> +
>> +	fd = bpf_prog_load(prog->type, prog->name, obj->license, prog->insns, prog->insns_cnt,
>> +			   pattr);
>> +
>> +	return libbpf_err(fd);
>> +}
>> +
>>  #define SEC_DEF(sec_pfx, ptype, atype, flags, ...) {			    \
>>  	.sec = (char *)sec_pfx,						    \
>>  	.prog_type = BPF_PROG_TYPE_##ptype,				    \
>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>> index dfc37a615578..0be34852350f 100644
>> --- a/tools/lib/bpf/libbpf.h
>> +++ b/tools/lib/bpf/libbpf.h
>> @@ -2021,6 +2021,23 @@ LIBBPF_API int libbpf_register_prog_handler(const char *sec,
>>   */
>>  LIBBPF_API int libbpf_unregister_prog_handler(int handler_id);
>>  
>> +/**
>> + * @brief **bpf_program__clone()** loads a single BPF program from a prepared
>> + * BPF object into the kernel, returning its file descriptor.
>> + *
>> + * The BPF object must have been previously prepared with
>> + * **bpf_object__prepare()**. If @opts is provided, any non-zero field
>> + * overrides the defaults derived from the program/object internals.
>> + * If @opts is NULL, all fields are populated automatically.
>> + *
>> + * The returned FD is owned by the caller and must be closed with close().
>> + *
>> + * @param prog BPF program from a prepared object
>> + * @param opts Optional load options; non-zero fields override defaults
>> + * @return program FD (>= 0) on success; negative error code on failure
>> + */
>> +LIBBPF_API int bpf_program__clone(struct bpf_program *prog, const struct bpf_prog_load_opts *opts);
>> +
>>  #ifdef __cplusplus
>>  } /* extern "C" */
>>  #endif
>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>> index d18fbcea7578..e727a54e373a 100644
>> --- a/tools/lib/bpf/libbpf.map
>> +++ b/tools/lib/bpf/libbpf.map
>> @@ -452,6 +452,7 @@ LIBBPF_1.7.0 {
>>  		bpf_map__set_exclusive_program;
>>  		bpf_map__exclusive_program;
>>  		bpf_prog_assoc_struct_ops;
>> +		bpf_program__clone;
>>  		bpf_program__assoc_struct_ops;
>>  		btf__permute;
>>  } LIBBPF_1.6.0;

  reply	other threads:[~2026-02-23 17:59 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 [this message]
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
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=877bs3cpeg.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