From: Alan Maguire <alan.maguire@oracle.com>
To: Fuyu Zhao <zhaofuyu@vivo.com>, bpf@vger.kernel.org
Cc: eddyz87@gmail.com, andrii.nakryiko@gmail.com
Subject: Re: [PATCH bpf-next v6 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
Date: Tue, 1 Sep 2026 08:33:18 +0100 [thread overview]
Message-ID: <b2602c85-c5ad-42d9-9f64-1bb7e1100a85@oracle.com> (raw)
In-Reply-To: <dd0005b7-a8e9-4cb1-9ba4-6f80b950c401@vivo.com>
On 01/09/2026 04:15, Fuyu Zhao wrote:
> 在 2026/8/31 22:46, Alan Maguire 写道:
>>
>>
>> On 31/08/2026 14:38, Fuyu Zhao wrote:
>>>
>>> Add btf_module_names and nr_btf_module_names fields to
>>> bpf_object_open_opts to support selective kernel module BTF loading.
>>>
>>> With btf_module_names:
>>> - when provided, only the specified kernel module BTFs are loaded;
>>> - when an empty list is provided, no module BTFs are loaded;
>>> - when NULL, all module BTFs are loaded as before.
>>>
>>> This avoids unnecessary module BTF loading and reduces BPF object
>>> loading time when only a subset of kernel module BTFs is needed.
>>>
>>> Suggested-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
>>> Signed-off-by: Fuyu Zhao <zhaofuyu@vivo.com>
>>> ---
>>> tools/lib/bpf/libbpf.c | 117 +++++++++++++++++++++++++++++++++++++++++
>>> tools/lib/bpf/libbpf.h | 24 ++++++++-
>>> 2 files changed, 140 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>>> index 514e4e9daa82..e19d0afd2592 100644
>>> --- a/tools/lib/bpf/libbpf.c
>>> +++ b/tools/lib/bpf/libbpf.c
>>> @@ -779,6 +779,9 @@ struct bpf_object {
>>> char *token_path;
>>> int token_fd;
>>>
>>> + char **btf_module_names;
>>> + size_t nr_btf_module_names;
>>> +
>>> char path[];
>>> };
>>>
>>> @@ -5803,6 +5806,103 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
>>> return 0;
>>> }
>>>
>>> +static void bpf_object_free_btf_module_names(struct bpf_object *obj)
>>> +{
>>> + size_t i;
>>> +
>>> + if (!obj->btf_module_names)
>>> + return;
>>> +
>>> + for (i = 0; i < obj->nr_btf_module_names; i++)
>>> + zfree(&obj->btf_module_names[i]);
>>> + zfree(&obj->btf_module_names);
>>> + obj->nr_btf_module_names = 0;
>>> +}
>>> +
>>> +static int bpf_object_init_btf_module_names(struct bpf_object *obj,
>>> + const struct bpf_object_open_opts *opts)
>>> +{
>>> + const char **names;
>>> + size_t i, j, cnt;
>>> + int err;
>>> +
>>> + names = OPTS_GET(opts, btf_module_names, NULL);
>>> + if (!names)
>>> + return 0;
>>> +
>>> + cnt = OPTS_GET(opts, nr_btf_module_names, 0);
>>> +
>>> + /*
>>> + * Allocate one entry for an empty list to distinguish it from the
>>> + * default behavior.
>>> + */
>>> + obj->btf_module_names = calloc(cnt ?: 1,
>>> + sizeof(*obj->btf_module_names));
>>> + if (!obj->btf_module_names)
>>> + return -ENOMEM;
>>> +
>>> + for (i = 0; i < cnt; i++) {
>>> + if (!names[i] || !names[i][0]) {
>>> + pr_warn("invalid kernel module BTF name at index %zu\n", i);
>>> + err = -EINVAL;
>>> + goto err_out;
>>> + }
>>> +
>>> + /*
>>> + * The list is expected to be small, so a simple nested scan is
>>> + * sufficient for duplicate detection.
>>> + */
>>> + for (j = 0; j < i; j++) {
>>> + if (strcmp(obj->btf_module_names[j], names[i]) == 0) {
>>> + pr_warn("duplicate kernel module BTF name '%s'\n",
>>> + names[i]);
>>> + err = -EINVAL;
>>> + goto err_out;
>>> + }
>>> + }
>>> +
>>> + obj->btf_module_names[i] = strdup(names[i]);
>>> + if (!obj->btf_module_names[i]) {
>>> + err = -ENOMEM;
>>> + goto err_out;
>>> + }
>>> +
>>> + obj->nr_btf_module_names++;
>>> + }
>>> + return 0;
>>> +
>>> +err_out:
>>> + bpf_object_free_btf_module_names(obj);
>>> + return err;
>>> +}
>>> +
>>> +static bool is_module_btf_needed(const struct bpf_object *obj, const char *name)
>>> +{
>>> + size_t i;
>>> +
>>> + if (!obj->btf_module_names)
>>> + return true;
>>> +
>>> + for (i = 0; i < obj->nr_btf_module_names; i++) {
>>> + if (strcmp(obj->btf_module_names[i], name) == 0)
>>> + return true;
>>> + }
>>> +
>>> + pr_debug("skipping module BTF '%s', not in btf_module_names\n", name);
>>> + return false;
>>> +}
>>> +
>>> +static bool no_module_btfs_needed(const struct bpf_object *obj)
>>> +{
>>> + return obj->btf_module_names && !obj->nr_btf_module_names;
>>> +}
>>> +
>>> +static bool all_needed_module_btfs_loaded(const struct bpf_object *obj)
>>> +{
>>> + return obj->btf_module_names &&
>>> + obj->nr_btf_module_names == obj->btf_module_cnt;
>>> +}
>>> +
>>> static int load_module_btfs(struct bpf_object *obj)
>>> {
>>> struct bpf_btf_info info;
>>> @@ -5825,6 +5925,9 @@ static int load_module_btfs(struct bpf_object *obj)
>>> if (!kernel_supports(obj, FEAT_MODULE_BTF))
>>> return 0;
>>>
>>> + if (no_module_btfs_needed(obj))
>>> + return 0;
>>> +
>>> while (true) {
>>> err = bpf_btf_get_next_id(id, &id);
>>> if (err && errno == ENOENT)
>>> @@ -5867,6 +5970,11 @@ static int load_module_btfs(struct bpf_object *obj)
>>> continue;
>>> }
>>>
>>> + if (!is_module_btf_needed(obj, name)) {
>>> + close(fd);
>>> + continue;
>>> + }
>>> +
>>> btf = btf_get_from_fd(fd, obj->btf_vmlinux);
>>> err = libbpf_get_error(btf);
>>> if (err) {
>>> @@ -5891,6 +5999,9 @@ static int load_module_btfs(struct bpf_object *obj)
>>> break;
>>> }
>>> obj->btf_module_cnt++;
>>> +
>>> + if (all_needed_module_btfs_loaded(obj))
>>> + break;
>>> }
>>>
>>
>> I was looking at the selftest in patch 2 and was a bit confused as to why
>> specifying just "nonexistent_module" in the module list was a way to test
>> the scenario where we fail to specify a module that is needed ("bpf_testmod"
>> in that case). I think I see the answer here - we don't look at the module
>> count and check it against the number we asked for after the loop completes.
>> In the case of "nonexistent_module" our obj->btf_module_cnt is 0. Since
>> all_needed_module_btfs_loaded() will evaluate to false in that case,
>> we keep iterating over BTF ids.
>>
>> On loop exit shouldn't we check obj->btf_module_cnt in the case we asked
>> for specific modules, and make sure it matches the requested number?
>>
>
> Sorry for causing some confusion here. My original design was to
> treat `btf_module_names` as a filter rather than a list of required
> modules. Even if the number of requested and loaded module BTFs does
> not match after the loop, BPF program loading will still fail after
> `load_module_btfs()` if a required module BTF is missing.
>
> However, your question made me realize that this behavior could be
> made more explicit. It seems better to detect the mismatch and fail
> here, rather than defer the failure to subsequent processing in
> libbpf. I'll add this check and update the corresponding selftests in
> the next version.
>
Sounds good. Thinking about it, it might be good to retain the non-existent
module tolerance optionally somehow (a required_module_cnt perhaps?) since it's
often the case that a module is built-in on one kernel and a module on
another. If a module is builtin ("y" instead of "m"), having a way to say
"load this if it is a module but don't fail if it is not present" would be handy,
since the needed BTF would then be in vmlinux BTF rather than module BTF.
So in such a case the nonexistent_module test would fail if
required_cnt == module_cnt == 1, but not if required_cnt = 0.
Others may disagree so let's wait to see if anyone else has opinions on this,
but in general it may be useful to have flexibility around module BTF presence
as it varies greatly across configs.
Thanks!
Alan
next prev parent reply other threads:[~2026-09-01 7:33 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 13:38 [PATCH bpf-next v6 0/2] libbpf: Improve BPF load performance by selectively loading module BTFs Fuyu Zhao
2026-08-31 13:38 ` [PATCH bpf-next v6 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
2026-08-31 14:46 ` bot+bpf-ci
2026-09-01 3:55 ` Fuyu Zhao
2026-08-31 14:46 ` Alan Maguire
2026-09-01 3:15 ` Fuyu Zhao
2026-09-01 7:33 ` Alan Maguire [this message]
2026-09-03 0:12 ` Andrii Nakryiko
2026-09-03 12:52 ` Fuyu Zhao
2026-08-31 13:38 ` [PATCH bpf-next v6 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
2026-08-31 14:46 ` bot+bpf-ci
2026-09-01 3:56 ` Fuyu Zhao
2026-09-03 0:12 ` Andrii Nakryiko
2026-09-03 12:52 ` Fuyu Zhao
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=b2602c85-c5ad-42d9-9f64-1bb7e1100a85@oracle.com \
--to=alan.maguire@oracle.com \
--cc=andrii.nakryiko@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=eddyz87@gmail.com \
--cc=zhaofuyu@vivo.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