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 --- 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] + #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