summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 13:29:22 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 13:29:22 -0700
commit55ee4b931a7ffedc886175d265dd6e6d08fd4151 (patch)
tree23501b461d58dd52edec64fc6f1cb995e7cc5862 /include
parent98f21c54f99519329c18e2625b0ea6db14524d09 (diff)
parent7d81675d1bb2cc6db61a2d93b1e0dc7fb0929f9c (diff)
downloadlinux-55ee4b931a7ffedc886175d265dd6e6d08fd4151.tar.gz
linux-55ee4b931a7ffedc886175d265dd6e6d08fd4151.zip
Merge tag 'trace-rv-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull Real-time Verifier updates from Steven Rostedt: - Switch LTL and DOT parsers to Lark in code generation tool The rvgen code generation tool originally parsed DOT files and LTL specifications using custom string parsing and Ply, which is no longer maintained. The DOT parser was fragile and prone to failure on minor format variations. Both LTL and DOT parsers have been rewritten to use the Lark parsing library. - Simplify Hybrid Automata clock variables The clock variables in hybrid automata monitors now use a single representation of the elapsed time since the clock was reset, rather than converting between invariant and guard representations. This allows simpler code generation for the newly refactored parser. - Generate cleanup hook for per-obj monitor The code generation scripts now adds a cleanup function to per-obj monitors for the user to wire to the appropriate event (e.g. sched_process_exit for tasks). - Reduce read_lock scope during per-task cleanup Take the tasklist_lock only when necessary, that is when iterating over for_each_process_thread(). - Simplify task monitor slot management Only rely on the slot array for per-task slot management to avoid inconsistency with the unused counter. - Improve rvgen code robustness and templates Use pathlib in rvgen and improve kernel path discovery. Also improve consistency across templates when generating code (e.g. author placeholder and monitor struct name). - Update rtapp sleep monitor Simplify the sleep monitor by excluding kernel threads and updating the nanosleep check to focus only on CLOCK_REALTIME. Also switch to use the sched_exit tracepoint to run in the context of the offending (wakee) task. - Add wakeup monitor Add the new rtapp/wakeup monitor to detect when lower-priority tasks wake up higher-priority ones, complementing the existing sleep monitor by running in the waker context and capturing its stack trace. - Fix tools/rv exit status on failure Ensure the rv tool returns a failure exit code when a monitor fails to start because it was already running. - Add automated selftests for tools/rv and rvgen Introduced automated bash selftests to validate rv monitor listing and execution under different configurations. Added tests for the rvgen code generator, validating generated files against expected output (golden). Tests are reachable via make check. - Add KUnit test coverage for verification monitors Added comprehensive KUnit tests to validate the functionality of deterministic, hybrid, and LTL monitors by emulating event sequences and timing in a mock environment without affecting the running kernel while expecting mock reactions to fire. Ensure real RV monitors cannot run during KUnit tests to avoid state corruption. - Mock current in rv monitors Mock the call to current in rv monitors when the KUnit tests are built to allow them to run the test on dummy tasks. No overhead is expected when KUnit tests aren't running. - Introduce rvgen kunit subcommand Added a new 'kunit' subcommand to rvgen to automatically patch an already generated monitor with KUnit integration templates by parsing its event handlers and creating the required mock structures and initializations. - Refine kernel verification selftests Added new selftests for the deadline and stall monitors and rearranged the existing wwnr_printk test to resolve flakiness. Additionally, fixed an issue in the selftests framework where negative assertion failures were not correctly propagated due to shell rules. - Fix 32-bit build of nomiss KUnit test A previous commit introduced a division between an u64 and a constant value and that doesn't build on 32-bit systems. Use div_u64() instead. - Document changes in sleep monitor The sleep monitor introduced some changes in the past like allowing epoll_wait() as a valid sleep and a task going to runnable before scheduling as a valid wakeup. Document both. * tag 'trace-rv-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (40 commits) Documentation/rv: Explain epoll and aborted sleeps rv: Fix 32-bit build of nomiss KUnit test selftests/verification: Add selftests for deadline and stall monitors selftests/verification: Rearrange the wwnr_printk test selftests/verification: Fix wrong errexit assumption rv: Add KUnit tests for some LTL monitors rv: Add KUnit mock for current rv: Add KUnit tests for some DA/HA monitors rv: Export task monitor slot and react symbols verification/rvgen: Add selftests for rvgen kunit verification/rvgen: Add the rvgen kunit subcommand verification/rvgen: Add selftests verification/rvgen: Add golden and spec folders for tests tools/rv: Add selftests verification/rvgen: Improve consistency in template files verification/rvgen: Use pathlib instead of os.path verification/rvgen: Improve rv_dir discovery in RVGenerator tools/rv: Fix exit status when monitor execution fails rv: Use generic rv_this for the rv_monitor variable in LTL rv/rtapp: Add wakeup monitor ...
Diffstat (limited to 'include')
-rw-r--r--include/rv/da_monitor.h31
-rw-r--r--include/rv/ha_monitor.h85
-rw-r--r--include/rv/kunit.h73
-rw-r--r--include/rv/ltl_monitor.h14
4 files changed, 148 insertions, 55 deletions
diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h
index 34b8fba9ecd4..e3cf85c9ce55 100644
--- a/include/rv/da_monitor.h
+++ b/include/rv/da_monitor.h
@@ -16,6 +16,7 @@
#include <rv/automata.h>
#include <linux/rv.h>
+#include <rv/kunit.h>
#include <linux/stringify.h>
#include <linux/bug.h>
#include <linux/sched.h>
@@ -311,6 +312,11 @@ static inline struct da_monitor *da_get_monitor(struct task_struct *tsk)
return &tsk->rv[task_mon_slot].da_mon;
}
+static inline void da_reset(struct task_struct *tsk)
+{
+ da_monitor_reset(da_get_monitor(tsk));
+}
+
/*
* da_get_target - return the task associated to the monitor
*/
@@ -334,12 +340,12 @@ static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
struct task_struct *g, *p;
int cpu;
- read_lock(&tasklist_lock);
- for_each_process_thread(g, p)
- reset(da_get_monitor(p));
+ scoped_guard(read_lock, &tasklist_lock) {
+ for_each_process_thread(g, p)
+ reset(da_get_monitor(p));
+ }
for_each_present_cpu(cpu)
reset(da_get_monitor(idle_task(cpu)));
- read_unlock(&tasklist_lock);
}
static void da_monitor_reset_all(void)
@@ -908,4 +914,21 @@ static inline void da_reset(da_id_type id, monitor_target target)
}
#endif /* RV_MON_TYPE */
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#if RV_MON_TYPE == RV_MON_PER_TASK
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .is_per_task = true, \
+ .task_slot = &task_mon_slot, \
+ .task_reset = da_reset, \
+}
+#else
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .monitor_init = da_monitor_init, \
+ .monitor_destroy = da_monitor_destroy, \
+}
+#endif /* RV_MON_TYPE */
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
+
#endif
diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h
index 28d3c74cabfc..6e1c7fe5449a 100644
--- a/include/rv/ha_monitor.h
+++ b/include/rv/ha_monitor.h
@@ -327,19 +327,8 @@ static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon)
}
/*
- * The clock variables have 2 different representations in the env_store:
- * - The guard representation is the timestamp of the last reset
- * - The invariant representation is the timestamp when the invariant expires
- * As the representations are incompatible, care must be taken when switching
- * between them: the invariant representation can only be used when starting a
- * timer when the previous representation was guard (e.g. no other invariant
- * started since the last reset operation).
- * Likewise, switching from invariant to guard representation without a reset
- * can be done only by subtracting the exact value used to start the invariant.
- *
- * Reading the environment variable (ha_get_clk) also reflects this difference
- * any reads in states that have an invariant return the (possibly negative)
- * time since expiration, other reads return the time since last reset.
+ * The clock variables store the time epoch - the timestamp when the clock was last reset.
+ * They are read by subtracting the time epoch from the current time.
*/
/*
@@ -353,31 +342,21 @@ static inline void ha_reset_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64
{
WRITE_ONCE(ha_mon->env_store[env], time_ns);
}
-static inline void ha_set_invariant_ns(struct ha_monitor *ha_mon, enum envs env,
- u64 value, u64 time_ns)
-{
- WRITE_ONCE(ha_mon->env_store[env], time_ns + value);
-}
-static inline bool ha_check_invariant_ns(struct ha_monitor *ha_mon,
- enum envs env, u64 time_ns)
+static inline bool ha_check_invariant_ns(struct ha_monitor *ha_mon, enum envs env,
+ u64 time_ns, u64 expire_ns)
{
- return READ_ONCE(ha_mon->env_store[env]) >= time_ns;
+ return READ_ONCE(ha_mon->env_store[env]) >= time_ns - expire_ns;
}
/*
* ha_invariant_passed_ns - prepare the invariant and return the time since reset
*/
-static inline u64 ha_invariant_passed_ns(struct ha_monitor *ha_mon, enum envs env,
- u64 expire, u64 time_ns)
+static inline u64 ha_invariant_passed_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
{
- u64 passed = 0;
-
if (env < 0 || env >= ENV_MAX_STORED)
return 0;
if (ha_monitor_env_invalid(ha_mon, env))
return 0;
- passed = ha_get_env(ha_mon, env, time_ns);
- ha_set_invariant_ns(ha_mon, env, expire - passed, time_ns);
- return passed;
+ return ha_get_env(ha_mon, env, time_ns);
}
/*
@@ -391,32 +370,21 @@ static inline void ha_reset_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
{
WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64());
}
-static inline void ha_set_invariant_jiffy(struct ha_monitor *ha_mon,
- enum envs env, u64 value)
+static inline bool ha_check_invariant_jiffy(struct ha_monitor *ha_mon, enum envs env,
+ u64 time_ns, u64 expire_jiffy)
{
- WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64() + value);
-}
-static inline bool ha_check_invariant_jiffy(struct ha_monitor *ha_mon,
- enum envs env, u64 time_ns)
-{
- return time_after64(READ_ONCE(ha_mon->env_store[env]), get_jiffies_64());
-
+ return time_after64(READ_ONCE(ha_mon->env_store[env]), get_jiffies_64() - expire_jiffy);
}
/*
* ha_invariant_passed_jiffy - prepare the invariant and return the time since reset
*/
-static inline u64 ha_invariant_passed_jiffy(struct ha_monitor *ha_mon, enum envs env,
- u64 expire, u64 time_ns)
+static inline u64 ha_invariant_passed_jiffy(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
{
- u64 passed = 0;
-
if (env < 0 || env >= ENV_MAX_STORED)
return 0;
if (ha_monitor_env_invalid(ha_mon, env))
return 0;
- passed = ha_get_env(ha_mon, env, time_ns);
- ha_set_invariant_jiffy(ha_mon, env, expire - passed);
- return passed;
+ return ha_get_env(ha_mon, env, time_ns);
}
/*
@@ -463,14 +431,14 @@ static inline void ha_setup_timer(struct ha_monitor *ha_mon)
static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
- u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
+ u64 passed = ha_invariant_passed_jiffy(ha_mon, env, time_ns);
mod_timer(&ha_mon->timer, get_jiffies_64() + expire - passed);
}
static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
- u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
+ u64 passed = ha_invariant_passed_ns(ha_mon, env, time_ns);
ha_start_timer_jiffy(ha_mon, ENV_MAX_STORED,
nsecs_to_jiffies(expire - passed + TICK_NSEC - 1), time_ns);
@@ -516,7 +484,7 @@ static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
int mode = HRTIMER_MODE_REL_HARD;
- u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
+ u64 passed = ha_invariant_passed_ns(ha_mon, env, time_ns);
if (RV_MON_TYPE == RV_MON_PER_CPU)
mode |= HRTIMER_MODE_PINNED;
@@ -525,7 +493,7 @@ static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
- u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
+ u64 passed = ha_invariant_passed_jiffy(ha_mon, env, time_ns);
ha_start_timer_ns(ha_mon, ENV_MAX_STORED,
jiffies_to_nsecs(expire - passed), time_ns);
@@ -558,4 +526,25 @@ static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon) { }
#endif
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#ifdef RV_MON_OPS_INIT
+#undef RV_MON_OPS_INIT
+#endif
+
+#if RV_MON_TYPE == RV_MON_PER_TASK
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .is_per_task = true, \
+ .task_slot = &task_mon_slot, \
+ .task_reset = da_reset, \
+}
+#else
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .monitor_init = ha_monitor_init, \
+ .monitor_destroy = ha_monitor_destroy, \
+}
+#endif /* RV_MON_TYPE */
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
+
#endif
diff --git a/include/rv/kunit.h b/include/rv/kunit.h
new file mode 100644
index 000000000000..31e0b93c40ea
--- /dev/null
+++ b/include/rv/kunit.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
+ *
+ * Declaration of wrappers to allow mocking core functionality, like current,
+ * and other testing utilities.
+ * Necessary only when mocking may be needed. If the RV KUnit test is
+ * enabled, the wrappers incur an additional function call overhead.
+ */
+
+#ifndef _RV_KUNIT_H
+#define _RV_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+#include <linux/delay.h>
+
+int rv_set_testing(struct kunit_suite *suite);
+void rv_clear_testing(struct kunit_suite *suite);
+
+#define RV_KUNIT_MAX_MOCK_TASKS 8
+
+struct rv_kunit_ctx {
+ int reactions, expected;
+ int mock_task_count;
+ struct task_struct *mock_tasks[RV_KUNIT_MAX_MOCK_TASKS];
+};
+
+#define RV_KUNIT_EXPECT_REACTION(test, ctx) \
+ do { \
+ KUNIT_EXPECT_EQ(test, ctx->reactions, ++ctx->expected); \
+ if (ctx->reactions != ctx->expected) \
+ ctx->expected = ctx->reactions; \
+ } while (0)
+
+#define RV_KUNIT_EXPECT_NO_REACTION(test, ctx) \
+ do { \
+ KUNIT_EXPECT_EQ(test, ctx->reactions, ctx->expected); \
+ if (ctx->reactions != ctx->expected) \
+ ctx->expected = ctx->reactions; \
+ } while (0)
+
+#define RV_KUNIT_EXPECT_REACTION_HERE(test, ctx) \
+ for (int __done = ({ RV_KUNIT_EXPECT_NO_REACTION(test, ctx); 0; }); \
+ !__done; \
+ __done = ({ RV_KUNIT_EXPECT_REACTION(test, ctx); 1; }))
+
+struct rv_kunit_mon {
+ struct rv_monitor *rv_this;
+ int (*monitor_init)(void);
+ void (*monitor_destroy)(void);
+ bool is_per_task;
+ int *task_slot;
+ void (*task_reset)(struct task_struct *task);
+};
+
+void prepare_test(struct kunit *test, const struct rv_kunit_mon *mon);
+void teardown_test(void *arg);
+struct task_struct *rv_kunit_alloc_mock_task(struct kunit *test);
+
+void rv_mock_current(struct task_struct *tsk);
+struct task_struct *rv_get_mock_current(void);
+
+#define rv_get_current() (unlikely(kunit_get_current_test()) ? rv_get_mock_current() : current)
+
+#else /* !CONFIG_RV_MONITORS_KUNIT_TEST */
+
+#define rv_get_current() current
+
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
+#endif /* _RV_KUNIT_H */
diff --git a/include/rv/ltl_monitor.h b/include/rv/ltl_monitor.h
index 38e792401f76..e9fd8265a3da 100644
--- a/include/rv/ltl_monitor.h
+++ b/include/rv/ltl_monitor.h
@@ -9,6 +9,7 @@
#include <linux/stringify.h>
#include <linux/seq_buf.h>
#include <rv/instrumentation.h>
+#include <rv/kunit.h>
#include <trace/events/task.h>
#include <trace/events/sched.h>
@@ -16,8 +17,7 @@
#error "Please include $(MODEL_NAME).h generated by rvgen"
#endif
-#define RV_MONITOR_NAME CONCATENATE(rv_, MONITOR_NAME)
-static struct rv_monitor RV_MONITOR_NAME;
+static struct rv_monitor rv_this;
static int ltl_monitor_slot = RV_PER_TASK_MONITOR_INIT;
@@ -85,7 +85,7 @@ static void ltl_monitor_destroy(void)
static void ltl_illegal_state(struct task_struct *task, struct ltl_monitor *mon)
{
CONCATENATE(trace_error_, MONITOR_NAME)(task);
- rv_react(&RV_MONITOR_NAME, "rv: "__stringify(MONITOR_NAME)": %s[%d]: violation detected\n",
+ rv_react(&rv_this, "rv: "__stringify(MONITOR_NAME)": %s[%d]: violation detected\n",
task->comm, task->pid);
}
@@ -172,3 +172,11 @@ static void __maybe_unused ltl_atom_pulse(struct task_struct *task, enum ltl_ato
ltl_atom_set(mon, atom, !value);
ltl_validate(task, mon);
}
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .is_per_task = true, \
+ .task_slot = &ltl_monitor_slot, \
+}
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */