bpf-util: add helper to probe the kernel for a kfunc
Add bpf_kernel_has_kfunc() which checks whether the kernel's vmlinux BTF advertises a function of a given name. The three btf__* symbols it needs are optional in the dlopen table. btf__load_vmlinux_btf() only exists since libbpf 0.5 and a libbpf that lacks any of them should degrade this probe, not fail dlopen_bpf() and with it all of BPF. Note, libbpf < 1.0 returns error pointers instead of NULL, so the result is checked with bpf_get_error_translated(). Finding the name doesn't tell us all that much though. It tells us that the function exists, not that it is registered as a kfunc for the program type we care about. So callers still have to be prepared for the program load to fail. Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
+39
-2
@@ -54,6 +54,9 @@ static int missing_bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *
|
||||
return -ENOSYS;
|
||||
}
|
||||
DLSYM_PROTOTYPE(bpf_token_create) = missing_bpf_token_create;
|
||||
DLSYM_PROTOTYPE(btf__find_by_name_kind) = NULL;
|
||||
DLSYM_PROTOTYPE(btf__free) = NULL;
|
||||
DLSYM_PROTOTYPE(btf__load_vmlinux_btf) = NULL;
|
||||
DLSYM_PROTOTYPE(libbpf_set_print) = NULL;
|
||||
DLSYM_PROTOTYPE(ring_buffer__epoll_fd) = NULL;
|
||||
DLSYM_PROTOTYPE(ring_buffer__free) = NULL;
|
||||
@@ -143,12 +146,18 @@ int dlopen_bpf(int log_level) {
|
||||
"Neither libbpf.so.1 nor libbpf.so.0 are installed, cgroup BPF features disabled.");
|
||||
|
||||
/* Version-specific symbols: bpf_create_map exists only in libbpf < 1.0; bpf_map_create and
|
||||
* bpf_object__next_map only in 0.7+. bpf_token_create only in 1.5+. Unresolved prototypes keep
|
||||
* their initializers (NULL, or a fallback returning -ENOSYS for bpf_token_create). */
|
||||
* bpf_object__next_map only in 0.7+. bpf_token_create only in 1.5+. btf__load_vmlinux_btf only in
|
||||
* 0.5+; its two btf__* companions are older, but keep the whole probe trio optional so a missing
|
||||
* symbol degrades bpf_kernel_has_kfunc() instead of failing dlopen_bpf() entirely. Unresolved
|
||||
* prototypes keep their initializers (NULL, or a fallback returning -ENOSYS for
|
||||
* bpf_token_create). */
|
||||
DLSYM_OPTIONAL(bpf_dl, bpf_create_map);
|
||||
DLSYM_OPTIONAL(bpf_dl, bpf_map_create);
|
||||
DLSYM_OPTIONAL(bpf_dl, bpf_object__next_map);
|
||||
DLSYM_OPTIONAL(bpf_dl, bpf_token_create);
|
||||
DLSYM_OPTIONAL(bpf_dl, btf__find_by_name_kind);
|
||||
DLSYM_OPTIONAL(bpf_dl, btf__free);
|
||||
DLSYM_OPTIONAL(bpf_dl, btf__load_vmlinux_btf);
|
||||
|
||||
/* We set the print helper unconditionally. Otherwise libbpf will emit not useful log messages. */
|
||||
(void) sym_libbpf_set_print(bpf_print_func);
|
||||
@@ -177,4 +186,32 @@ int bpf_get_error_translated(const void *ptr) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct btf *, sym_btf__free, NULL);
|
||||
|
||||
bool bpf_kernel_has_kfunc(const char *name) {
|
||||
_cleanup_(sym_btf__freep) struct btf *btf = NULL;
|
||||
int id, r;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (dlopen_bpf(LOG_DEBUG) < 0)
|
||||
return false;
|
||||
|
||||
if (!sym_btf__load_vmlinux_btf || !sym_btf__find_by_name_kind || !sym_btf__free) {
|
||||
log_debug("libbpf too old to probe the kernel BTF for functions, assuming %s() is unavailable.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
btf = sym_btf__load_vmlinux_btf();
|
||||
r = bpf_get_error_translated(btf); /* libbpf < 1.0 returns error pointers, not NULL; btf__free() copes with both */
|
||||
if (r != 0) {
|
||||
log_debug_errno(r, "Failed to load vmlinux BTF, assuming kfunc %s() is unavailable: %m", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
id = sym_btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
|
||||
log_debug("Kernel BTF %s %s().", id > 0 ? "provides" : "lacks", name);
|
||||
return id > 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#endif
|
||||
|
||||
#include <bpf/bpf.h> /* IWYU pragma: export */
|
||||
#include <bpf/btf.h> /* IWYU pragma: export */
|
||||
#include <bpf/libbpf.h> /* IWYU pragma: export */
|
||||
|
||||
#include "dlfcn-util.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
* - bpf_map_create, bpf_object__next_map: available since libbpf 0.7
|
||||
* - bpf_token_create: available since libbpf 1.5
|
||||
* - bpf_create_map: removed in libbpf 1.0
|
||||
* - btf__load_vmlinux_btf: available since libbpf 0.5
|
||||
*/
|
||||
DISABLE_WARNING_REDUNDANT_DECLS;
|
||||
/* NOLINTBEGIN(readability-redundant-declaration) */
|
||||
@@ -30,6 +32,7 @@ extern int bpf_create_map(enum bpf_map_type, int key_size, int value_size, int m
|
||||
extern int bpf_map_create(enum bpf_map_type, const char *, __u32, __u32, __u32, const struct bpf_map_create_opts *);
|
||||
extern struct bpf_map* bpf_object__next_map(const struct bpf_object *obj, const struct bpf_map *map);
|
||||
extern int bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *opts);
|
||||
extern struct btf* btf__load_vmlinux_btf(void);
|
||||
/* NOLINTEND(readability-redundant-declaration) */
|
||||
REENABLE_WARNING;
|
||||
|
||||
@@ -65,6 +68,9 @@ extern DLSYM_PROTOTYPE(bpf_program__attach_lsm);
|
||||
extern DLSYM_PROTOTYPE(bpf_program__name);
|
||||
extern DLSYM_PROTOTYPE(bpf_program__set_autoload);
|
||||
extern DLSYM_PROTOTYPE(bpf_token_create);
|
||||
extern DLSYM_PROTOTYPE(btf__find_by_name_kind);
|
||||
extern DLSYM_PROTOTYPE(btf__free);
|
||||
extern DLSYM_PROTOTYPE(btf__load_vmlinux_btf);
|
||||
extern DLSYM_PROTOTYPE(libbpf_set_print);
|
||||
extern DLSYM_PROTOTYPE(ring_buffer__epoll_fd);
|
||||
extern DLSYM_PROTOTYPE(ring_buffer__free);
|
||||
@@ -92,6 +98,12 @@ static inline int compat_bpf_map_create(
|
||||
* this helper instead of libbpf_get_error() to ensure some of the known ones are translated into errnos
|
||||
* we understand. */
|
||||
int bpf_get_error_translated(const void *ptr);
|
||||
|
||||
/* Probe the kernel's vmlinux BTF for a function of the given name, to check whether a BPF kfunc is
|
||||
* available. This probes vmlinux BTF only, not module BTFs; and BTF advertising the function only proves
|
||||
* it exists, not that it is registered as a kfunc for a given program type — callers must still handle
|
||||
* program load failure. */
|
||||
bool bpf_kernel_has_kfunc(const char *name);
|
||||
#endif
|
||||
|
||||
int dlopen_bpf(int log_level) _dlopen_loader_;
|
||||
|
||||
Reference in New Issue
Block a user