[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250122-modules-error-injection-v1-2-910590a04fd5@samsung.com>
Date: Wed, 22 Jan 2025 14:11:14 +0100
From: Daniel Gomez <da.gomez@...sung.com>
To: Luis Chamberlain <mcgrof@...nel.org>, Petr Pavlu <petr.pavlu@...e.com>,
Sami Tolvanen <samitolvanen@...gle.com>, Alexei Starovoitov
<ast@...nel.org>, Daniel Borkmann <daniel@...earbox.net>, Andrii Nakryiko
<andrii@...nel.org>, Martin KaFai Lau <martin.lau@...ux.dev>, Eduard
Zingerman <eddyz87@...il.com>, Song Liu <song@...nel.org>, Yonghong Song
<yonghong.song@...ux.dev>, John Fastabend <john.fastabend@...il.com>, "KP
Singh" <kpsingh@...nel.org>, Stanislav Fomichev <sdf@...ichev.me>, Hao Luo
<haoluo@...gle.com>, Jiri Olsa <jolsa@...nel.org>, Nathan Chancellor
<nathan@...nel.org>, Nick Desaulniers <ndesaulniers@...gle.com>, "Bill
Wendling" <morbo@...gle.com>, Justin Stitt <justinstitt@...gle.com>
CC: <linux-modules@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
<bpf@...r.kernel.org>, <llvm@...ts.linux.dev>,
<iovisor-dev@...ts.iovisor.org>, <gost.dev@...sung.com>, Daniel Gomez
<da.gomez@...sung.com>
Subject: [PATCH 2/2] moderr: add module error injection tool
Add support for a module error injection tool. The tool
can inject errors in the annotated module kernel functions
such as complete_formation(), do_init_module() and
module_enable_rodata_after_init(). Module name and module function are
required parameters to have control over the error injection.
Example: Inject error -22 to module_enable_rodata_ro_after_init for
brd module:
sudo moderr --modname=brd --modfunc=module_enable_rodata_ro_after_init \
--error=-22 --trace
Monitoring module error injection... Hit Ctrl-C to end.
MODULE ERROR FUNCTION
brd -22 module_enable_rodata_after_init()
Kernel messages:
[ 89.463690] brd: module loaded
[ 89.463855] brd: module_enable_rodata_ro_after_init() returned -22,
ro_after_init data might still be writable
Signed-off-by: Daniel Gomez <da.gomez@...sung.com>
---
tools/bpf/Makefile | 13 ++-
tools/bpf/moderr/.gitignore | 2 +
tools/bpf/moderr/Makefile | 95 +++++++++++++++++
tools/bpf/moderr/moderr.bpf.c | 127 +++++++++++++++++++++++
tools/bpf/moderr/moderr.c | 236 ++++++++++++++++++++++++++++++++++++++++++
tools/bpf/moderr/moderr.h | 40 +++++++
6 files changed, 510 insertions(+), 3 deletions(-)
diff --git a/tools/bpf/Makefile b/tools/bpf/Makefile
index 243b79f2b451e52ca196f79dc46befd1b3dab458..018cab5102e7e42b8b7b2749f4f463bf55c5119b 100644
--- a/tools/bpf/Makefile
+++ b/tools/bpf/Makefile
@@ -38,7 +38,7 @@ FEATURE_TESTS = libbfd disassembler-four-args disassembler-init-styled
FEATURE_DISPLAY = libbfd
check_feat := 1
-NON_CHECK_FEAT_TARGETS := clean bpftool_clean runqslower_clean resolve_btfids_clean
+NON_CHECK_FEAT_TARGETS := clean bpftool_clean moderr_clean runqslower_clean resolve_btfids_clean
ifdef MAKECMDGOALS
ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
check_feat := 0
@@ -76,7 +76,7 @@ $(OUTPUT)%.lex.o: $(OUTPUT)%.lex.c
PROGS = $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg $(OUTPUT)bpf_asm
-all: $(PROGS) bpftool runqslower
+all: $(PROGS) bpftool moderr runqslower
$(OUTPUT)bpf_jit_disasm: CFLAGS += -DPACKAGE='bpf_jit_disasm'
$(OUTPUT)bpf_jit_disasm: $(OUTPUT)bpf_jit_disasm.o
@@ -92,7 +92,7 @@ $(OUTPUT)bpf_exp.lex.c: $(OUTPUT)bpf_exp.yacc.c
$(OUTPUT)bpf_exp.yacc.o: $(OUTPUT)bpf_exp.yacc.c
$(OUTPUT)bpf_exp.lex.o: $(OUTPUT)bpf_exp.lex.c
-clean: bpftool_clean runqslower_clean resolve_btfids_clean
+clean: bpftool_clean moderr_clean runqslower_clean resolve_btfids_clean
$(call QUIET_CLEAN, bpf-progs)
$(Q)$(RM) -r -- $(OUTPUT)*.o $(OUTPUT)bpf_jit_disasm $(OUTPUT)bpf_dbg \
$(OUTPUT)bpf_asm $(OUTPUT)bpf_exp.yacc.* $(OUTPUT)bpf_exp.lex.*
@@ -118,6 +118,12 @@ bpftool_install:
bpftool_clean:
$(call descend,bpftool,clean)
+moderr:
+ $(call descend,moderr)
+
+moderr_clean:
+ $(call descend,moderr,clean)
+
runqslower:
$(call descend,runqslower)
@@ -131,5 +137,6 @@ resolve_btfids_clean:
$(call descend,resolve_btfids,clean)
.PHONY: all install clean bpftool bpftool_install bpftool_clean \
+ moderr moderr_clean \
runqslower runqslower_clean \
resolve_btfids resolve_btfids_clean
diff --git a/tools/bpf/moderr/.gitignore b/tools/bpf/moderr/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ffdb70230c8bc308efcc8b7d2084856e2225da91
--- /dev/null
+++ b/tools/bpf/moderr/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+/.output
diff --git a/tools/bpf/moderr/Makefile b/tools/bpf/moderr/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..e6331179f7800e6c1d1945ca713e34f74f7d805d
--- /dev/null
+++ b/tools/bpf/moderr/Makefile
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+include ../../scripts/Makefile.include
+include ../../scripts/Makefile.arch
+
+OUTPUT ?= $(abspath .output)/
+
+BPFTOOL_OUTPUT := $(OUTPUT)bpftool/
+DEFAULT_BPFTOOL := $(BPFTOOL_OUTPUT)bootstrap/bpftool
+BPFTOOL ?= $(DEFAULT_BPFTOOL)
+LIBBPF_SRC := $(abspath ../../lib/bpf)
+BPFOBJ_OUTPUT := $(OUTPUT)libbpf/
+BPFOBJ := $(BPFOBJ_OUTPUT)libbpf.a
+BPF_DESTDIR := $(BPFOBJ_OUTPUT)
+BPF_INCLUDE := $(BPF_DESTDIR)include
+INCLUDES := -I$(OUTPUT) -I$(BPF_INCLUDE) -I$(abspath ../../include/uapi)
+CFLAGS := -g -Wall $(CLANG_CROSS_FLAGS)
+CFLAGS += $(EXTRA_CFLAGS)
+LDFLAGS += $(EXTRA_LDFLAGS)
+LDLIBS += -lelf -lz
+
+# Try to detect best kernel BTF source
+KERNEL_REL := $(shell uname -r)
+VMLINUX_BTF_PATHS := $(if $(O),$(O)/vmlinux) \
+ $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux) \
+ ../../../vmlinux /sys/kernel/btf/vmlinux \
+ /boot/vmlinux-$(KERNEL_REL)
+VMLINUX_BTF_PATH := $(or $(VMLINUX_BTF),$(firstword \
+ $(wildcard $(VMLINUX_BTF_PATHS))))
+
+ifeq ($(V),1)
+Q =
+else
+Q = @
+MAKEFLAGS += --no-print-directory
+submake_extras := feature_display=0
+endif
+
+.DELETE_ON_ERROR:
+
+.PHONY: all clean moderr libbpf_hdrs
+all: moderr
+
+moderr: $(OUTPUT)moderr
+
+clean:
+ $(call QUIET_CLEAN, moderr)
+ $(Q)$(RM) -r $(BPFOBJ_OUTPUT) $(BPFTOOL_OUTPUT)
+ $(Q)$(RM) $(OUTPUT)*.o $(OUTPUT)*.d
+ $(Q)$(RM) $(OUTPUT)*.skel.h $(OUTPUT)vmlinux.h
+ $(Q)$(RM) $(OUTPUT)moderr
+ $(Q)$(RM) -r .output
+
+libbpf_hdrs: $(BPFOBJ)
+
+$(OUTPUT)moderr: $(OUTPUT)moderr.o $(BPFOBJ)
+ $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+$(OUTPUT)moderr.o: moderr.h $(OUTPUT)moderr.skel.h \
+ $(OUTPUT)moderr.bpf.o | libbpf_hdrs
+
+$(OUTPUT)moderr.bpf.o: $(OUTPUT)vmlinux.h moderr.h | libbpf_hdrs
+
+$(OUTPUT)%.skel.h: $(OUTPUT)%.bpf.o | $(BPFTOOL)
+ $(QUIET_GEN)$(BPFTOOL) gen skeleton $< > $@
+
+$(OUTPUT)%.bpf.o: %.bpf.c $(BPFOBJ) | $(OUTPUT)
+ $(QUIET_GEN)$(CLANG) -g -O2 --target=bpf $(INCLUDES) \
+ -D__TARGET_ARCH_$(SRCARCH) \
+ -c $(filter %.c,$^) -o $@ && \
+ $(LLVM_STRIP) -g $@
+
+$(OUTPUT)%.o: %.c | $(OUTPUT)
+ $(QUIET_CC)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
+
+$(OUTPUT) $(BPFOBJ_OUTPUT) $(BPFTOOL_OUTPUT):
+ $(QUIET_MKDIR)mkdir -p $@
+
+$(OUTPUT)vmlinux.h: $(VMLINUX_BTF_PATH) | $(OUTPUT) $(BPFTOOL)
+ifeq ($(VMLINUX_H),)
+ $(Q)if [ ! -e "$(VMLINUX_BTF_PATH)" ] ; then \
+ echo "Couldn't find kernel BTF; set VMLINUX_BTF to" \
+ "specify its location." >&2; \
+ exit 1;\
+ fi
+ $(QUIET_GEN)$(BPFTOOL) btf dump file $(VMLINUX_BTF_PATH) format c > $@
+else
+ $(Q)cp "$(VMLINUX_H)" $@
+endif
+
+$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(BPFOBJ_OUTPUT)
+ $(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(BPFOBJ_OUTPUT) \
+ DESTDIR=$(BPFOBJ_OUTPUT) prefix= $(abspath $@) install_headers
+
+$(DEFAULT_BPFTOOL): | $(BPFTOOL_OUTPUT)
+ $(Q)$(MAKE) $(submake_extras) -C ../bpftool OUTPUT=$(BPFTOOL_OUTPUT) bootstrap
diff --git a/tools/bpf/moderr/moderr.bpf.c b/tools/bpf/moderr/moderr.bpf.c
new file mode 100644
index 0000000000000000000000000000000000000000..1c5d03336dd87a2f065ef6b608f077a8b988e5cf
--- /dev/null
+++ b/tools/bpf/moderr/moderr.bpf.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Samsung */
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+#include "moderr.h"
+
+const volatile bool filter_modname = false;
+const volatile char targ_modname[MODULE_NAME_LEN];
+const volatile bool set_errinj = false;
+const volatile int targ_errinj = 0;
+const volatile bool filter_modfunc = false;
+const volatile int targ_modfunc = 0;
+
+char LICENSE[] SEC("license") = "GPL";
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 2097152);
+} rb SEC(".maps");
+
+static __always_inline bool filter_module_name(struct module *mod)
+{
+ char modname[MODULE_NAME_LEN];
+
+ bpf_probe_read_str(modname, sizeof(modname), mod->name);
+
+ if (!filter_modname ||
+ filter_modname && bpf_strncmp(modname, MODULE_NAME_LEN,
+ (const char *)targ_modname) != 0)
+ return false;
+
+ return true;
+}
+
+static __always_inline bool filter_module_func(enum modfunc fc)
+{
+ if (!filter_modfunc || filter_modfunc && targ_modfunc != fc)
+ return false;
+
+ return true;
+}
+
+static __always_inline bool
+generate_errinj_event(struct pt_regs *ctx, struct module *mod, enum modfunc fc)
+{
+ struct event *e;
+
+ e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
+ if (!e)
+ return false;
+
+ e->err = 0;
+ e->func = fc;
+ bpf_probe_read_str(e->modname, sizeof(e->modname), mod->name);
+
+ if (set_errinj) {
+ bpf_override_return(ctx, targ_errinj);
+ e->err = targ_errinj;
+ }
+
+ bpf_ringbuf_submit(e, 0);
+ return true;
+}
+
+static __always_inline bool generate_debug_event(struct pt_regs *ctx,
+ struct module *mod,
+ enum modfunc fc,
+ const char *fmt)
+{
+ struct event *e;
+
+ e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
+ if (!e)
+ return false;
+
+ e->dbg = BPF_SNPRINTF(e->msg, sizeof(e->msg), "[%s:%s]: %s", mod->name,
+ modfunc_to_string(fc), fmt);
+
+ bpf_ringbuf_submit(e, 0);
+ return true;
+}
+
+static __always_inline int
+module_error_injection(struct pt_regs *ctx, struct module *mod, enum modfunc fc)
+{
+ if (!filter_module_name(mod)) {
+ generate_debug_event(ctx, mod, fc,
+ "Target module does not match");
+ return 0;
+ }
+
+ if (!filter_module_func(fc)) {
+ generate_debug_event(ctx, mod, fc,
+ "Target function does not match");
+ return 0;
+ }
+
+ if (!generate_errinj_event(ctx, mod, fc)) {
+ generate_debug_event(
+ ctx, mod, fc,
+ "Error injection event cannot be generated");
+ return 0;
+ }
+
+ return 0;
+}
+
+SEC("kprobe/complete_formation")
+int BPF_KPROBE(complete_formation, struct module *mod, struct load_info *info)
+{
+ return module_error_injection(ctx, mod, COMPLETE_FORMATION);
+}
+
+SEC("kprobe/do_init_module")
+int BPF_KPROBE(do_init_module, struct module *mod, struct load_info *info)
+{
+ return module_error_injection(ctx, mod, DO_INIT_MODULE);
+}
+
+SEC("kprobe/module_enable_rodata_ro_after_init")
+int BPF_KPROBE(module_enable_rodata_ro_after_init, struct module *mod)
+{
+ return module_error_injection(ctx, mod,
+ MODULE_ENABLE_RODATA_AFTER_INIT);
+}
diff --git a/tools/bpf/moderr/moderr.c b/tools/bpf/moderr/moderr.c
new file mode 100644
index 0000000000000000000000000000000000000000..dce18b02b55d1ad1f7e304cb49985d570b115aa4
--- /dev/null
+++ b/tools/bpf/moderr/moderr.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Samsung */
+#include <argp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
+#include <stdlib.h>
+#include <string.h>
+#include "moderr.h"
+#include "moderr.skel.h"
+
+static struct env {
+ bool verbose;
+ char modname[MODULE_NAME_LEN];
+ enum modfunc func;
+ bool trace;
+ int errinj;
+} env;
+
+const char *argp_program_version = "moderr 0.1";
+const char *argp_program_bug_address = "<da.gomez@...sung.com>";
+const char argp_program_doc[] =
+"BPF moderr application.\n"
+"\n"
+"It injects errors in module initialization\n"
+"\nUSAGE: "
+"moderr [-m <module_name>] [-f <function_name>] [-e <errno>]\n";
+
+static volatile bool exiting = false;
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+
+static const struct argp_option opts[] = {
+ { "verbose", 'v', NULL, 0, "Verbose debug output" },
+ { "trace", 't', NULL, 0, "Enable trace output", 0 },
+ { "modname", 'm', "MODNAME", 0, "Trace this module name only", 0 },
+ { "modfunc", 'f', "MODFUNC", 0, "Trace this module function only", 0 },
+ { "list", 'l', NULL, 0, "List available module functions", 0 },
+ { "error", 'e', "ERROR", 0, "Inject this error", 0 },
+ { NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help", 0 },
+ {},
+};
+
+static void help_modfunc(void)
+{
+ printf("\nAvailable modfunc options are:\n"
+ "- complete_formation\n"
+ "- do_init_module\n"
+ "- module_enable_rodata_ro_after_init\n\n");
+}
+
+static enum modfunc string_to_modfunc(char *arg)
+{
+ if (strncmp(arg, "complete_formation", strlen(arg)) == 0)
+ return COMPLETE_FORMATION;
+
+ if (strncmp(arg, "do_init_module", strlen(arg)) == 0)
+ return DO_INIT_MODULE;
+
+ if (strncmp(arg, "module_enable_rodata_ro_after_init", strlen(arg)) ==
+ 0)
+ return MODULE_ENABLE_RODATA_AFTER_INIT;
+
+ return UNKNOWN;
+}
+
+static error_t parse_arg(int key, char *arg, struct argp_state *state)
+{
+ switch (key) {
+ case 'h':
+ argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
+ break;
+ case 'l':
+ help_modfunc();
+ argp_usage(state);
+ break;
+ case 'v':
+ env.verbose = true;
+ break;
+ case 'm':
+ if (strlen(arg) + 1 > MODULE_NAME_LEN) {
+ fprintf(stderr, "module name error\n");
+ argp_usage(state);
+ }
+ strncpy(env.modname, arg, sizeof(env.modname) - 1);
+ break;
+ case 'f':
+ if (strlen(arg) + 1 > MODULE_FUNC_LEN) {
+ fprintf(stderr, "module function too long\n");
+ argp_usage(state);
+ }
+ env.func = string_to_modfunc(arg);
+ if (!env.func) {
+ fprintf(stderr, "invalid module function\n");
+ help_modfunc();
+ argp_usage(state);
+ }
+ break;
+ case 'e':
+ env.errinj = atoi(arg);
+ break;
+ case 't':
+ env.trace = true;
+ break;
+ case ARGP_KEY_ARG:
+ argp_usage(state);
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp argp = {
+ .options = opts,
+ .parser = parse_arg,
+ .doc = argp_program_doc,
+};
+
+static int libbpf_print_fn(enum libbpf_print_level level, const char *format,
+ va_list args)
+{
+ if (level == LIBBPF_DEBUG && !env.verbose)
+ return 0;
+ return vfprintf(stderr, format, args);
+}
+
+static void sig_handler(int sig)
+{
+ exiting = true;
+}
+
+static int handle_event(void *ctx, void *data, size_t data_sz)
+{
+ const struct event *e = data;
+
+ if (!env.trace)
+ return 0;
+
+ if (e->dbg) {
+ if (env.verbose)
+ printf("%s\n", e->msg);
+ return 0;
+ }
+
+ printf("%-10s %-5d %-20s\n", e->modname, e->err,
+ modfunc_to_string(e->func));
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct ring_buffer *rb = NULL;
+ struct moderr_bpf *obj;
+ int err;
+
+ err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
+ if (err)
+ return err;
+
+ if (!strlen(env.modname) || !env.func) {
+ fprintf(stderr, "missing arguments\n");
+ return EXIT_FAILURE;
+ }
+
+ libbpf_set_print(libbpf_print_fn);
+
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+
+ obj = moderr_bpf__open();
+ if (!obj) {
+ fprintf(stderr, "failed to open and load BPF object\n");
+ return 1;
+ }
+
+ obj->rodata->filter_modname = true;
+ strncpy(obj->rodata->targ_modname, env.modname, MODULE_NAME_LEN - 1);
+ obj->rodata->targ_modname[MODULE_NAME_LEN - 1] = '\0';
+
+ obj->rodata->filter_modfunc = true;
+ obj->rodata->targ_modfunc = env.func;
+
+ if (env.errinj) {
+ obj->rodata->set_errinj = true;
+ obj->rodata->targ_errinj = env.errinj;
+ }
+
+ err = moderr_bpf__load(obj);
+ if (err) {
+ fprintf(stderr, "failed to load and verify BPF object\n");
+ goto cleanup;
+ }
+
+ err = moderr_bpf__attach(obj);
+ if (err) {
+ fprintf(stderr, "failed to attach BPF object\n");
+ goto cleanup;
+ }
+
+ printf("Monitoring module error injection... Hit Ctrl-C to end.\n");
+
+ rb = ring_buffer__new(bpf_map__fd(obj->maps.rb), handle_event, NULL,
+ NULL);
+ if (!rb) {
+ err = -1;
+ fprintf(stderr, "failed to create ring buffer\n");
+ goto cleanup;
+ }
+
+ if (env.trace)
+ printf("%-10s %-5s %-20s\n", "MODULE", "ERROR", "FUNCTION");
+
+ while (!exiting) {
+ err = ring_buffer__poll(rb, 100);
+ if (err == -EINTR) {
+ err = 0;
+ break;
+ }
+ if (err < 0) {
+ fprintf(stderr, "error polling ring buffer: %d\n", err);
+ break;
+ }
+ }
+
+ printf("\n");
+
+cleanup:
+ ring_buffer__free(rb);
+ moderr_bpf__destroy(obj);
+
+ return err < 0 ? -err : 0;
+}
diff --git a/tools/bpf/moderr/moderr.h b/tools/bpf/moderr/moderr.h
new file mode 100644
index 0000000000000000000000000000000000000000..e17440cf4bd5fe09b927cb83807a88f66861bba5
--- /dev/null
+++ b/tools/bpf/moderr/moderr.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2025 Samsung */
+#ifndef __MODERR_H
+#define __MODERR_H
+
+#define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long))
+#define MODULE_NAME_LEN MAX_PARAM_PREFIX_LEN
+#define MODULE_FUNC_LEN 128
+#define MESSAGE_LEN 128
+
+enum modfunc {
+ UNKNOWN,
+ COMPLETE_FORMATION = 1,
+ DO_INIT_MODULE,
+ MODULE_ENABLE_RODATA_AFTER_INIT,
+};
+
+struct event {
+ char modname[MODULE_NAME_LEN];
+ int err;
+ int func;
+ char msg[MESSAGE_LEN];
+ int dbg;
+};
+
+static inline const char *modfunc_to_string(enum modfunc fc)
+{
+ switch (fc) {
+ case COMPLETE_FORMATION:
+ return "complete_formation()";
+ case DO_INIT_MODULE:
+ return "do_init_module()";
+ case MODULE_ENABLE_RODATA_AFTER_INIT:
+ return "module_enable_rodata_after_init()";
+ default:
+ return "unknown";
+ }
+}
+
+#endif /* __MODERR_H */
--
2.39.5
Powered by blists - more mailing lists