[PATCH bpf-next v5 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
From: Fuyu Zhao
Date: Wed Aug 26 2026 - 09:15:06 EST
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@xxxxxxxxx>
Signed-off-by: Fuyu Zhao <zhaofuyu@xxxxxxxx>
---
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