]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
selftests/bpf: Add bpf_testmod kernel module for testing
authorAndrii Nakryiko <andrii@kernel.org>
Thu, 3 Dec 2020 20:46:26 +0000 (12:46 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 4 Dec 2020 01:38:20 +0000 (17:38 -0800)
Add bpf_testmod module, which is conceptually out-of-tree module and provides
ways for selftests/bpf to test various kernel module-related functionality:
raw tracepoint, fentry/fexit/fmod_ret, etc. This module will be auto-loaded by
test_progs test runner and expected by some of selftests to be present and
loaded.

Pahole currently isn't able to generate BTF for static functions in kernel
modules, so make sure traced function is global.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20201203204634.1325171-7-andrii@kernel.org
tools/testing/selftests/bpf/.gitignore
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_testmod/.gitignore [new file with mode: 0644]
tools/testing/selftests/bpf/bpf_testmod/Makefile [new file with mode: 0644]
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod-events.h [new file with mode: 0644]
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c [new file with mode: 0644]
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h [new file with mode: 0644]
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_progs.h

index 395ae040ce1f0b85868c0f73b7f171e784cac9bf..752d8edddc667b6737b39f978242a1db4c2f44f1 100644 (file)
@@ -35,3 +35,4 @@ test_cpp
 /tools
 /runqslower
 /bench
+*.ko
index 371b022d932c0b7346334cbcd1ec581879974f69..ac25ba5d0d6cbf7ffb390fb6547dee179d5c3384 100644 (file)
@@ -80,7 +80,7 @@ TEST_PROGS_EXTENDED := with_addr.sh \
 # Compile but not part of 'make run_tests'
 TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \
        flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \
-       test_lirc_mode2_user xdping test_cpp runqslower bench
+       test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko
 
 TEST_CUSTOM_PROGS = urandom_read
 
@@ -104,6 +104,7 @@ OVERRIDE_TARGETS := 1
 override define CLEAN
        $(call msg,CLEAN)
        $(Q)$(RM) -r $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(EXTRA_CLEAN)
+       $(Q)$(MAKE) -C bpf_testmod clean
 endef
 
 include ../lib.mk
@@ -136,6 +137,11 @@ $(OUTPUT)/urandom_read: urandom_read.c
        $(call msg,BINARY,,$@)
        $(Q)$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) -Wl,--build-id=sha1
 
