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;
next prev parent 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