[PATCH] HACK: ftrace stress test

From: Mark Rutland
Date: Wed Mar 06 2024 - 08:23:47 EST


Try to check that ops->func() is called with the correct ops.

Signed-off-by: Mark Rutland <mark.rutland@xxxxxxx>
---
lib/Makefile | 3 +
lib/fstress.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 161 insertions(+)
create mode 100644 lib/fstress.c

diff --git a/lib/Makefile b/lib/Makefile
index 6b09731d8e619..678a916d4f0a0 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -36,6 +36,9 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
nmi_backtrace.o win_minmax.o memcat_p.o \
buildid.o objpool.o

+obj-y += fstress.o
+CFLAGS_fstress.o += $(CC_FLAGS_FTRACE)
+
lib-$(CONFIG_PRINTK) += dump_stack.o
lib-$(CONFIG_SMP) += cpumask.o

diff --git a/lib/fstress.c b/lib/fstress.c
new file mode 100644
index 0000000000000..1ec2303f67f6f
--- /dev/null
+++ b/lib/fstress.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "fstress: " fmt
+
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/ftrace.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+
+#define DEFINE_OPS(x) \
+ static void trace_func_##x(unsigned long ip, \
+ unsigned long parent_ip, \
+ struct ftrace_ops *op, \
+ struct ftrace_regs *fregs); \
+ \
+ struct ftrace_ops ops_##x = { \
+ .func = trace_func_##x, \
+ }; \
+ \
+ static void trace_func_##x(unsigned long ip, \
+ unsigned long parent_ip, \
+ struct ftrace_ops *op, \
+ struct ftrace_regs *fregs) \
+ { \
+ struct ftrace_ops *expected = &ops_##x; \
+ if (op == expected) \
+ return; \
+ panic("%s(%pS, %pS, %pS, %pS) expected ops %pS\n", \
+ __func__, (void *)ip, (void *)parent_ip, \
+ op, fregs, expected); \
+ }
+
+DEFINE_OPS(a);
+DEFINE_OPS(b);
+DEFINE_OPS(c);
+DEFINE_OPS(d);
+
+static struct ftrace_ops *fstress_ops[] = {
+ &ops_a,
+ &ops_b,
+ &ops_c,
+ &ops_d,
+};
+
+static noinline void tracee(void)
+{
+ /*
+ * Do nothing, but prevent compiler from eliding calls to this
+ * function.
+ */
+ barrier();
+}
+
+static noinline int fstress_tracee_thread(void *unused)
+{
+ while (!kthread_should_stop()) {
+ for (int i = 0; i < 100; i++)
+ tracee();
+ cond_resched();
+ }
+
+ return 0;
+}
+
+static int fstress_manager_thread(void *unused)
+{
+ while (!kthread_should_stop()) {
+ int nr_ops = ARRAY_SIZE(fstress_ops);
+
+ for (int i = 0; i <= nr_ops; i++) {
+ if (i < nr_ops)
+ WARN_ON_ONCE(register_ftrace_function(fstress_ops[i]));
+ if (i > 0)
+ WARN_ON_ONCE(unregister_ftrace_function(fstress_ops[i - 1]));
+
+ /*
+ * TODO: introduce some delay here, so that we spent
+ * more time with a single tracer enabled (rather than
+ * the list func).
+ */
+ }
+
+ cond_resched();
+ }
+
+ return 0;
+}
+
+#define NR_TRACEE_THREADS 25
+
+struct task_struct *tracee_threads[NR_TRACEE_THREADS];
+struct task_struct *manager_thread;
+
+static __init int fstress_init(void)
+{
+ int ret;
+
+ pr_info("Initializing ops\n");
+ for (int i = 0; i < ARRAY_SIZE(fstress_ops); i++) {
+ ret = ftrace_set_filter_ip(fstress_ops[i], (unsigned long)tracee, 0, 0);
+ ret = 0;
+ if (ret) {
+ pr_err("Cannot set filter: %d\n", ret);
+ goto out_free_filters;
+ }
+ }
+
+ pr_info("Creating tracee threads\n");
+ for (int i = 0; i < NR_TRACEE_THREADS; i++) {
+ tracee_threads[i] = kthread_create(fstress_tracee_thread, NULL, "fstress_%d", i);
+ if (!tracee_threads[i]) {
+ pr_err("Cannot create tracee thread %d\n", i);
+ goto out_free_tracees;
+ }
+ }
+
+ pr_info("Creating manager thread\n");
+ manager_thread = kthread_create(fstress_manager_thread, NULL, "fstress_manager");
+ if (!manager_thread) {
+ pr_err("Cannot create manager thread\n");
+ goto out_free_tracees;
+ }
+
+ pr_info("Starting threads\n");
+ for (int i = 0; i < NR_TRACEE_THREADS; i++)
+ wake_up_process(tracee_threads[i]);
+ wake_up_process(manager_thread);
+
+ return 0;
+
+out_free_tracees:
+ for (int i = 0; i < NR_TRACEE_THREADS; i++) {
+ if (tracee_threads[i])
+ kthread_stop(tracee_threads[i]);
+ }
+out_free_filters:
+ for (int i = 0; i < ARRAY_SIZE(fstress_ops); i++)
+ ftrace_free_filter(fstress_ops[i]);
+
+ return -EAGAIN;
+}
+
+static __exit void fstress_exit(void)
+{
+ kthread_stop(manager_thread);
+
+ for (int i = 0; i < NR_TRACEE_THREADS; i++)
+ kthread_stop(tracee_threads[i]);
+
+ for (int i = 0; i < ARRAY_SIZE(fstress_ops); i++)
+ ftrace_free_filter(fstress_ops[i]);
+}
+
+module_init(fstress_init);
+module_exit(fstress_exit);
+MODULE_LICENSE("GPL");
--
2.30.2