+$(OUTPUT)/bpf_testmod.ko: $(VMLINUX_BTF) $(wildcard bpf_testmod/Makefile bpf_testmod/*.[ch])
+       $(call msg,MOD,,$@)
+       $(Q)$(MAKE) $(submake_extras) -C bpf_testmod
+       $(Q)cp bpf_testmod/bpf_testmod.ko $@
+
 $(OUTPUT)/test_stub.o: test_stub.c $(BPFOBJ)
        $(call msg,CC,,$@)
        $(Q)$(CC) -c $(CFLAGS) -o $@ $<
@@ -388,7 +394,7 @@ TRUNNER_BPF_PROGS_DIR := progs
 TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \
                         network_helpers.c testing_helpers.c            \
                         btf_helpers.c  flow_dissector_load.h
-TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read                          \
+TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko \
                       ima_setup.sh                                     \
                       $(wildcard progs/btf_dump_test_case_*.c)
 TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE
@@ -460,4 +466,4 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o $(OUTPUT)/testing_helpers.o \
 EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR)                     \
        prog_tests/tests.h map_tests/tests.h verifier/tests.h           \
        feature                                                         \
-       $(addprefix $(OUTPUT)/,*.o *.skel.h no_alu32 bpf_gcc)
+       $(addprefix $(OUTPUT)/,*.o *.skel.h no_alu32 bpf_gcc bpf_testmod.ko)
diff --git a/tools/testing/selftests/bpf/bpf_testmod/.gitignore b/tools/testing/selftests/bpf/bpf_testmod/.gitignore
new file mode 100644 (file)
index 0000000..ded5137
--- /dev/null
@@ -0,0 +1,6 @@
+*.mod
+*.mod.c
+*.o
+.ko
+/Module.symvers
+/modules.order
diff --git a/tools/testing/selftests/bpf/bpf_testmod/Makefile b/tools/testing/selftests/bpf/bpf_testmod/Makefile
new file mode 100644 (file)
index 0000000..15cb36c
--- /dev/null
@@ -0,0 +1,20 @@
+BPF_TESTMOD_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
+KDIR ?= $(abspath $(BPF_TESTMOD_DIR)/../../../../..)
+
+ifeq ($(V),1)
+Q =
+else
+Q = @
+endif
+
+MODULES = bpf_testmod.ko
+
+obj-m += bpf_testmod.o
+CFLAGS_bpf_testmod.o = -I$(src)
+
+all:
+       +$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) modules
+
+clean:
+       +$(Q)make -C $(KDIR) M=$(BPF_TESTMOD_DIR) clean
+
diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod-events.h b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod-events.h
new file mode 100644 (file)
index 0000000..b83ea44
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2020 Facebook */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM bpf_testmod
+
+#if !defined(_BPF_TESTMOD_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _BPF_TESTMOD_EVENTS_H
+
+#include <linux/tracepoint.h>
+#include "bpf_testmod.h"
+
+TRACE_EVENT(bpf_testmod_test_read,
+       TP_PROTO(struct task_struct *task, struct bpf_testmod_test_read_ctx *ctx),
+       TP_ARGS(task, ctx),
+       TP_STRUCT__entry(
+               __field(pid_t, pid)
+               __array(char, comm, TASK_COMM_LEN)
+               __field(loff_t, off)
+               __field(size_t, len)
+       ),
+       TP_fast_assign(
+               __entry->pid = task->pid;
+               memcpy(__entry->comm, task->comm, TASK_COMM_LEN);
+               __entry->off = ctx->off;
+               __entry->len = ctx->len;
+       ),
+       TP_printk("pid=%d comm=%s off=%llu len=%zu",
+                 __entry->pid, __entry->comm, __entry->off, __entry->len)
+);
+
+#endif /* _BPF_TESTMOD_EVENTS_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE bpf_testmod-events
+#include <trace/define_trace.h>
diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
new file mode 100644 (file)
index 0000000..2df19d7
--- /dev/null
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2020 Facebook */
+#include <linux/error-injection.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/tracepoint.h>
+#include "bpf_testmod.h"
+
+#define CREATE_TRACE_POINTS
+#include "bpf_testmod-events.h"
+
+noinline ssize_t
+bpf_testmod_test_read(struct file *file, struct kobject *kobj,
+                     struct bin_attribute *bin_attr,
+                     char *buf, loff_t off, size_t len)
+{
+       struct bpf_testmod_test_read_ctx ctx = {
+               .buf = buf,
+               .off = off,
+               .len = len,
+       };
+
+       trace_bpf_testmod_test_read(current, &ctx);
+
+       return -EIO; /* always fail */
+}
+EXPORT_SYMBOL(bpf_testmod_test_read);
+ALLOW_ERROR_INJECTION(bpf_testmod_test_read, ERRNO);
+
+static struct bin_attribute bin_attr_bpf_testmod_file __ro_after_init = {
+       .attr = { .name = "bpf_testmod", .mode = 0444, },
+       .read = bpf_testmod_test_read,
+};
+
+static int bpf_testmod_init(void)
+{
+       return sysfs_create_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);
+}
+
+static void bpf_testmod_exit(void)
+{
+       return sysfs_remove_bin_file(kernel_kobj, &bin_attr_bpf_testmod_file);
+}
+
+module_init(bpf_testmod_init);
+module_exit(bpf_testmod_exit);
+
+MODULE_AUTHOR("Andrii Nakryiko");
+MODULE_DESCRIPTION("BPF selftests module");
+MODULE_LICENSE("Dual BSD/GPL");
+
diff --git a/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.h
new file mode 100644 (file)
index 0000000..b81adfe
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2020 Facebook */
+#ifndef _BPF_TESTMOD_H
+#define _BPF_TESTMOD_H
+
+#include <linux/types.h>
+
+struct bpf_testmod_test_read_ctx {
+       char *buf;
+       loff_t off;
+       size_t len;
+};
+
+#endif /* _BPF_TESTMOD_H */
index 22943b58d752ae12baf9b4db75ceff36ee4b5205..17587754b7a7fd3c90fa9599d1ae4b45416c2402 100644 (file)
@@ -360,6 +360,58 @@ err:
        return -1;
 }
 
