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 <[email protected]>
Signed-off-by: Fuyu Zhao <[email protected]>
---
 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


Reply via email to