mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH bpf-next v5 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs
@ 2026-08-26 13:14 Fuyu Zhao
  2026-08-26 13:14 ` [PATCH bpf-next v5 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
  2026-08-26 13:14 ` [PATCH bpf-next v5 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
  0 siblings, 2 replies; 5+ messages in thread
From: Fuyu Zhao @ 2026-08-26 13:14 UTC (permalink / raw)
  To: bpf, eddyz87, andrii.nakryiko, alan.maguire
  Cc: andrii, ast, daniel, memxor, martin.lau, song, yonghong.song,
	jolsa, emil, ihor.solodrai, shuah, yatsenko, linux-kernel,
	linux-kselftest, Fuyu Zhao

Currently, during BPF object loading, load_module_btfs() unconditionally
iterates over all kernel module BTFs and loads each one. This introduces
unnecessary overhead when a BPF program only needs BTFs from a specific,
small subset of modules. In environments with hundreds of modules,
loading all module BTFs can measurably increase the loading time.

In our Android testing, BPF programs are loaded on demand rather than
preloaded to avoid unnecessary memory usage from unused programs. With
93 module BTFs present, the total BPF loading time exceeds 300 ms, with
module BTF loading accounting for around 69% of the total loading time.

The existing module qualification in SEC(), such as
SEC("fentry/mymod:foo"), does not address this issue. It only affects
BTF ID lookup after module BTFs have already been loaded by
load_module_btfs(), and does not reduce the number of module BTFs loaded.

This series adds btf_module_names and nr_btf_module_names fields to
bpf_object_open_opts, allowing users to specify a list of kernel modules
whose BTFs should be loaded. libbpf uses this information to skip
unrelated module BTFs during iteration and stops iterating once all
requested module BTFs have been loaded. Without these options, the
existing behavior remains unchanged.

Performance impact (BPF skeleton open and load time):

  Modules loaded | Default (load all) | With btf_module_names | Speedup
  ---------------|--------------------|-----------------------|--------
  1              | 35.6 ms            | 35.6 ms               | Baseline
  10             | 37.2 ms            | 35.7 ms               | +4.0%
  100            | 46.7 ms            | 36.5 ms               | +21.8%
  300            | 65.2 ms            | 38.5 ms               | +40.9%

Changelog:
v5:	
- Add no_module_btfs_needed() to skip loading module BTFs early when the
  module BTF name list is empty. (sashiko-bot)
- Document struct_ops as an affected use case for btf_module_names.
  (bot+bpf-ci)
- Add invalid input tests and assert expected error codes for negative
  cases. (bot+bpf-ci)

v4:
- Link: https://lore.kernel.org/bpf/20260824125310.1384-1-zhaofuyu@vivo.com/
- Simplify and rename newly added struct members and their documentation.
  (Eduard)
- Use array search instead of a hashmap for module name matching to
  simplify the code. (Eduard)
- Allow an empty module name list to skip loading all module BTFs.
  (Eduard, sashiko-bot)
- Move btf_module_names option handling before bpf_object__elf_init().
  (Eduard)
- Rename internal helpers to follow libbpf naming conventions. (Andrii)
- Reject duplicate module names and document the rejection rule. (Andrii)
- Replace log interception with functional tests and simplify selftests.
  (Andrii)

v3:
- Link: https://lore.kernel.org/bpf/20260819090426.267-1-zhaofuyu@vivo.com/
- Use bpf_object_open_opts to specify kernel module BTFs to load rather
  than introducing a new .kmod_btfs ELF section, as suggested by Andrii.

v2:
- Link: https://lore.kernel.org/bpf/20260813032613.2755-1-zhaofuyu@vivo.com/
- Addressed issues with allocation error handling, multiple .kmod_btfs
  sections, the transient stack pointer in is_kmod_btf_needed(), and
  selftest dependency and comment formatting. (sashiko-bot)
- Removed KMODS_BTF_LOADED and KMODS_BTF_UNLOADED and simplified the
  related logic.

v1:
- Link: https://lore.kernel.org/bpf/20260806042042.3239428-1-zhaofuyu@vivo.com/

Fuyu Zhao (2):
  libbpf: support selective kernel module BTF loading via
    bpf_object_open_opts
  selftests/bpf: add tests for selective module BTF loading

 tools/lib/bpf/libbpf.c                        | 113 ++++++++++++++
 tools/lib/bpf/libbpf.h                        |  24 ++-
 .../bpf/prog_tests/btf_module_names.c         | 139 ++++++++++++++++++
 .../selftests/bpf/progs/btf_module_names.c    |  13 ++
 4 files changed, 288 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_module_names.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf_module_names.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH bpf-next v5 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
  2026-08-26 13:14 [PATCH bpf-next v5 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs Fuyu Zhao
@ 2026-08-26 13:14 ` Fuyu Zhao
  2026-08-26 14:13   ` bot+bpf-ci
  2026-08-26 13:14 ` [PATCH bpf-next v5 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
  1 sibling, 1 reply; 5+ messages in thread
From: Fuyu Zhao @ 2026-08-26 13:14 UTC (permalink / raw)
  To: bpf, eddyz87, andrii.nakryiko, alan.maguire
  Cc: andrii, ast, daniel, memxor, martin.lau, song, yonghong.song,
	jolsa, emil, ihor.solodrai, shuah, yatsenko, linux-kernel,
	linux-kselftest, Fuyu Zhao

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 | 113 +++++++++++++++++++++++++++++++++++++++++
 tools/lib/bpf/libbpf.h |  24 ++++++++-
 2 files changed, 136 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 514e4e9daa82..1f061f4873b5 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,99 @@ 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;
+		}
+
+		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 +5921,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 +5966,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 +5995,9 @@ static int load_module_btfs(struct bpf_object *obj)
 			break;
 		}
 		obj->btf_module_cnt++;
+
+		if (all_needed_module_btfs_loaded(obj))
+			break;
 	}
 
 	if (err) {
@@ -8508,6 +8615,10 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf,
 		}
 	}
 
+	err = bpf_object_init_btf_module_names(obj, opts);
+	if (err)
+		goto out;
+
 	err = bpf_object__elf_init(obj);
 	err = err ? : bpf_object__elf_collect(obj);
 	err = err ? : bpf_object__collect_externs(obj);
@@ -9629,6 +9740,8 @@ void bpf_object__close(struct bpf_object *obj)
 		close(obj->jumptable_maps[i].fd);
 	zfree(&obj->jumptable_maps);
 
+	bpf_object_free_btf_module_names(obj);
+
 	free(obj);
 }
 
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b965ad571540..b8c1d98d69d8 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -224,10 +224,32 @@ struct bpf_object_open_opts {
 	 * point (/sys/fs/bpf), in case this default behavior is undesirable.
 	 */
 	const char *bpf_token_path;
+	/*
+	 * Optional list of kernel module names whose BTFs should be loaded.
+	 * nr_btf_module_names specifies the number of entries in
+	 * btf_module_names.
+	 *
+	 * With btf_module_names:
+	 * - when provided, only the BTFs of the specified modules are loaded;
+	 * - when an empty list is provided, no module BTFs are loaded;
+	 * - when NULL, all module BTFs are loaded as before.
+	 *
+	 * The list must not contain duplicate entries; otherwise -EINVAL is
+	 * returned.
+	 *
+	 * This affects:
+	 * - BPF CO-RE relocations against types defined in modules;
+	 * - BTF-based resolution of function attach targets for
+	 *   fentry/fexit/fmod_ret/freplace/LSM programs;
+	 * - struct_ops kernel type resolution;
+	 * - extern (ksym) resolution for kernel symbols defined in modules.
+	 */
+	const char **btf_module_names;
+	size_t nr_btf_module_names;
 
 	size_t :0;
 };
-#define bpf_object_open_opts__last_field bpf_token_path
+#define bpf_object_open_opts__last_field nr_btf_module_names
 
 /**
  * @brief **bpf_object__open()** creates a bpf_object by opening
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH bpf-next v5 2/2] selftests/bpf: add tests for selective module BTF loading
  2026-08-26 13:14 [PATCH bpf-next v5 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs Fuyu Zhao
  2026-08-26 13:14 ` [PATCH bpf-next v5 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
@ 2026-08-26 13:14 ` Fuyu Zhao
  2026-08-26 14:13   ` bot+bpf-ci
  1 sibling, 1 reply; 5+ messages in thread
From: Fuyu Zhao @ 2026-08-26 13:14 UTC (permalink / raw)
  To: bpf, eddyz87, andrii.nakryiko, alan.maguire
  Cc: andrii, ast, daniel, memxor, martin.lau, song, yonghong.song,
	jolsa, emil, ihor.solodrai, shuah, yatsenko, linux-kernel,
	linux-kselftest, Fuyu Zhao

Add selftests covering selective kernel module BTF loading through
bpf_object_open_opts.

The tests verify that:
- the existing behavior is preserved when btf_module_names is not
  specified, and loading succeeds when the required module BTF is
  specified;
- a required module BTF is skipped when the module is not specified in
  btf_module_names;
- an empty btf_module_names list skips loading all module BTFs;
- invalid module BTF name lists are rejected.

Signed-off-by: Fuyu Zhao <zhaofuyu@vivo.com>
---
 .../bpf/prog_tests/btf_module_names.c         | 139 ++++++++++++++++++
 .../selftests/bpf/progs/btf_module_names.c    |  13 ++
 2 files changed, 152 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_module_names.c
 create mode 100644 tools/testing/selftests/bpf/progs/btf_module_names.c

diff --git a/tools/testing/selftests/bpf/prog_tests/btf_module_names.c b/tools/testing/selftests/bpf/prog_tests/btf_module_names.c
new file mode 100644
index 000000000000..3dc72bef014c
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_module_names.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "btf_module_names.skel.h"
+
+static void btf_module_names_load(void)
+{
+	struct btf_module_names *skel = NULL;
+	int ret;
+	static const char *mod_names[] = { "bpf_testmod" };
+
+	LIBBPF_OPTS(bpf_object_open_opts, opts,
+		.btf_module_names = mod_names,
+		.nr_btf_module_names = 1,
+	);
+
+	/* Verify backward compatibility. */
+	skel = btf_module_names__open_opts(NULL);
+	if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts default"))
+		goto out;
+
+	ret = btf_module_names__load(skel);
+	ASSERT_OK(ret, "btf_module_names__load default");
+
+	btf_module_names__destroy(skel);
+	skel = NULL;
+
+	skel = btf_module_names__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts"))
+		goto out;
+
+	ret = btf_module_names__load(skel);
+	ASSERT_OK(ret, "btf_module_names__load");
+out:
+	btf_module_names__destroy(skel);
+}
+
+/*
+ * Verify that an unrequested module BTF is skipped. The BPF program
+ * requires the BTF of bpf_testmod, but bpf_testmod is not specified in
+ * btf_module_names, so its BTF is skipped and the BPF program fails to load.
+ */
+static void btf_module_names_skip(void)
+{
+	struct btf_module_names *skel = NULL;
+	int ret;
+	static const char *mod_names[] = { "module_nonexist" };
+
+	LIBBPF_OPTS(bpf_object_open_opts, opts,
+		.btf_module_names = mod_names,
+		.nr_btf_module_names = 1,
+	);
+
+	skel = btf_module_names__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts"))
+		goto out;
+
+	ret = btf_module_names__load(skel);
+	ASSERT_EQ(ret, -ESRCH, "btf_module_names__load");
+
+out:
+	btf_module_names__destroy(skel);
+}
+
+/*
+ * Verify that an empty filter skips loading all module BTFs. The BPF
+ * program requires bpf_testmod BTF, so it fails to load.
+ */
+static void btf_module_names_empty(void)
+{
+	struct btf_module_names *skel = NULL;
+	int ret;
+	static const char *mod_names[] = { "" };
+
+	LIBBPF_OPTS(bpf_object_open_opts, opts,
+		.btf_module_names = mod_names,
+	);
+
+	skel = btf_module_names__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "btf_module_names__open_opts empty"))
+		goto out;
+
+	ret = btf_module_names__load(skel);
+	ASSERT_EQ(ret, -ESRCH, "btf_module_names__load empty");
+
+out:
+	btf_module_names__destroy(skel);
+}
+
+static void btf_module_names_invalid(void)
+{
+	struct btf_module_names *skel = NULL;
+	const char *names[] = { NULL };
+	const char *empty_names[] = { "" };
+	const char *duplicate_names[] = {
+		"bpf_testmod", "bpf_testmod",
+	};
+	LIBBPF_OPTS(bpf_object_open_opts, opts,
+		.btf_module_names = names,
+		.nr_btf_module_names = 1,
+	);
+
+	skel = btf_module_names__open_opts(&opts);
+	ASSERT_EQ(libbpf_get_error(skel), -EINVAL,
+		  "btf_module_names__open_opts null");
+	btf_module_names__destroy(skel);
+
+	opts.btf_module_names = empty_names;
+	skel = btf_module_names__open_opts(&opts);
+	ASSERT_EQ(libbpf_get_error(skel), -EINVAL,
+		  "btf_module_names__open_opts empty");
+	btf_module_names__destroy(skel);
+
+	opts.btf_module_names = duplicate_names;
+	opts.nr_btf_module_names = 2;
+	skel = btf_module_names__open_opts(&opts);
+	ASSERT_EQ(libbpf_get_error(skel), -EINVAL,
+		  "btf_module_names__open_opts duplicate name");
+	btf_module_names__destroy(skel);
+}
+
+void test_btf_module_names(void)
+{
+	if (!env.has_testmod) {
+		test__skip();
+		return;
+	}
+
+	if (test__start_subtest("btf_module_names_load"))
+		btf_module_names_load();
+
+	if (test__start_subtest("btf_module_names_skip"))
+		btf_module_names_skip();
+
+	if (test__start_subtest("btf_module_names_empty"))
+		btf_module_names_empty();
+
+	if (test__start_subtest("btf_module_names_invalid"))
+		btf_module_names_invalid();
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_module_names.c b/tools/testing/selftests/bpf/progs/btf_module_names.c
new file mode 100644
index 000000000000..232adc0f33f6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_module_names.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+SEC("fentry/bpf_testmod_loop_test")
+int BPF_PROG(test_btf_module_names)
+{
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next v5 2/2] selftests/bpf: add tests for selective module BTF loading
  2026-08-26 13:14 ` [PATCH bpf-next v5 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
@ 2026-08-26 14:13   ` bot+bpf-ci
  0 siblings, 0 replies; 5+ messages in thread
From: bot+bpf-ci @ 2026-08-26 14:13 UTC (permalink / raw)
  To: zhaofuyu, bpf, eddyz87, andrii.nakryiko, alan.maguire
  Cc: andrii, ast, daniel, memxor, martin.lau, song, yonghong.song,
	jolsa, emil, ihor.solodrai, shuah, yatsenko, linux-kernel,
	linux-kselftest, zhaofuyu, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 2221 bytes --]

> diff --git a/tools/testing/selftests/bpf/prog_tests/btf_module_names.c b/tools/testing/selftests/bpf/prog_tests/btf_module_names.c
> new file mode 100644
> index 0000000000000..3dc72bef014c8
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/btf_module_names.c
> @@ -0,0 +1,139 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <test_progs.h>
> +#include "btf_module_names.skel.h"
> +
> +static void btf_module_names_load(void)
> +{
> +	struct btf_module_names *skel = NULL;
> +	int ret;
> +	static const char *mod_names[] = { "bpf_testmod" };
> +
> +	LIBBPF_OPTS(bpf_object_open_opts, opts,
> +		.btf_module_names = mod_names,
> +		.nr_btf_module_names = 1,
> +	);

[ ... ]

> +static void btf_module_names_skip(void)
> +{
> +	struct btf_module_names *skel = NULL;
> +	int ret;
> +	static const char *mod_names[] = { "module_nonexist" };
> +
> +	LIBBPF_OPTS(bpf_object_open_opts, opts,
> +		.btf_module_names = mod_names,
> +		.nr_btf_module_names = 1,
> +	);

[ ... ]

> +static void btf_module_names_empty(void)
> +{
> +	struct btf_module_names *skel = NULL;
> +	int ret;
> +	static const char *mod_names[] = { "" };
> +
> +	LIBBPF_OPTS(bpf_object_open_opts, opts,
> +		.btf_module_names = mod_names,
> +	);

[ ... ]

> +static void btf_module_names_invalid(void)
> +{
> +	struct btf_module_names *skel = NULL;
> +	const char *names[] = { NULL };
> +	const char *empty_names[] = { "" };
> +	const char *duplicate_names[] = {
> +		"bpf_testmod", "bpf_testmod",
> +	};
> +	LIBBPF_OPTS(bpf_object_open_opts, opts,
> +		.btf_module_names = names,
> +		.nr_btf_module_names = 1,
> +	);

This isn't a bug, but could the three static const char *mod_names[]
declarations in btf_module_names_load(), btf_module_names_skip(), and
btf_module_names_empty() just be automatic arrays, matching the non-static
declarations in btf_module_names_invalid()? Since libbpf strdup()s the
entries, the static storage doesn't add anything beyond inconsistency within
this new file.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/32974547426

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf-next v5 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
  2026-08-26 13:14 ` [PATCH bpf-next v5 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
@ 2026-08-26 14:13   ` bot+bpf-ci
  0 siblings, 0 replies; 5+ messages in thread
From: bot+bpf-ci @ 2026-08-26 14:13 UTC (permalink / raw)
  To: zhaofuyu, bpf, eddyz87, andrii.nakryiko, alan.maguire
  Cc: andrii, ast, daniel, memxor, martin.lau, song, yonghong.song,
	jolsa, emil, ihor.solodrai, shuah, yatsenko, linux-kernel,
	linux-kselftest, zhaofuyu, ast, andrii, daniel, martin.lau,
	eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 5545 bytes --]

> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index b749c01742ee0..a466aabf35e1c 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c

[ ... ]

> @@ -5851,6 +5854,99 @@ 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)
> +{

Andrii Nakryiko raised a concern in v3 about the naming convention used
here. Internal helper functions should not use double underscore naming, as
that convention is reserved for extern method-like APIs. Would
bpf_object_free_module_names and bpf_object_init_module_names be more
appropriate, following the single underscore pattern for internal helpers?

Reference: https://lore.kernel.org/bpf/CAEf4BzbbdjeSW88EEk2xRvAxq-HWUJ7qTw4BYnL3kLH0f0uhwg@mail.gmail.com/

> +	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;
> +		}
> +
> +		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;
> +			}
> +		}

Andrii Nakryiko suggested in v3 that the module name list should be sorted
and duplicates detected in a single pass rather than using the O(n^2)
nested loop approach. The suggestion was to error out with -EINVAL if
duplicates are found after sorting. Would this optimization be worth
implementing?

Reference: https://lore.kernel.org/bpf/CAEf4BzbbdjeSW88EEk2xRvAxq-HWUJ7qTw4BYnL3kLH0f0uhwg@mail.gmail.com/

> +
> +		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;
> +}