+static int finit_module(int fd, const char *param_values, int flags)
+{
+       return syscall(__NR_finit_module, fd, param_values, flags);
+}
+
+static int delete_module(const char *name, int flags)
+{
+       return syscall(__NR_delete_module, name, flags);
+}
+
+static void unload_bpf_testmod(void)
+{
+       if (delete_module("bpf_testmod", 0)) {
+               if (errno == ENOENT) {
+                       if (env.verbosity > VERBOSE_NONE)
+                               fprintf(stdout, "bpf_testmod.ko is already unloaded.\n");
+                       return;
+               }
+               fprintf(env.stderr, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno);
+               exit(1);
+       }
+       if (env.verbosity > VERBOSE_NONE)
+               fprintf(stdout, "Successfully unloaded bpf_testmod.ko.\n");
+}
+
+static int load_bpf_testmod(void)
+{
+       int fd;
+
+       /* ensure previous instance of the module is unloaded */
+       unload_bpf_testmod();
+
+       if (env.verbosity > VERBOSE_NONE)
+               fprintf(stdout, "Loading bpf_testmod.ko...\n");
+
+       fd = open("bpf_testmod.ko", O_RDONLY);
+       if (fd < 0) {
+               fprintf(env.stderr, "Can't find bpf_testmod.ko kernel module: %d\n", -errno);
+               return -ENOENT;
+       }
+       if (finit_module(fd, "", 0)) {
+               fprintf(env.stderr, "Failed to load bpf_testmod.ko into the kernel: %d\n", -errno);
+               close(fd);
+               return -EINVAL;
+       }
+       close(fd);
+
+       if (env.verbosity > VERBOSE_NONE)
+               fprintf(stdout, "Successfully loaded bpf_testmod.ko.\n");
+       return 0;
+}
+
 /* extern declarations for test funcs */
 #define DEFINE_TEST(name) extern void test_##name(void);
 #include <prog_tests/tests.h>
@@ -678,6 +730,11 @@ int main(int argc, char **argv)
 
        save_netns();
        stdio_hijack();
+       env.has_testmod = true;
+       if (load_bpf_testmod()) {
+               fprintf(env.stderr, "WARNING! Selftests relying on bpf_testmod.ko will be skipped.\n");
+               env.has_testmod = false;
+       }
        for (i = 0; i < prog_test_cnt; i++) {
                struct prog_test_def *test = &prog_test_defs[i];
 
@@ -722,6 +779,8 @@ int main(int argc, char **argv)
                if (test->need_cgroup_cleanup)
                        cleanup_cgroup_environment();
        }
+       if (env.has_testmod)
+               unload_bpf_testmod();
        stdio_restore();
 
        if (env.get_test_cnt) {
index d6b14853f3bc1d172be2cf95861ff9c3585961f0..115953243f62391ad679f1cf0737b5e3c7cda1b6 100644 (file)
@@ -66,6 +66,7 @@ struct test_env {
        enum verbosity verbosity;
 
        bool jit_enabled;
+       bool has_testmod;
        bool get_test_cnt;
        bool list_test_names;