The BPF implementation of RV monitors cannot use standard reactors in
the kernel and currently reactions are skipped.

Add rv_react() to build the printk format using BPF macros and pass that
to a BPF function with a single message parameter, since BPF doesn't
support variable arguments.
This function is defined as weak, so the rv tool can link a different
implementation on top at load time.

Implement a printk reactor that is simply reimplementing this function
passing the message to bpf_printk().

Signed-off-by: Gabriele Monaco <[email protected]>
---
 include/rv/da_monitor.h                       |   4 +-
 tools/verification/rv/Makefile                |  12 ++-
 tools/verification/rv/Makefile.rv             |   5 +
 .../rv/bpf_monitors/da_monitor_bpf.h          |  12 ++-
 tools/verification/rv/bpf_reactors/.gitignore |   2 +
 tools/verification/rv/bpf_reactors/panic.c    |  15 +++
 tools/verification/rv/bpf_reactors/printk.c   |  13 +++
 tools/verification/rv/src/bpf_monitor.c       | 102 +++++++++++++++++-
 8 files changed, 157 insertions(+), 8 deletions(-)
 create mode 100644 tools/verification/rv/bpf_reactors/.gitignore
 create mode 100644 tools/verification/rv/bpf_reactors/panic.c
 create mode 100644 tools/verification/rv/bpf_reactors/printk.c

diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h
index ee1cec73ec85..28cff26aecfe 100644
--- a/include/rv/da_monitor.h
+++ b/include/rv/da_monitor.h
@@ -84,7 +84,8 @@ static struct rv_monitor rv_this;
 
 #ifdef __BPF__
 #include "da_monitor_bpf.h"
-#else
+#endif
+
 static void react(enum states curr_state, enum events event)
 {
        rv_react(&rv_this,
@@ -93,7 +94,6 @@ static void react(enum states curr_state, enum events event)
                 model_get_event_name(event),
                 model_get_state_name(curr_state));
 }
-#endif
 
 /*
  * da_monitor_reset_state - reset a monitor and setting it to init state
diff --git a/tools/verification/rv/Makefile b/tools/verification/rv/Makefile
index bb81593acd71..44b0e3af1488 100644
--- a/tools/verification/rv/Makefile
+++ b/tools/verification/rv/Makefile
@@ -73,8 +73,11 @@ static: $(RV_IN)
        $(QUIET_LINK)$(CC) $(LDFLAGS) -o $(RV)-static $(RV_IN) $(EXTLIBS)
 
 BPF_DIR                := bpf_monitors
+BPF_REACT_DIR   := bpf_reactors
 BPF_SOURCES    := $(wildcard $(BPF_DIR)/*.c)
 BPF_OBJECTS    := $(BPF_SOURCES:.c=.o)
+BPF_REACT_SOURCES := $(wildcard $(BPF_REACT_DIR)/*.c)
+BPF_REACT_OBJECTS := $(BPF_REACT_SOURCES:.c=.o)
 VMLINUX_H      := $(BPF_DIR)/vmlinux.h
 
 BPF_INCLUDES   := -I$(srctree)/include/uapi
@@ -95,9 +98,14 @@ $(BPF_DIR)/%.o: $(BPF_DIR)/%.c $(VMLINUX_H)
        $(Q)$(LLVM_STRIP) -g $@
        $(Q)$(LLVM_OBJCOPY) --remove-section=.rel.rodata $@
 
--include $(BPF_SOURCES:.c=.d)
+$(BPF_REACT_DIR)/%.o: $(BPF_REACT_DIR)/%.c $(VMLINUX_H)
+       $(QUIET_CLANG)$(CLANG) $(BPF_CFLAGS) -c $< -o $@
+       $(Q)$(LLVM_STRIP) -g $@
+       $(Q)$(LLVM_OBJCOPY) --remove-section=.rel.rodata $@
+
+-include $(BPF_SOURCES:.c=.d) $(BPF_REACT_SOURCES:.c=.d)
 
-bpf: $(BPF_OBJECTS)
+bpf: $(BPF_OBJECTS) $(BPF_REACT_OBJECTS)
 
 rv.%: fixdep FORCE
        make -f $(srctree)/tools/build/Makefile.build dir=. $@
diff --git a/tools/verification/rv/Makefile.rv 
b/tools/verification/rv/Makefile.rv
index 418748bc3aa1..3ac329851914 100644
--- a/tools/verification/rv/Makefile.rv
+++ b/tools/verification/rv/Makefile.rv
@@ -34,6 +34,7 @@ MKDIR         := mkdir
 STRIP          := strip
 BINDIR         := /usr/bin
 BPF_MON_DIR    := /usr/share/rv/bpf_monitors
+BPF_REACT_DIR  := /usr/share/rv/bpf_reactors
 
 .PHONY: install
 install: doc_install
@@ -44,6 +45,10 @@ ifneq ($(wildcard bpf_monitors/*.o),)
        $(Q)$(MKDIR) -p $(DESTDIR)$(BPF_MON_DIR)
        $(call QUIET_INSTALL,bpf_monitors)$(INSTALL) bpf_monitors/*.o -m 644 
$(DESTDIR)$(BPF_MON_DIR)
 endif
+ifneq ($(wildcard bpf_reactors/*.o),)
+       $(Q)$(MKDIR) -p $(DESTDIR)$(BPF_REACT_DIR)
+       $(call QUIET_INSTALL,bpf_reactors)$(INSTALL) bpf_reactors/*.o -m 644 
$(DESTDIR)$(BPF_REACT_DIR)
+endif
 
 .PHONY: doc doc_clean doc_install
 doc:
diff --git a/tools/verification/rv/bpf_monitors/da_monitor_bpf.h 
b/tools/verification/rv/bpf_monitors/da_monitor_bpf.h
index 12f0f78b9c9d..a37d11fef6f1 100644
--- a/tools/verification/rv/bpf_monitors/da_monitor_bpf.h
+++ b/tools/verification/rv/bpf_monitors/da_monitor_bpf.h
@@ -17,7 +17,6 @@
 #include "bpf_atomic.h"
 
 /* BPF monitors don't support these */