[ ... ]

> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index b965ad5715403..b8c1d98d69d85 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -224,10 +224,32 @@ struct bpf_object_open_opts {
>  	 * point (/sys/fs/bpf), in case this default behavior is undesirable.
>  	 */
>  	const char *bpf_token_path;
> +	/*
> +	 * Optional list of kernel module names whose BTFs should be loaded.
> +	 * nr_btf_module_names specifies the number of entries in
> +	 * btf_module_names.
> +	 *
> +	 * With btf_module_names:
> +	 * - when provided, only the BTFs of the specified modules are loaded;
> +	 * - when an empty list is provided, no module BTFs are loaded;
> +	 * - when NULL, all module BTFs are loaded as before.
> +	 *
> +	 * The list must not contain duplicate entries; otherwise -EINVAL is
> +	 * returned.
> +	 *
> +	 * This affects:
> +	 * - BPF CO-RE relocations against types defined in modules;
> +	 * - BTF-based resolution of function attach targets for
> +	 *   fentry/fexit/fmod_ret/freplace/LSM programs;
> +	 * - struct_ops kernel type resolution;
> +	 * - extern (ksym) resolution for kernel symbols defined in modules.
> +	 */

Looking at the 'This affects:' list and the -EINVAL contract, are there two
things missing?

First, load_module_btfs() has four callers in libbpf.c, but the list above
covers only three: bpf_core_find_cands (CO-RE relocations), find_kernel_btf_id
(fentry/fexit/etc), and find_ksym_btf_id (extern resolution). The fourth
caller is find_attach_module(), which is reached from tracing_multi_mod_fd()
and collect_func_ids_by_glob() for 'module:glob' attach patterns:

    mod = find_attach_module(obj, pattern);
    if (!mod) {
        err = -EINVAL;
        goto cleanup;
    }

So a user who sets btf_module_names and then attaches a tracing-multi
program with 'mymod:foo*' gets -EINVAL if mymod is not in the list, but the
documented behavior above doesn't hint that this option affects multi-attach
module resolution.

Second, the -EINVAL contract mentions only duplicates, but
bpf_object_init_btf_module_names() also rejects NULL and empty-string
entries:

    if (!names[i] || !names[i][0]) {
        pr_warn("invalid kernel module BTF name at index %zu\n", i);
        err = -EINVAL;

Would it be worth clarifying both of these in the documentation?

> +	const char **btf_module_names;
> +	size_t nr_btf_module_names;
>
>  	size_t :0;
>  };
> -#define bpf_object_open_opts__last_field bpf_token_path
> +#define bpf_object_open_opts__last_field nr_btf_module_names


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/32974547426

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-26 14:13 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-26 13:14 [PATCH bpf-next v5 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs Fuyu Zhao
2026-08-26 13:14 ` [PATCH bpf-next v5 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
2026-08-26 14:13   ` bot+bpf-ci
2026-08-26 13:14 ` [PATCH bpf-next v5 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
2026-08-26 14:13   ` bot+bpf-ci

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®