Re: [RFC PATCH bpf-next v2 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section
Fuyu Zhao <[email protected]>
| Newsgroups | org.kernel.vger.linux-kselftest,org.kernel.vger.bpf,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On 8/15/2026 3:57 AM, Andrii Nakryiko wrote: > On Wed, Aug 12, 2026 at 8:26 PM Fuyu Zhao <[email protected]> wrote: >> >> Add support for a new ELF section ".kmod_btfs" that allows BPF programs >> to declare which kernel modules need BTF loading. This avoids loading >> all module BTFs and speeds up program load when only a subset of modules >> is needed. >> >> Specifically, this patch introduces two internal functions: >> - bpf_object__collect_kmod_btf_names(): parses the ".kmod_btfs" >> section and collects the declared module names. >> - is_kmod_btf_needed(): determines whether a given module's BTF >> should be loaded, allowing libbpf to skip unneeded modules. >> >> Signed-off-by: Fuyu Zhao <[email protected]> >> --- >> tools/lib/bpf/bpf_helpers.h | 14 +++++ >> tools/lib/bpf/libbpf.c | 106 ++++++++++++++++++++++++++++++++++++ >> 2 files changed, 120 insertions(+) >> >> diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h >> index 9d160b5b9c0e..171ea055cd32 100644 >> --- a/tools/lib/bpf/bpf_helpers.h >> +++ b/tools/lib/bpf/bpf_helpers.h >> @@ -188,6 +188,20 @@ enum libbpf_tristate { >> TRI_MODULE = 2, >> }; >> >> +/* Helper typedef for declaring kernel module names that need BTF loading. >> + * >> + * Usage: define an array in the ".kmod_btfs" ELF section to specify >> + * which modules need BTF loading: >> + * >> + * DEFINE_KMOD_BTFS(_needed_kmods)= { "module1", "module2", ... }; >> + * >> + * This avoids unnecessary BTF loading and speeds up the BPF program >> + * load process. >> + */ >> +#define KMOD_NAME_LEN 64 >> +#define DEFINE_KMOD_BTFS(name) \ >> + SEC(".kmod_btfs") char name[][KMOD_NAME_LEN] >> + > > I don't really like another special ELF section just to provide a > custom list of modules. > > If we have to do a customization of BTF searching behavior of libbpf, > let's do this through bpf_object_open_opts struct and pass NULL or a > empty/non-empty list of kernel modules, or whatever other appropriate > way to specify this. > > But in any case, extending ELF format for BPF object file for this > seems like an overkill and overcomplication. > > pw-bot: cr > Hi Andrii, Thanks for the feedback. I also considered passing the module list through bpf_object_open_opts. The main reason for using an ELF section is that it keeps the module dependency information together with the BPF object and requires minimal changes for existing users. However, I understand the concern that extending the ELF format introduces additional complexity. I'll explore bpf_object_open_opts or other more suitable approaches. Best regards, Fuyu >> #define __kconfig __attribute__((section(".kconfig"))) >> #define __ksym __attribute__((section(".ksyms"))) >> #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted"))) >> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c >> index 514e4e9daa82..9fe8a653e8b6 100644 >> --- a/tools/lib/bpf/libbpf.c >> +++ b/tools/lib/bpf/libbpf.c >> @@ -548,6 +548,7 @@ struct bpf_struct_ops { >> #define STRUCT_OPS_SEC ".struct_ops" >> #define STRUCT_OPS_LINK_SEC ".struct_ops.link" >> #define ARENA_SEC ".addr_space.1" >> +#define KMODS_BTFS_SEC ".kmod_btfs" >> >> enum libbpf_map_type { >> LIBBPF_MAP_UNSPEC, >> @@ -703,6 +704,9 @@ enum bpf_object_state { >> OBJ_LOADED, >> }; >> >> +/* Should match the definition in bpf_helpers.h */ >> +#define KMOD_NAME_LEN 64 >> + >> struct bpf_object { >> char name[BPF_OBJ_NAME_LEN]; >> char license[64]; >> @@ -779,6 +783,14 @@ struct bpf_object { >> char *token_path; >> int token_fd; >> >> + /* kernel module BTFs to load, declared in ".kmod_btfs" ELF section */ >> + struct { >> + char (*data)[KMOD_NAME_LEN]; >> + size_t nr_names; >> + size_t nr_loaded; >> + struct hashmap *hashmap; >> + } *kmod_btfs; >> + >> char path[]; >> }; >> >> @@ -901,6 +913,75 @@ bpf_object__init_prog(struct bpf_object *obj, struct bpf_program *prog, >> return -ENOMEM; >> } >> >> +static size_t mod_name_hash_fn(long key, void *ctx) >> +{ >> + return str_hash((char *)key); >> +} >> + >> +static bool mod_name_equal_fn(long key1, long key2, void *ctx) >> +{ >> + return strcmp((char *)key1, (char *)key2) == 0; >> +} >> + >> +static int >> +bpf_object__collect_kmod_btf_names(struct bpf_object *obj, Elf_Data *sec_data, >> + const char *sec_name) >> +{ >> + int module_cnt, i, err = 0; >> + >> + if (obj->kmod_btfs) { >> + pr_warn("sec '%s': duplicate sections detected\n", sec_name); >> + return -EEXIST; >> + } >> + >> + if (sec_data->d_size % KMOD_NAME_LEN != 0) { >> + pr_warn("sec '%s': size %zu should be multiple of %d\n", >> + sec_name, sec_data->d_size, KMOD_NAME_LEN); >> + return -EINVAL; >> + } >> + >> + module_cnt = sec_data->d_size / KMOD_NAME_LEN; >> + obj->kmod_btfs = calloc(1, sizeof(*obj->kmod_btfs)); >> + if (!obj->kmod_btfs) >> + return -ENOMEM; >> + >> + obj->kmod_btfs->data = calloc(module_cnt, KMOD_NAME_LEN); >> + if (!obj->kmod_btfs->data) { >> + err = -ENOMEM; >> + goto err_out; >> + } >> + memcpy(obj->kmod_btfs->data, sec_data->d_buf, sec_data->d_size); >> + >> + obj->kmod_btfs->hashmap = hashmap__new(mod_name_hash_fn, >> + mod_name_equal_fn, NULL); >> + if (IS_ERR(obj->kmod_btfs->hashmap)) { >> + err = PTR_ERR(obj->kmod_btfs->hashmap); >> + goto err_out; >> + } >> + >> + for (i = 0; i < module_cnt; i++) { >> + obj->kmod_btfs->data[i][KMOD_NAME_LEN - 1] = '\0'; >> + if (hashmap__find(obj->kmod_btfs->hashmap, >> + obj->kmod_btfs->data[i], NULL)) { >> + pr_warn("sec '%s': ignored duplicate module '%s'\n", >> + sec_name, obj->kmod_btfs->data[i]); >> + continue; >> + } >> + err = hashmap__set(obj->kmod_btfs->hashmap, obj->kmod_btfs->data[i], >> + 0, NULL, NULL); >> + if (err) >> + goto err_out; >> + obj->kmod_btfs->nr_names++; >> + } >> + return 0; >> + >> +err_out: >> + hashmap__free(obj->kmod_btfs->hashmap); >> + zfree(&obj->kmod_btfs->data); >> + zfree(&obj->kmod_btfs); >> + return err; >> +} >> + >> static int >> bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data, >> const char *sec_name, int sec_idx) >> @@ -4034,6 +4115,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj) >> memcpy(obj->jumptables_data, data->d_buf, data->d_size); >> obj->jumptables_data_sz = data->d_size; >> obj->efile.jumptables_data_shndx = idx; >> + } else if (strcmp(name, KMODS_BTFS_SEC) == 0) { >> + err = bpf_object__collect_kmod_btf_names(obj, data, name); >> + if (err) >> + return err; >> } else { >> pr_info("elf: skipping unrecognized data section(%d) %s\n", >> idx, name); >> @@ -5803,6 +5888,11 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand, >> return 0; >> } >> >> +static bool is_kmod_btf_needed(struct bpf_object *obj, const char *name) >> +{ >> + return hashmap_find(obj->kmod_btfs->hashmap, (long)name, NULL); >> +} >> + >> static int load_module_btfs(struct bpf_object *obj) >> { >> struct bpf_btf_info info; >> @@ -5867,6 +5957,12 @@ static int load_module_btfs(struct bpf_object *obj) >> continue; >> } >> >> + if (obj->kmod_btfs && obj->kmod_btfs->hashmap && >> + !is_kmod_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 +5987,10 @@ static int load_module_btfs(struct bpf_object *obj) >> break; >> } >> obj->btf_module_cnt++; >> + >> + if (obj->kmod_btfs && >> + obj->kmod_btfs->nr_names == obj->kmod_btfs->nr_loaded) >> + break; >> } >> >> if (err) { >> @@ -9030,6 +9130,12 @@ static void bpf_object_cleanup_btf(struct bpf_object *obj) >> /* clean up vmlinux BTF */ >> btf__free(obj->btf_vmlinux); >> obj->btf_vmlinux = NULL; >> + >> + if (obj->kmod_btfs) { >> + hashmap__free(obj->kmod_btfs->hashmap); >> + zfree(&obj->kmod_btfs->data); >> + zfree(&obj->kmod_btfs); >> + } >> } >> >> static void bpf_object_post_load_cleanup(struct bpf_object *obj) >> -- >> 2.34.1 >>