-#define rv_react(...) do {} while (0)
 #define trace_rv_retries_error(...) do {} while (0)
 #define pr_warn(fmt, ...) bpf_printk(fmt, ##__VA_ARGS__)
 #define rv_monitoring_on() 1
@@ -75,8 +74,15 @@ static const struct rv_trace_entry __used *_btf_trace;
 
 static inline void da_monitor_reset(struct da_monitor *da_mon);
 
-/* BPF monitors do not use reactors */
-static inline void react(enum states curr_state, enum events event) { }
+__noinline __weak void bpf_rv_react(char *msg) { }
+
+#define rv_react(mon, fmt, ...)                                                
          \
+       ({                                                                      
  \
+               char ___msg[256];                                               
  \
+                                                                               
  \
+               if (BPF_SNPRINTF(___msg, sizeof(___msg), fmt, ##__VA_ARGS__) > 
0) \
+                       bpf_rv_react(___msg);                                   
  \
+       })
 
 /*
  * BPF monitor implementations
diff --git a/tools/verification/rv/bpf_reactors/.gitignore 
b/tools/verification/rv/bpf_reactors/.gitignore
new file mode 100644
index 000000000000..e5add9141ef5
--- /dev/null
+++ b/tools/verification/rv/bpf_reactors/.gitignore
@@ -0,0 +1,2 @@
+*.d
+vmlinux.h
diff --git a/tools/verification/rv/bpf_reactors/panic.c 
b/tools/verification/rv/bpf_reactors/panic.c
new file mode 100644
index 000000000000..775f91a300f0
--- /dev/null
+++ b/tools/verification/rv/bpf_reactors/panic.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+
+void bpf_rv_react(char *msg)
+{
+       struct pt_regs regs = { 0 };
+
+       crash_kexec(&regs);
+}
+
+char LICENSE[] SEC("license") = "GPL";
+static char DESCRIPTION[] SEC(".rodata.description") =
+       "panic the system if an exception is found.";
diff --git a/tools/verification/rv/bpf_reactors/printk.c 
b/tools/verification/rv/bpf_reactors/printk.c
new file mode 100644
index 000000000000..580410d2a1fc
--- /dev/null
+++ b/tools/verification/rv/bpf_reactors/printk.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+
+void bpf_rv_react(char *msg)
+{
+       bpf_printk("%s", msg);
+}
+
+char LICENSE[] SEC("license") = "GPL";
+static char DESCRIPTION[] SEC(".rodata.description") =
+       "prints the exception msg to the trace buffer.";
diff --git a/tools/verification/rv/src/bpf_monitor.c 
b/tools/verification/rv/src/bpf_monitor.c
index d43ee75d3904..35bd1f40a109 100644
--- a/tools/verification/rv/src/bpf_monitor.c
+++ b/tools/verification/rv/src/bpf_monitor.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2026 Red Hat Inc, Gabriele Monaco <[email protected]>
  */
 
+#define _GNU_SOURCE
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -14,6 +15,7 @@
 #include <libgen.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <sys/mman.h>
 #include <bpf/libbpf.h>
 #include <bpf/bpf.h>
 #include <bpf/btf.h>
@@ -36,6 +38,7 @@ static char bpf_base_paths[][MAX_PATH] = {
 #define MAX_ENUMS 64
 #define MAX_LINKS 16
 #define PROG_ENABLE_MON "enable_monitor"
+#define BPF_REACTOR "bpf_rv_react"
 #define RV_TRACE_STRUCT "rv_trace_entry"
 #define RV_TRACE_TYPE_ENUM "rv_trace_type"
 
@@ -676,12 +679,31 @@ static struct ring_buffer *bpf_setup_ring_buffer(struct 
bpf_object *obj,
        return rb;
 }
 
+static void list_reactor_action(const char *name, struct bpf_object *obj)
+{
+       const struct btf *btf = bpf_object__btf(obj);
+
+       if (btf__find_by_name_kind(btf, BPF_REACTOR, BTF_KIND_FUNC) >= 0)
+               fprintf(stderr, "%s ", name);
+}
+
+/*
+ * list_reactors_from_path - list reactors from a specific base path
+ */
+static void list_reactors_from_path(const char *base_path)
+{
+       bpf_object_iterate_path(base_path, "bpf_reactors", list_reactor_action);
+}
+
 /*
  * bpf_usage_print_reactors - print available BPF reactors
  */
 void bpf_usage_print_reactors(void)
 {
-       fprintf(stderr, "  available BPF reactors: nop\n");
+       fprintf(stderr, "  available BPF reactors: nop ");
+       for (int i = 0; bpf_base_paths[i][0]; i++)
+               list_reactors_from_path(bpf_base_paths[i]);
+       fprintf(stderr, "\n");
 }
 
 /*
@@ -720,6 +742,7 @@ static struct bpf_object *open_bpf_monitor(const char 
*path, struct bpf_monitor_
        int res;
 
        LIBBPF_OPTS(bpf_object_open_opts, opts,
+               .object_name = ctx->monitor_name,
                /* Define statically as arch is known, Kconfig may not be 
available */
 #ifdef __x86_64__
                .kconfig = "CONFIG_X86_64=y\n",
@@ -795,6 +818,65 @@ static int attach_bpf_handlers(const char *monitor_name, 
struct bpf_object *obj,
        return enable_mon_fd;
 }
 
+static int find_bpf_reactor(const char *reactor_name, char *path_out, size_t 
path_len)
+{
+       return find_bpf_file("bpf_reactors", reactor_name, path_out, path_len);
+}
+
+/*
+ * link_bpf_reactor - link the reactor function to the monitor
+ *
+ * Reactors are objects defining the BPF_REACTOR function, link that over the
+ * weak definition present in the monitor and return a file descriptor to the
+ * final linked object in memory.
+ *
+ * Returns memfd of final object on success, -1 on error.
+ */
+static int link_bpf_reactor(const char *monitor_path, const char *reactor_path)
+{
+       struct bpf_linker *linker = NULL;
+       int memfd = -1;
+       int err = 0;
+
+       memfd = memfd_create("linked_bpf", 0);
+       if (memfd < 0) {
+               err_msg("bpf: failed to create memfd: %s\n", strerror(errno));
+               return -1;
+       }
+
+       linker = bpf_linker__new_fd(memfd, NULL);
+       if (!linker) {
+               err_msg("bpf: failed to create BPF linker\n");
+               goto out;
+       }
+
+       err = bpf_linker__add_file(linker, monitor_path, NULL);
+       if (err) {
+               err_msg("bpf: failed to add monitor file to linker: %s\n", 
strerror(-err));
+               goto out;
+       }
+
+       err = bpf_linker__add_file(linker, reactor_path, NULL);
+       if (err) {
+               err_msg("bpf: failed to add reactor file to linker: %s\n", 
strerror(-err));
+               goto out;
+       }
+
+       err = bpf_linker__finalize(linker);
+       if (err) {
+               err_msg("bpf: failed to finalize BPF linker: %s\n", 
strerror(-err));
+               goto out;
+       }
+
+       bpf_linker__free(linker);
+       return memfd;
+
+out:
+       bpf_linker__free(linker);
+       close(memfd);
+       return -1;
+}
+
 /*
  * bpf_run_monitor - load and run a BPF monitor
  *
@@ -808,6 +890,7 @@ int bpf_run_monitor(char *monitor_name, int argc, char 
**argv)
        struct bpf_object *obj = NULL;
        int res, link_count = 0, enable_mon_fd, retval = -1;
        char monitor_path[MAX_PATH];
+       int memfd = -1;
 
        libbpf_set_print(libbpf_print_fn);
        bpf_fill_base_paths();
@@ -830,8 +913,25 @@ int bpf_run_monitor(char *monitor_name, int argc, char 
**argv)
 
        strncpy(ctx.monitor_name, monitor_name, sizeof(ctx.monitor_name) - 1);
 
+       if (config.reactor && strcmp(config.reactor, "nop")) {
+               char reactor_path[MAX_PATH];
+
+               if (!find_bpf_reactor(config.reactor, reactor_path, 
sizeof(reactor_path))) {
+                       mon_usage(1, monitor_name,
+                                 "bpf: failed to set %s reactor, is it 
available?",
+                                 config.reactor);
+                       goto cleanup;
+               }
+
+               memfd = link_bpf_reactor(monitor_path, reactor_path);
+               if (memfd < 0)
+                       goto cleanup;
+
+               snprintf(monitor_path, sizeof(monitor_path), 
"/proc/self/fd/%d", memfd);
+       }
 
        obj = open_bpf_monitor(monitor_path, &ctx);
+       close(memfd);
        if (!obj)
                goto cleanup;
 
-- 
2.55.0


Reply via email to