Implement the BPF monitor listing functionality that scans for compiled BPF monitor object files and displays them in the monitor list.
If enabled, running rv list shows in-kernel and BPF monitors found in /usr/share/rv/bpf_monitors/ or in ./bpf_monitors/ relative to the RV binary's location (used during development). Signed-off-by: Gabriele Monaco <[email protected]> --- tools/verification/rv/Makefile | 4 +- tools/verification/rv/Makefile.config | 23 ++ tools/verification/rv/include/bpf_monitor.h | 14 ++ tools/verification/rv/src/Build | 5 + tools/verification/rv/src/bpf_monitor.c | 223 ++++++++++++++++++++ tools/verification/rv/src/rv.c | 2 + 6 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 tools/verification/rv/include/bpf_monitor.h create mode 100644 tools/verification/rv/src/bpf_monitor.c diff --git a/tools/verification/rv/Makefile b/tools/verification/rv/Makefile index 8ae5fc0d1d17..0a8c7a656f7f 100644 --- a/tools/verification/rv/Makefile +++ b/tools/verification/rv/Makefile @@ -32,8 +32,10 @@ DOCSRC := ../../../Documentation/tools/rv/ FEATURE_TESTS := libtraceevent FEATURE_TESTS += libtracefs +FEATURE_TESTS += libbpf FEATURE_DISPLAY := libtraceevent FEATURE_DISPLAY += libtracefs +FEATURE_DISPLAY += libbpf all: $(RV) @@ -57,7 +59,7 @@ endif CFLAGS += $(INCLUDES) $(LIB_INCLUDES) -export CFLAGS OUTPUT srctree +export CFLAGS OUTPUT srctree BUILD_BPF $(RV): $(RV_IN) $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RV) $(RV_IN) $(EXTLIBS) diff --git a/tools/verification/rv/Makefile.config b/tools/verification/rv/Makefile.config index 066302230eb2..0600170ac217 100644 --- a/tools/verification/rv/Makefile.config +++ b/tools/verification/rv/Makefile.config @@ -43,6 +43,29 @@ else $(info libtracefs is missing. Please install libtracefs-dev/libtracefs-devel) endif +ifndef BUILD_BPF + # BPF monitors are optional but enabled by default + BUILD_BPF := 1 +endif + +ifeq ($(BUILD_BPF),0) + $(info BPF monitor support disabled, building without BPF monitor support.) +endif + +$(call feature_check,libbpf) +ifeq ($(feature-libbpf), 1) + $(call detected,CONFIG_LIBBPF) +else + $(info libbpf is missing, building without BPF monitor support.) + $(info Please install libbpf-dev/libbpf-devel) + BUILD_BPF := 0 +endif + +ifeq ($(BUILD_BPF),1) + CFLAGS += -DHAVE_LIBBPF + $(call lib_setup,bpf) +endif + ifeq ($(STOP_ERROR),1) $(error Please, check the errors above.) endif diff --git a/tools/verification/rv/include/bpf_monitor.h b/tools/verification/rv/include/bpf_monitor.h new file mode 100644 index 000000000000..e58bc45c5f75 --- /dev/null +++ b/tools/verification/rv/include/bpf_monitor.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _BPF_MONITOR_H +#define _BPF_MONITOR_H + +#ifdef HAVE_LIBBPF +int bpf_list_monitors(char *container); +#else +static inline int bpf_list_monitors(char *container) +{ + return 0; +} +#endif /* HAVE_LIBBPF */ + +#endif diff --git a/tools/verification/rv/src/Build b/tools/verification/rv/src/Build index d781983c1a79..326503c22915 100644 --- a/tools/verification/rv/src/Build +++ b/tools/verification/rv/src/Build @@ -1,4 +1,9 @@ rv-y += trace.o rv-y += utils.o rv-y += in_kernel.o + +ifeq ($(BUILD_BPF),1) + rv-y += bpf_monitor.o +endif + rv-y += rv.o diff --git a/tools/verification/rv/src/bpf_monitor.c b/tools/verification/rv/src/bpf_monitor.c new file mode 100644 index 000000000000..a1de0c157712 --- /dev/null +++ b/tools/verification/rv/src/bpf_monitor.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BPF monitor support: allows rv to control BPF monitors. + * + * Copyright (C) 2026 Red Hat Inc, Gabriele Monaco <[email protected]> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <libgen.h> +#include <errno.h> +#include <bpf/libbpf.h> +#include <bpf/bpf.h> +#include <bpf/btf.h> + +#include <bpf_monitor.h> +#include <utils.h> +#include <rv.h> + +static char bpf_base_paths[][MAX_PATH] = { + ".", + "/etc/rv", + "/usr/local/share/rv", + "/usr/share/rv", + "", /* Marker */ +}; + +/* Path used for development, searched first */ +#define DEVEL_PATH 0 + + +/* + * bpf_read_enable - reads monitor's enable status + * + * Iterate through all BPF maps in the system, if the rv_mon_NAME map is + * loaded, the monitor is enabled. + * Since map names have limited size and may get truncated, check that also the + * corresponding BTF matches. + */ +static int bpf_read_enable(const char *monitor_name) +{ + char ringbuf_name[2 * MAX_DA_NAME_LEN]; + uint32_t id = 0; + + snprintf(ringbuf_name, sizeof(ringbuf_name), + "rv_mon_%s", monitor_name); + + while (bpf_map_get_next_id(id, &id) == 0) { + struct bpf_map_info info = { 0 }; + uint32_t info_len = sizeof(info); + struct btf *btf; + int type_id; + int fd = bpf_map_get_fd_by_id(id); + + if (fd < 0) + continue; + + if (bpf_map_get_info_by_fd(fd, &info, &info_len) != 0) { + close(fd); + continue; + } + close(fd); + + if (strncmp(info.name, ringbuf_name, BPF_OBJ_NAME_LEN - 1) != 0) + continue; + + if (!info.btf_id) + continue; + btf = btf__load_from_kernel_by_id(info.btf_id); + if (!btf) + continue; + + type_id = btf__find_by_name_kind(btf, ringbuf_name, BTF_KIND_VAR); + btf__free(btf); + if (type_id > 0) + return 1; + } + + return 0; +} + +/* + * bpf_read_desc - read monitors' description + * + * Return the provided string containing the monitor's description, NULL + * otherwise. + */ +static char *bpf_read_desc(char *desc, struct bpf_object *obj, const char *monitor_name) +{ + struct bpf_map *map = bpf_object__find_map_by_name(obj, ".rodata.description"); + const char *desc_data; + size_t desc_size; + + if (!map) { + debug_msg("bpf: cannot find description for %s\n", + monitor_name); + return NULL; + } + desc_data = bpf_map__initial_value(map, &desc_size); + if (!desc_data || desc_size == 0) { + debug_msg("bpf: empty description for %s\n", monitor_name); + *desc = 0; + return desc; + } + + if (desc_size >= MAX_DESCRIPTION) + desc_size = MAX_DESCRIPTION - 1; + strncpy(desc, desc_data, desc_size); + desc[desc_size] = '\0'; + + return desc; +} + +/* + * bpf_fill_base_paths - fill the path for development builds + * + * RV searches for BPF monitors on absolute paths on the system as well + * as in the same directory of the rv binary. This is useful when running + * rv from the kernel tree. This function resolves the right location. + */ +static void bpf_fill_base_paths(void) +{ + char tmp_path[MAX_PATH], *dir; + ssize_t len; + + len = readlink("/proc/self/exe", tmp_path, MAX_PATH); + if (len > 0 && len != MAX_PATH) { + tmp_path[len] = '\0'; + dir = dirname(tmp_path); + snprintf(bpf_base_paths[DEVEL_PATH], MAX_PATH, "%s", dir); + } +} + +static void bpf_object_iterate_path(const char *base_path, const char *subdir, + void (*action)(const char *name, struct bpf_object *obj)) +{ + char path[MAX_PATH]; + struct dirent *entry; + DIR *dir; + char *ext; + + snprintf(path, sizeof(path), "%s/%s", base_path, subdir); + dir = opendir(path); + if (!dir) { + debug_msg("bpf: error opening directory: %s\n", path); + return; + } + + while ((entry = readdir(dir)) != NULL) { + size_t size; + struct bpf_object *obj; + char name[MAX_DA_NAME_LEN], obj_path[MAX_PATH]; + + if (entry->d_name[0] == '.') + continue; + + ext = strrchr(entry->d_name, '.'); + if (!ext || strcmp(ext, ".o") != 0) + continue; + + size = snprintf(obj_path, sizeof(obj_path), "%s/%s", path, + entry->d_name); + obj = bpf_object__open_file(obj_path, NULL); + if (!obj || size > MAX_PATH) { + err_msg("bpf: error opening object file %s: %s\n", + obj_path, strerror(errno)); + continue; + } + + strncpy(name, entry->d_name, sizeof(name)); + ext = strrchr(name, '.'); + if (ext) + *ext = '\0'; + + action(name, obj); + + bpf_object__close(obj); + } + + closedir(dir); +} + +static void list_monitor_action(const char *name, struct bpf_object *obj) +{ + char desc[MAX_DESCRIPTION]; + + if (!bpf_read_desc(desc, obj, name)) { + err_msg("bpf: monitor %s does not have desc map, bug?\n", name); + return; + } + + printf("%-*s %s %s\n", MAX_DA_NAME_LEN, name, + desc, bpf_read_enable(name) ? "[ON]" : "[OFF]"); +} + +/* + * list_monitors_from_path - list monitors from a specific base path + */ +static void list_monitors_from_path(const char *base_path) +{ + bpf_object_iterate_path(base_path, "bpf_monitors", list_monitor_action); +} + +/* + * bpf_list_monitors - list available BPF monitors from all sources + * + * @container: BPF monitors are not nested, skip listing. + * + * Returns 0 on success + */ +int bpf_list_monitors(char *container) +{ + if (container) + return 0; + bpf_fill_base_paths(); + for (int i = 0; bpf_base_paths[i][0]; i++) + list_monitors_from_path(bpf_base_paths[i]); + + return 0; +} diff --git a/tools/verification/rv/src/rv.c b/tools/verification/rv/src/rv.c index 09e0d8598619..7c4a2e49ff9d 100644 --- a/tools/verification/rv/src/rv.c +++ b/tools/verification/rv/src/rv.c @@ -13,6 +13,7 @@ #include <trace.h> #include <utils.h> #include <in_kernel.h> +#include <bpf_monitor.h> static int stop_session; @@ -76,6 +77,7 @@ static void rv_list(int argc, char **argv) } ikm_list_monitors(container); + bpf_list_monitors(container); exit(EXIT_SUCCESS); } -- 2.55.0
