summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/trace/rv/Kconfig15
-rw-r--r--kernel/trace/rv/Makefile2
-rw-r--r--kernel/trace/rv/monitors/nomiss/nomiss.c36
-rw-r--r--kernel/trace/rv/monitors/nomiss/nomiss_kunit.c38
-rw-r--r--kernel/trace/rv/monitors/nomiss/nomiss_kunit.h35
-rw-r--r--kernel/trace/rv/monitors/opid/opid.c12
-rw-r--r--kernel/trace/rv/monitors/opid/opid_kunit.c33
-rw-r--r--kernel/trace/rv/monitors/opid/opid_kunit.h23
-rw-r--r--kernel/trace/rv/monitors/pagefault/pagefault.c20
-rw-r--r--kernel/trace/rv/monitors/pagefault/pagefault_kunit.c34
-rw-r--r--kernel/trace/rv/monitors/pagefault/pagefault_kunit.h24
-rw-r--r--kernel/trace/rv/monitors/rtapp/Kconfig2
-rw-r--r--kernel/trace/rv/monitors/sco/sco.c13
-rw-r--r--kernel/trace/rv/monitors/sco/sco_kunit.c29
-rw-r--r--kernel/trace/rv/monitors/sco/sco_kunit.h24
-rw-r--r--kernel/trace/rv/monitors/sleep/Kconfig1
-rw-r--r--kernel/trace/rv/monitors/sleep/sleep.c107
-rw-r--r--kernel/trace/rv/monitors/sleep/sleep.h142
-rw-r--r--kernel/trace/rv/monitors/sleep/sleep_kunit.c59
-rw-r--r--kernel/trace/rv/monitors/sleep/sleep_kunit.h29
-rw-r--r--kernel/trace/rv/monitors/sssw/sssw.c14
-rw-r--r--kernel/trace/rv/monitors/sssw/sssw_kunit.c33
-rw-r--r--kernel/trace/rv/monitors/sssw/sssw_kunit.h30
-rw-r--r--kernel/trace/rv/monitors/stall/stall.c2
-rw-r--r--kernel/trace/rv/monitors/sts/sts.c19
-rw-r--r--kernel/trace/rv/monitors/sts/sts_kunit.c39
-rw-r--r--kernel/trace/rv/monitors/sts/sts_kunit.h33
-rw-r--r--kernel/trace/rv/monitors/wakeup/Kconfig16
-rw-r--r--kernel/trace/rv/monitors/wakeup/wakeup.c153
-rw-r--r--kernel/trace/rv/monitors/wakeup/wakeup.h92
-rw-r--r--kernel/trace/rv/monitors/wakeup/wakeup_trace.h14
-rw-r--r--kernel/trace/rv/rv.c86
-rw-r--r--kernel/trace/rv/rv_monitors_test.c182
-rw-r--r--kernel/trace/rv/rv_reactors.c1
-rw-r--r--kernel/trace/rv/rv_trace.h1
35 files changed, 1212 insertions, 181 deletions
diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
index 3884b14df375..efa930f94ea4 100644
--- a/kernel/trace/rv/Kconfig
+++ b/kernel/trace/rv/Kconfig
@@ -76,6 +76,7 @@ source "kernel/trace/rv/monitors/opid/Kconfig"
source "kernel/trace/rv/monitors/rtapp/Kconfig"
source "kernel/trace/rv/monitors/pagefault/Kconfig"
source "kernel/trace/rv/monitors/sleep/Kconfig"
+source "kernel/trace/rv/monitors/wakeup/Kconfig"
# Add new rtapp monitors here
source "kernel/trace/rv/monitors/stall/Kconfig"
@@ -111,3 +112,17 @@ config RV_REACT_PANIC
help
Enables the panic reactor. The panic reactor emits a printk()
message if an exception is found and panic()s the system.
+
+config RV_MONITORS_KUNIT_TEST
+ tristate "KUnit tests for RV monitors" if !KUNIT_ALL_TESTS
+ depends on KUNIT && RV && RV_REACTORS
+ default KUNIT_ALL_TESTS
+ help
+ Enable KUnit tests for the RV (Runtime Verification) monitors.
+ These tests verify that monitors correctly detect violations by
+ triggering fake events and validating the expected reactions.
+
+ Enabling this may slightly increase overhead of some monitors if any
+ unrelated KUnit test is running.
+
+ If unsure, say N.
diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
index 94498da35b37..cdbf68c84f5a 100644
--- a/kernel/trace/rv/Makefile
+++ b/kernel/trace/rv/Makefile
@@ -20,7 +20,9 @@ obj-$(CONFIG_RV_MON_OPID) += monitors/opid/opid.o
obj-$(CONFIG_RV_MON_STALL) += monitors/stall/stall.o
obj-$(CONFIG_RV_MON_DEADLINE) += monitors/deadline/deadline.o
obj-$(CONFIG_RV_MON_NOMISS) += monitors/nomiss/nomiss.o
+obj-$(CONFIG_RV_MON_WAKEUP) += monitors/wakeup/wakeup.o
# Add new monitors here
obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
obj-$(CONFIG_RV_REACT_PANIC) += reactor_panic.o
+obj-$(CONFIG_RV_MONITORS_KUNIT_TEST) += rv_monitors_test.o
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.c b/kernel/trace/rv/monitors/nomiss/nomiss.c
index 8ead8783c29f..6e47d379f777 100644
--- a/kernel/trace/rv/monitors/nomiss/nomiss.c
+++ b/kernel/trace/rv/monitors/nomiss/nomiss.c
@@ -57,24 +57,12 @@ static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
enum states next_state, u64 time_ns)
{
if (curr_state == ready_nomiss)
- return ha_check_invariant_ns(ha_mon, clk_nomiss, time_ns);
+ return ha_check_invariant_ns(ha_mon, clk_nomiss, time_ns, DEADLINE_NS(ha_mon));
else if (curr_state == running_nomiss)
- return ha_check_invariant_ns(ha_mon, clk_nomiss, time_ns);
+ return ha_check_invariant_ns(ha_mon, clk_nomiss, time_ns, DEADLINE_NS(ha_mon));
return true;
}
-static inline void ha_convert_inv_guard(struct ha_monitor *ha_mon,
- enum states curr_state, enum events event,
- enum states next_state, u64 time_ns)
-{
- if (curr_state == next_state)
- return;
- if (curr_state == ready_nomiss)
- ha_inv_to_guard(ha_mon, clk_nomiss, DEADLINE_NS(ha_mon), time_ns);
- else if (curr_state == running_nomiss)
- ha_inv_to_guard(ha_mon, clk_nomiss, DEADLINE_NS(ha_mon), time_ns);
-}
-
static inline bool ha_verify_guards(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
@@ -122,8 +110,6 @@ static bool ha_verify_constraint(struct ha_monitor *ha_mon,
if (!ha_verify_invariants(ha_mon, curr_state, event, next_state, time_ns))
return false;
- ha_convert_inv_guard(ha_mon, curr_state, event, next_state, time_ns);
-
if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
return false;
@@ -291,3 +277,21 @@ module_exit(unregister_nomiss);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("nomiss: dl entities run to completion before their deadline.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "nomiss_kunit.h"
+
+const struct rv_nomiss_ops rv_nomiss_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .deadline_thresh = &deadline_thresh,
+ .handle_dl_replenish = handle_dl_replenish,
+ .handle_dl_throttle = handle_dl_throttle,
+ .handle_dl_server_stop = handle_dl_server_stop,
+ .handle_sched_switch = handle_sched_switch,
+ .handle_sched_wakeup = handle_sched_wakeup,
+ .handle_sys_enter = handle_sys_enter,
+ .handle_newtask = handle_newtask,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_nomiss_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss_kunit.c b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.c
new file mode 100644
index 000000000000..763129e2a990
--- /dev/null
+++ b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "nomiss_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_NOMISS)
+
+static void rv_test_nomiss(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_nomiss_ops.mon);
+
+ target->pid = 99;
+ target->policy = SCHED_DEADLINE;
+ target->dl.runtime = 10000;
+ target->dl.dl_deadline = 20000;
+
+ rv_nomiss_ops.handle_newtask(NULL, target, 0);
+
+ /* Task gets preempted and can't terminate before deadline */
+ rv_nomiss_ops.handle_sched_switch(NULL, 0, other, target, TASK_RUNNING);
+ rv_nomiss_ops.handle_dl_replenish(NULL, &target->dl, 0, DL_TASK);
+ udelay(10);
+ rv_nomiss_ops.handle_sched_switch(NULL, 0, target, other, TASK_RUNNING);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx) {
+ udelay(15 + div_u64(*rv_nomiss_ops.deadline_thresh, 1000));
+ rv_nomiss_ops.handle_sched_switch(NULL, 0, other, target, TASK_RUNNING);
+ }
+}
+
+#else
+#define rv_test_nomiss rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss_kunit.h b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.h
new file mode 100644
index 000000000000..2be779c5dbaa
--- /dev/null
+++ b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __NOMISS_KUNIT_H
+#define __NOMISS_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_nomiss_ops {
+ struct rv_kunit_mon mon;
+ const u64 *deadline_thresh;
+ void (*handle_dl_replenish)(void *data, struct sched_dl_entity *dl_se,
+ int cpu, u8 type);
+ void (*handle_dl_throttle)(void *data, struct sched_dl_entity *dl_se,
+ int cpu, u8 type);
+ void (*handle_dl_server_stop)(void *data, struct sched_dl_entity *dl_se,
+ int cpu, u8 type);
+ void (*handle_sched_switch)(void *data, bool preempt,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned int prev_state);
+ void (*handle_sched_wakeup)(void *data, struct task_struct *tsk);
+ void (*handle_sys_enter)(void *data, struct pt_regs *regs, long id);
+ void (*handle_newtask)(void *data, struct task_struct *task, u64 flags);
+} rv_nomiss_ops;
+#endif
+
+#endif /* __NOMISS_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/opid/opid.c b/kernel/trace/rv/monitors/opid/opid.c
index 3b6a85e815b8..9ae619f176fa 100644
--- a/kernel/trace/rv/monitors/opid/opid.c
+++ b/kernel/trace/rv/monitors/opid/opid.c
@@ -115,3 +115,15 @@ module_exit(unregister_opid);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("opid: operations with preemption and irq disabled.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "opid_kunit.h"
+
+const struct rv_opid_ops rv_opid_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_need_resched = handle_sched_need_resched,
+ .handle_sched_waking = handle_sched_waking,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_opid_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/opid/opid_kunit.c b/kernel/trace/rv/monitors/opid/opid_kunit.c
new file mode 100644
index 000000000000..3cb087a74241
--- /dev/null
+++ b/kernel/trace/rv/monitors/opid/opid_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "opid_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_OPID)
+
+static void rv_test_opid(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_opid_ops.mon);
+
+ /* Ensure we keep the same per-cpu monitor */
+ guard(migrate)();
+ KUNIT_EXPECT_TRUE(test, preemptible());
+
+ /* Wakeup with preemption and interrupts enabled */
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_opid_ops.handle_sched_waking(NULL, NULL);
+
+ /* Need resched with interrupts enabled */
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx) {
+ scoped_guard(preempt)
+ rv_opid_ops.handle_sched_need_resched(NULL, NULL, 0, TIF_NEED_RESCHED);
+ }
+}
+
+#else
+#define rv_test_opid rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/opid/opid_kunit.h b/kernel/trace/rv/monitors/opid/opid_kunit.h
new file mode 100644
index 000000000000..4969c6175957
--- /dev/null
+++ b/kernel/trace/rv/monitors/opid/opid_kunit.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __OPID_KUNIT_H
+#define __OPID_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_opid_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_need_resched)(void *data, struct task_struct *tsk, int cpu, int tif);
+ void (*handle_sched_waking)(void *data, struct task_struct *p);
+} rv_opid_ops;
+#endif
+
+#endif /* __OPID_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault.c b/kernel/trace/rv/monitors/pagefault/pagefault.c
index 9fe6123b2200..c599fc19fc88 100644
--- a/kernel/trace/rv/monitors/pagefault/pagefault.c
+++ b/kernel/trace/rv/monitors/pagefault/pagefault.c
@@ -38,7 +38,7 @@ static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bo
static void handle_page_fault(void *data, unsigned long address, struct pt_regs *regs,
unsigned long error_code)
{
- ltl_atom_pulse(current, LTL_PAGEFAULT, true);
+ ltl_atom_pulse(rv_get_current(), LTL_PAGEFAULT, true);
}
static int enable_pagefault(void)
@@ -63,7 +63,7 @@ static void disable_pagefault(void)
ltl_monitor_destroy();
}
-static struct rv_monitor rv_pagefault = {
+static struct rv_monitor rv_this = {
.name = "pagefault",
.description = "Monitor that RT tasks do not raise page faults",
.enable = enable_pagefault,
@@ -72,12 +72,12 @@ static struct rv_monitor rv_pagefault = {
static int __init register_pagefault(void)
{
- return rv_register_monitor(&rv_pagefault, &rv_rtapp);
+ return rv_register_monitor(&rv_this, &rv_rtapp);
}
static void __exit unregister_pagefault(void)
{
- rv_unregister_monitor(&rv_pagefault);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_pagefault);
@@ -86,3 +86,15 @@ module_exit(unregister_pagefault);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nam Cao <namcao@linutronix.de>");
MODULE_DESCRIPTION("pagefault: Monitor that RT tasks do not raise page faults");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "pagefault_kunit.h"
+
+const struct rv_pagefault_ops rv_pagefault_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_page_fault = handle_page_fault,
+ .handle_task_newtask = handle_task_newtask,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_pagefault_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault_kunit.c b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.c
new file mode 100644
index 000000000000..06369960b008
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <linux/sched/deadline.h>
+#include <linux/sched/rt.h>
+#include "pagefault_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_PAGEFAULT)
+
+static void rv_test_pagefault(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_pagefault_ops.mon);
+
+ /* Initial pagefault when non-RT to start the model without failure */
+ target->policy = SCHED_NORMAL;
+ target->prio = MAX_RT_PRIO + 20;
+ rv_pagefault_ops.handle_task_newtask(NULL, target, 0);
+ rv_mock_current(target);
+ rv_pagefault_ops.handle_page_fault(NULL, 0, NULL, 0);
+
+ /* RT task has a page fault */
+ target->policy = SCHED_FIFO;
+ target->prio = MAX_RT_PRIO - 1;
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_pagefault_ops.handle_page_fault(NULL, 0, NULL, 0);
+}
+
+#else
+#define rv_test_pagefault rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault_kunit.h b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.h
new file mode 100644
index 000000000000..2f9652f08b3f
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __PAGEFAULT_KUNIT_H
+#define __PAGEFAULT_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_pagefault_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_page_fault)(void *data, unsigned long address, struct pt_regs *regs,
+ unsigned long error_code);
+ void (*handle_task_newtask)(void *data, struct task_struct *task, u64 flags);
+} rv_pagefault_ops;
+#endif
+
+#endif /* __PAGEFAULT_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/rtapp/Kconfig b/kernel/trace/rv/monitors/rtapp/Kconfig
index 1ce9370a9ba8..1fcd7a400ded 100644
--- a/kernel/trace/rv/monitors/rtapp/Kconfig
+++ b/kernel/trace/rv/monitors/rtapp/Kconfig
@@ -1,6 +1,6 @@
config RV_MON_RTAPP
depends on RV
- depends on RV_PER_TASK_MONITORS >= 2
+ depends on RV_PER_TASK_MONITORS >= 3
bool "rtapp monitor"
help
Collection of monitors to check for common problems with real-time
diff --git a/kernel/trace/rv/monitors/sco/sco.c b/kernel/trace/rv/monitors/sco/sco.c
index 5a3bd5e16e62..1ef1b96e859d 100644
--- a/kernel/trace/rv/monitors/sco/sco.c
+++ b/kernel/trace/rv/monitors/sco/sco.c
@@ -83,3 +83,16 @@ module_exit(unregister_sco);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sco: scheduling context operations.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sco_kunit.h"
+
+const struct rv_sco_ops rv_sco_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_set_state = handle_sched_set_state,
+ .handle_schedule_entry = handle_schedule_entry,
+ .handle_schedule_exit = handle_schedule_exit,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sco_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sco/sco_kunit.c b/kernel/trace/rv/monitors/sco/sco_kunit.c
new file mode 100644
index 000000000000..5e59bcbfcf0b
--- /dev/null
+++ b/kernel/trace/rv/monitors/sco/sco_kunit.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "sco_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_SCO)
+
+static void rv_test_sco(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_sco_ops.mon);
+
+ /* Ensure we keep the same per-cpu monitor */
+ guard(migrate)();
+
+ /* Set state while scheduling */
+ rv_sco_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_sco_ops.handle_schedule_entry(NULL, false);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sco_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+}
+
+#else
+#define rv_test_sco rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sco/sco_kunit.h b/kernel/trace/rv/monitors/sco/sco_kunit.h
new file mode 100644
index 000000000000..567757df6b1d
--- /dev/null
+++ b/kernel/trace/rv/monitors/sco/sco_kunit.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __SCO_KUNIT_H
+#define __SCO_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sco_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_set_state)(void *data, struct task_struct *tsk, int state);
+ void (*handle_schedule_entry)(void *data, bool preempt);
+ void (*handle_schedule_exit)(void *data, bool is_switch);
+} rv_sco_ops;
+#endif
+
+#endif /* __SCO_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/sleep/Kconfig b/kernel/trace/rv/monitors/sleep/Kconfig
index 6b7a122e7b47..d6ec3e9a91b6 100644
--- a/kernel/trace/rv/monitors/sleep/Kconfig
+++ b/kernel/trace/rv/monitors/sleep/Kconfig
@@ -5,7 +5,6 @@ config RV_MON_SLEEP
select RV_LTL_MONITOR
depends on HAVE_SYSCALL_TRACEPOINTS
depends on RV_MON_RTAPP
- select TRACE_IRQFLAGS
default y
select LTL_MON_EVENTS_ID
bool "sleep monitor"
diff --git a/kernel/trace/rv/monitors/sleep/sleep.c b/kernel/trace/rv/monitors/sleep/sleep.c
index 8dfe5ec13e19..b82537251e09 100644
--- a/kernel/trace/rv/monitors/sleep/sleep.c
+++ b/kernel/trace/rv/monitors/sleep/sleep.c
@@ -36,16 +36,14 @@ static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon)
static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
{
ltl_atom_set(mon, LTL_SLEEP, false);
- ltl_atom_set(mon, LTL_WAKE, false);
+ ltl_atom_set(mon, LTL_SCHEDULE_IN, false);
ltl_atom_set(mon, LTL_ABORT_SLEEP, false);
ltl_atom_set(mon, LTL_WOKEN_BY_HARDIRQ, false);
ltl_atom_set(mon, LTL_WOKEN_BY_NMI, false);
ltl_atom_set(mon, LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO, false);
if (task_creation) {
- ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
+ ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
@@ -54,34 +52,7 @@ static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bo
ltl_atom_set(mon, LTL_BLOCK_ON_RT_MUTEX, false);
}
- if (task->flags & PF_KTHREAD) {
- ltl_atom_set(mon, LTL_KERNEL_THREAD, true);
-
- /* kernel tasks do not do syscall */
- ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
- ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
- ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
- ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
- ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
-
- if (strstarts(task->comm, "migration/"))
- ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, true);
- else
- ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, false);
-
- if (strstarts(task->comm, "rcu"))
- ltl_atom_set(mon, LTL_TASK_IS_RCU, true);
- else
- ltl_atom_set(mon, LTL_TASK_IS_RCU, false);
- } else {
- ltl_atom_set(mon, LTL_KTHREAD_SHOULD_STOP, false);
- ltl_atom_set(mon, LTL_KERNEL_THREAD, false);
- ltl_atom_set(mon, LTL_TASK_IS_RCU, false);
- ltl_atom_set(mon, LTL_TASK_IS_MIGRATION, false);
- }
-
+ ltl_atom_set(mon, LTL_USER_THREAD, !(task->flags & PF_KTHREAD));
}
static void handle_sched_set_state(void *data, struct task_struct *task, int state)
@@ -92,17 +63,17 @@ static void handle_sched_set_state(void *data, struct task_struct *task, int sta
ltl_atom_pulse(task, LTL_ABORT_SLEEP, true);
}
-static void handle_sched_wakeup(void *data, struct task_struct *task)
+static void handle_sched_exit(void *data, bool is_switch)
{
- ltl_atom_pulse(task, LTL_WAKE, true);
+ ltl_atom_pulse(rv_get_current(), LTL_SCHEDULE_IN, true);
}
static void handle_sched_waking(void *data, struct task_struct *task)
{
- if (this_cpu_read(hardirq_context)) {
+ if (in_hardirq()) {
ltl_atom_pulse(task, LTL_WOKEN_BY_HARDIRQ, true);
} else if (in_task()) {
- if (current->prio <= task->prio)
+ if (rv_get_current()->prio <= task->prio)
ltl_atom_pulse(task, LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO, true);
} else if (in_nmi()) {
ltl_atom_pulse(task, LTL_WOKEN_BY_NMI, true);
@@ -112,12 +83,12 @@ static void handle_sched_waking(void *data, struct task_struct *task)
static void handle_contention_begin(void *data, void *lock, unsigned int flags)
{
if (flags & LCB_F_RT)
- ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, true);
+ ltl_atom_update(rv_get_current(), LTL_BLOCK_ON_RT_MUTEX, true);
}
static void handle_contention_end(void *data, void *lock, int ret)
{
- ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, false);
+ ltl_atom_update(rv_get_current(), LTL_BLOCK_ON_RT_MUTEX, false);
}
static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
@@ -126,7 +97,7 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
unsigned long args[6];
int op, cmd;
- mon = ltl_get_monitor(current);
+ mon = ltl_get_monitor(rv_get_current());
switch (id) {
#ifdef __NR_clock_nanosleep
@@ -135,11 +106,10 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
#ifdef __NR_clock_nanosleep_time64
case __NR_clock_nanosleep_time64:
#endif
- syscall_get_arguments(current, regs, args);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, args[0] == CLOCK_MONOTONIC);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, args[0] == CLOCK_TAI);
+ syscall_get_arguments(rv_get_current(), regs, args);
+ ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, args[0] == CLOCK_REALTIME);
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, args[1] == TIMER_ABSTIME);
- ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, true);
+ ltl_atom_update(rv_get_current(), LTL_CLOCK_NANOSLEEP, true);
break;
#ifdef __NR_futex
@@ -148,25 +118,25 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
#ifdef __NR_futex_time64
case __NR_futex_time64:
#endif
- syscall_get_arguments(current, regs, args);
+ syscall_get_arguments(rv_get_current(), regs, args);
op = args[1];
cmd = op & FUTEX_CMD_MASK;
switch (cmd) {
case FUTEX_LOCK_PI:
case FUTEX_LOCK_PI2:
- ltl_atom_update(current, LTL_FUTEX_LOCK_PI, true);
+ ltl_atom_update(rv_get_current(), LTL_FUTEX_LOCK_PI, true);
break;
case FUTEX_WAIT:
case FUTEX_WAIT_BITSET:
case FUTEX_WAIT_REQUEUE_PI:
- ltl_atom_update(current, LTL_FUTEX_WAIT, true);
+ ltl_atom_update(rv_get_current(), LTL_FUTEX_WAIT, true);
break;
}
break;
#ifdef __NR_epoll_wait
case __NR_epoll_wait:
- ltl_atom_update(current, LTL_EPOLL_WAIT, true);
+ ltl_atom_update(rv_get_current(), LTL_EPOLL_WAIT, true);
break;
#endif
}
@@ -174,21 +144,14 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
static void handle_sys_exit(void *data, struct pt_regs *regs, long ret)
{
- struct ltl_monitor *mon = ltl_get_monitor(current);
+ struct ltl_monitor *mon = ltl_get_monitor(rv_get_current());
ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, false);
- ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
+ ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_REALTIME, false);
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
- ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, false);
-}
-
-static void handle_kthread_stop(void *data, struct task_struct *task)
-{
- /* FIXME: this could race with other tracepoint handlers */
- ltl_atom_update(task, LTL_KTHREAD_SHOULD_STOP, true);
+ ltl_atom_update(rv_get_current(), LTL_CLOCK_NANOSLEEP, false);
}
static int enable_sleep(void)
@@ -200,11 +163,10 @@ static int enable_sleep(void)
return retval;
rv_attach_trace_probe("rtapp_sleep", sched_waking, handle_sched_waking);
- rv_attach_trace_probe("rtapp_sleep", sched_wakeup, handle_sched_wakeup);
+ rv_attach_trace_probe("rtapp_sleep", sched_exit_tp, handle_sched_exit);
rv_attach_trace_probe("rtapp_sleep", sched_set_state_tp, handle_sched_set_state);
rv_attach_trace_probe("rtapp_sleep", contention_begin, handle_contention_begin);
rv_attach_trace_probe("rtapp_sleep", contention_end, handle_contention_end);
- rv_attach_trace_probe("rtapp_sleep", sched_kthread_stop, handle_kthread_stop);
rv_attach_trace_probe("rtapp_sleep", sys_enter, handle_sys_enter);
rv_attach_trace_probe("rtapp_sleep", sys_exit, handle_sys_exit);
return 0;
@@ -213,18 +175,17 @@ static int enable_sleep(void)
static void disable_sleep(void)
{
rv_detach_trace_probe("rtapp_sleep", sched_waking, handle_sched_waking);
- rv_detach_trace_probe("rtapp_sleep", sched_wakeup, handle_sched_wakeup);
+ rv_detach_trace_probe("rtapp_sleep", sched_exit_tp, handle_sched_exit);
rv_detach_trace_probe("rtapp_sleep", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("rtapp_sleep", contention_begin, handle_contention_begin);
rv_detach_trace_probe("rtapp_sleep", contention_end, handle_contention_end);
- rv_detach_trace_probe("rtapp_sleep", sched_kthread_stop, handle_kthread_stop);
rv_detach_trace_probe("rtapp_sleep", sys_enter, handle_sys_enter);
rv_detach_trace_probe("rtapp_sleep", sys_exit, handle_sys_exit);
ltl_monitor_destroy();
}
-static struct rv_monitor rv_sleep = {
+static struct rv_monitor rv_this = {
.name = "sleep",
.description = "Monitor that RT tasks do not undesirably sleep",
.enable = enable_sleep,
@@ -233,12 +194,12 @@ static struct rv_monitor rv_sleep = {
static int __init register_sleep(void)
{
- return rv_register_monitor(&rv_sleep, &rv_rtapp);
+ return rv_register_monitor(&rv_this, &rv_rtapp);
}
static void __exit unregister_sleep(void)
{
- rv_unregister_monitor(&rv_sleep);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_sleep);
@@ -247,3 +208,21 @@ module_exit(unregister_sleep);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nam Cao <namcao@linutronix.de>");
MODULE_DESCRIPTION("sleep: Monitor that RT tasks do not undesirably sleep");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sleep_kunit.h"
+
+const struct rv_sleep_ops rv_sleep_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_waking = handle_sched_waking,
+ .handle_sched_exit = handle_sched_exit,
+ .handle_sched_set_state = handle_sched_set_state,
+ .handle_contention_begin = handle_contention_begin,
+ .handle_contention_end = handle_contention_end,
+ .handle_sys_enter = handle_sys_enter,
+ .handle_sys_exit = handle_sys_exit,
+ .handle_task_newtask = handle_task_newtask,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sleep_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sleep/sleep.h b/kernel/trace/rv/monitors/sleep/sleep.h
index 95dc2727c059..44e593f41e6a 100644
--- a/kernel/trace/rv/monitors/sleep/sleep.h
+++ b/kernel/trace/rv/monitors/sleep/sleep.h
@@ -18,16 +18,12 @@ enum ltl_atom {
LTL_EPOLL_WAIT,
LTL_FUTEX_LOCK_PI,
LTL_FUTEX_WAIT,
- LTL_KERNEL_THREAD,
- LTL_KTHREAD_SHOULD_STOP,
- LTL_NANOSLEEP_CLOCK_MONOTONIC,
- LTL_NANOSLEEP_CLOCK_TAI,
+ LTL_NANOSLEEP_CLOCK_REALTIME,
LTL_NANOSLEEP_TIMER_ABSTIME,
LTL_RT,
+ LTL_SCHEDULE_IN,
LTL_SLEEP,
- LTL_TASK_IS_MIGRATION,
- LTL_TASK_IS_RCU,
- LTL_WAKE,
+ LTL_USER_THREAD,
LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
LTL_WOKEN_BY_HARDIRQ,
LTL_WOKEN_BY_NMI,
@@ -44,16 +40,12 @@ static const char *ltl_atom_str(enum ltl_atom atom)
"ep_wa",
"fu_lo_pi",
"fu_wa",
- "ker_th",
- "kth_sh_st",
- "na_cl_mo",
- "na_cl_ta",
+ "na_cl_re",
"na_ti_ab",
"rt",
- "sl",
- "ta_mi",
- "ta_rc",
- "wak",
+ "sch_in",
+ "sle",
+ "us_th",
"wo_eq_hi_pr",
"wo_ha",
"wo_nm",
@@ -81,47 +73,41 @@ static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
bool woken_by_equal_or_higher_prio = test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
mon->atoms);
- bool wake = test_bit(LTL_WAKE, mon->atoms);
- bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
- bool task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
+ bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
bool sleep = test_bit(LTL_SLEEP, mon->atoms);
+ bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
bool rt = test_bit(LTL_RT, mon->atoms);
bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME, mon->atoms);
- bool nanosleep_clock_tai = test_bit(LTL_NANOSLEEP_CLOCK_TAI, mon->atoms);
- bool nanosleep_clock_monotonic = test_bit(LTL_NANOSLEEP_CLOCK_MONOTONIC, mon->atoms);
- bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon->atoms);
- bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
+ bool nanosleep_clock_realtime = test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
bool epoll_wait = test_bit(LTL_EPOLL_WAIT, mon->atoms);
bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
- bool val42 = task_is_rcu || task_is_migration;
- bool val43 = futex_lock_pi || val42;
- bool val5 = block_on_rt_mutex || val43;
- bool val34 = abort_sleep || kthread_should_stop;
- bool val35 = woken_by_nmi || val34;
- bool val36 = woken_by_hardirq || val35;
- bool val14 = woken_by_equal_or_higher_prio || val36;
- bool val13 = !wake;
- bool val26 = nanosleep_clock_monotonic || nanosleep_clock_tai;
- bool val27 = nanosleep_timer_abstime && val26;
- bool val18 = clock_nanosleep && val27;
+ bool val7 = block_on_rt_mutex || futex_lock_pi;
+ bool val32 = woken_by_nmi || abort_sleep;
+ bool val33 = woken_by_hardirq || val32;
+ bool val14 = woken_by_equal_or_higher_prio || val33;
+ bool val13 = !schedule_in;
+ bool val25 = !nanosleep_clock_realtime;
+ bool val26 = nanosleep_timer_abstime && val25;
+ bool val18 = clock_nanosleep && val26;
bool val20 = val18 || epoll_wait;
- bool val9 = futex_wait || val20;
- bool val11 = val9 || kernel_thread;
+ bool val11 = futex_wait || val20;
+ bool val3 = !user_thread;
bool val2 = !sleep;
+ bool val4 = val2 || val3;
bool val1 = !rt;
- bool val3 = val1 || val2;
+ bool val5 = val1 || val4;
- if (val3)
+ if (val5)
__set_bit(S0, mon->states);
if (val11 && val13)
__set_bit(S1, mon->states);
if (val11 && val14)
__set_bit(S4, mon->states);
- if (val5)
+ if (val7)
__set_bit(S5, mon->states);
}
@@ -132,131 +118,125 @@ ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned l
bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
bool woken_by_equal_or_higher_prio = test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
mon->atoms);
- bool wake = test_bit(LTL_WAKE, mon->atoms);
- bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
- bool task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
+ bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
bool sleep = test_bit(LTL_SLEEP, mon->atoms);
+ bool schedule_in = test_bit(LTL_SCHEDULE_IN, mon->atoms);
bool rt = test_bit(LTL_RT, mon->atoms);
bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME, mon->atoms);
- bool nanosleep_clock_tai = test_bit(LTL_NANOSLEEP_CLOCK_TAI, mon->atoms);
- bool nanosleep_clock_monotonic = test_bit(LTL_NANOSLEEP_CLOCK_MONOTONIC, mon->atoms);
- bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon->atoms);
- bool kernel_thread = test_bit(LTL_KERNEL_THREAD, mon->atoms);
+ bool nanosleep_clock_realtime = test_bit(LTL_NANOSLEEP_CLOCK_REALTIME, mon->atoms);
bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
bool epoll_wait = test_bit(LTL_EPOLL_WAIT, mon->atoms);
bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
- bool val42 = task_is_rcu || task_is_migration;
- bool val43 = futex_lock_pi || val42;
- bool val5 = block_on_rt_mutex || val43;
- bool val34 = abort_sleep || kthread_should_stop;
- bool val35 = woken_by_nmi || val34;
- bool val36 = woken_by_hardirq || val35;
- bool val14 = woken_by_equal_or_higher_prio || val36;
- bool val13 = !wake;
- bool val26 = nanosleep_clock_monotonic || nanosleep_clock_tai;
- bool val27 = nanosleep_timer_abstime && val26;
- bool val18 = clock_nanosleep && val27;
+ bool val7 = block_on_rt_mutex || futex_lock_pi;
+ bool val32 = woken_by_nmi || abort_sleep;
+ bool val33 = woken_by_hardirq || val32;
+ bool val14 = woken_by_equal_or_higher_prio || val33;
+ bool val13 = !schedule_in;
+ bool val25 = !nanosleep_clock_realtime;
+ bool val26 = nanosleep_timer_abstime && val25;
+ bool val18 = clock_nanosleep && val26;
bool val20 = val18 || epoll_wait;
- bool val9 = futex_wait || val20;
- bool val11 = val9 || kernel_thread;
+ bool val11 = futex_wait || val20;
+ bool val3 = !user_thread;
bool val2 = !sleep;
+ bool val4 = val2 || val3;
bool val1 = !rt;
- bool val3 = val1 || val2;
+ bool val5 = val1 || val4;
switch (state) {
case S0:
- if (val3)
+ if (val5)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val5)
+ if (val7)
__set_bit(S5, next);
break;
case S1:
if (val11 && val13)
__set_bit(S1, next);
- if (val13 && val3)
+ if (val13 && val5)
__set_bit(S2, next);
- if (val14 && val3)
+ if (val14 && val5)
__set_bit(S3, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val13 && val5)
+ if (val13 && val7)
__set_bit(S6, next);
- if (val14 && val5)
+ if (val14 && val7)
__set_bit(S7, next);
break;
case S2:
if (val11 && val13)
__set_bit(S1, next);
- if (val13 && val3)
+ if (val13 && val5)
__set_bit(S2, next);
- if (val14 && val3)
+ if (val14 && val5)
__set_bit(S3, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val13 && val5)
+ if (val13 && val7)
__set_bit(S6, next);
- if (val14 && val5)
+ if (val14 && val7)
__set_bit(S7, next);
break;
case S3:
- if (val3)
+ if (val5)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val5)
+ if (val7)
__set_bit(S5, next);
break;
case S4:
- if (val3)
+ if (val5)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val5)
+ if (val7)
__set_bit(S5, next);
break;
case S5:
- if (val3)
+ if (val5)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val5)
+ if (val7)
__set_bit(S5, next);
break;
case S6:
if (val11 && val13)
__set_bit(S1, next);
- if (val13 && val3)
+ if (val13 && val5)
__set_bit(S2, next);
- if (val14 && val3)
+ if (val14 && val5)
__set_bit(S3, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val13 && val5)
+ if (val13 && val7)
__set_bit(S6, next);
- if (val14 && val5)
+ if (val14 && val7)
__set_bit(S7, next);
break;
case S7:
- if (val3)
+ if (val5)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
- if (val5)
+ if (val7)
__set_bit(S5, next);
break;
}
diff --git a/kernel/trace/rv/monitors/sleep/sleep_kunit.c b/kernel/trace/rv/monitors/sleep/sleep_kunit.c
new file mode 100644
index 000000000000..17df5baf1ec2
--- /dev/null
+++ b/kernel/trace/rv/monitors/sleep/sleep_kunit.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/syscalls.h>
+#include <trace/events/sched.h>
+#include <uapi/linux/futex.h>
+#include "sleep_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_SLEEP)
+
+static void rv_test_sleep(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+ unsigned long args[6] = {0};
+ struct pt_regs regs = {0};
+
+ prepare_test(test, &rv_sleep_ops.mon);
+ target->policy = SCHED_FIFO;
+ target->prio = MAX_RT_PRIO - 2;
+ other->policy = SCHED_FIFO;
+ other->prio = MAX_RT_PRIO - 1;
+ rv_sleep_ops.handle_task_newtask(NULL, target, 0);
+
+ /* RT task sleeps on a non RT-friendly nanosleep */
+ rv_mock_current(target);
+ args[0] = CLOCK_REALTIME;
+ syscall_set_arguments(target, &regs, args);
+#ifdef __NR_clock_nanosleep
+ rv_sleep_ops.handle_sys_enter(NULL, &regs, __NR_clock_nanosleep);
+#elif defined(__NR_clock_nanosleep_time64)
+ rv_sleep_ops.handle_sys_enter(NULL, &regs, __NR_clock_nanosleep_time64);
+#endif
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sleep_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_sleep_ops.handle_sys_exit(NULL, NULL, 0);
+
+ /* RT task woken up by lower priority task */
+ args[1] = FUTEX_WAIT;
+ syscall_set_arguments(target, &regs, args);
+ rv_mock_current(target);
+#ifdef __NR_futex
+ rv_sleep_ops.handle_sys_enter(NULL, &regs, __NR_futex);
+#elif defined(__NR_futex_time64)
+ rv_sleep_ops.handle_sys_enter(NULL, &regs, __NR_futex_time64);
+#endif
+ rv_sleep_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_mock_current(other);
+ rv_sleep_ops.handle_sched_waking(NULL, target);
+ rv_mock_current(target);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sleep_ops.handle_sched_exit(NULL, true);
+}
+
+#else
+#define rv_test_sleep rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sleep/sleep_kunit.h b/kernel/trace/rv/monitors/sleep/sleep_kunit.h
new file mode 100644
index 000000000000..3ebf8d2699f2
--- /dev/null
+++ b/kernel/trace/rv/monitors/sleep/sleep_kunit.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __SLEEP_KUNIT_H
+#define __SLEEP_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sleep_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_waking)(void *data, struct task_struct *task);
+ void (*handle_sched_exit)(void *data, bool is_switch);
+ void (*handle_sched_set_state)(void *data, struct task_struct *task, int state);
+ void (*handle_contention_begin)(void *data, void *lock, unsigned int flags);
+ void (*handle_contention_end)(void *data, void *lock, int ret);
+ void (*handle_sys_enter)(void *data, struct pt_regs *regs, long id);
+ void (*handle_sys_exit)(void *data, struct pt_regs *regs, long ret);
+ void (*handle_task_newtask)(void *data, struct task_struct *task, u64 flags);
+} rv_sleep_ops;
+#endif
+
+#endif /* __SLEEP_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/sssw/sssw.c b/kernel/trace/rv/monitors/sssw/sssw.c
index a91321c890cd..fbfde32dc136 100644
--- a/kernel/trace/rv/monitors/sssw/sssw.c
+++ b/kernel/trace/rv/monitors/sssw/sssw.c
@@ -112,3 +112,17 @@ module_exit(unregister_sssw);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sssw: set state sleep and wakeup.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sssw_kunit.h"
+
+const struct rv_sssw_ops rv_sssw_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_set_state = handle_sched_set_state,
+ .handle_sched_switch = handle_sched_switch,
+ .handle_sched_wakeup = handle_sched_wakeup,
+ .handle_signal_deliver = handle_signal_deliver,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sssw_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sssw/sssw_kunit.c b/kernel/trace/rv/monitors/sssw/sssw_kunit.c
new file mode 100644
index 000000000000..a95faf859c60
--- /dev/null
+++ b/kernel/trace/rv/monitors/sssw/sssw_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "sssw_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_SSSW)
+
+static void rv_test_sssw(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_sssw_ops.mon);
+
+ /* Suspend without setting to sleepable */
+ rv_sssw_ops.handle_sched_set_state(NULL, target, TASK_RUNNING);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sssw_ops.handle_sched_switch(NULL, 0, target, other, TASK_INTERRUPTIBLE);
+
+ /* Switch in after suspension without wakeup */
+ rv_sssw_ops.handle_sched_wakeup(NULL, target);
+ rv_sssw_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_sssw_ops.handle_sched_switch(NULL, 0, target, other, TASK_INTERRUPTIBLE);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sssw_ops.handle_sched_switch(NULL, 0, other, target, TASK_RUNNING);
+}
+
+#else
+#define rv_test_sssw rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sssw/sssw_kunit.h b/kernel/trace/rv/monitors/sssw/sssw_kunit.h
new file mode 100644
index 000000000000..6513daa7afba
--- /dev/null
+++ b/kernel/trace/rv/monitors/sssw/sssw_kunit.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __SSSW_KUNIT_H
+#define __SSSW_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sssw_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_set_state)(void *data, struct task_struct *tsk, int state);
+ void (*handle_sched_switch)(void *data, bool preempt,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned int prev_state);
+ void (*handle_sched_wakeup)(void *data, struct task_struct *p);
+ void (*handle_signal_deliver)(void *data, int sig,
+ struct kernel_siginfo *info,
+ struct k_sigaction *ka);
+} rv_sssw_ops;
+#endif
+
+#endif /* __SSSW_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/stall/stall.c b/kernel/trace/rv/monitors/stall/stall.c
index 3c38fb1a0159..b265578f845c 100644
--- a/kernel/trace/rv/monitors/stall/stall.c
+++ b/kernel/trace/rv/monitors/stall/stall.c
@@ -38,7 +38,7 @@ static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
enum states next_state, u64 time_ns)
{
if (curr_state == enqueued_stall)
- return ha_check_invariant_jiffy(ha_mon, clk_stall, time_ns);
+ return ha_check_invariant_jiffy(ha_mon, clk_stall, time_ns, threshold_jiffies);
return true;
}
diff --git a/kernel/trace/rv/monitors/sts/sts.c b/kernel/trace/rv/monitors/sts/sts.c
index ce031cbf202a..2a044cf925b1 100644
--- a/kernel/trace/rv/monitors/sts/sts.c
+++ b/kernel/trace/rv/monitors/sts/sts.c
@@ -152,3 +152,22 @@ module_exit(unregister_sts);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sts: schedule implies task switch.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sts_kunit.h"
+
+const struct rv_sts_ops rv_sts_ops = {
+ .mon = RV_MON_OPS_INIT(),
+#ifdef CONFIG_X86_LOCAL_APIC
+ .handle_vector_irq_entry = handle_vector_irq_entry,
+#endif
+ .handle_irq_disable = handle_irq_disable,
+ .handle_irq_enable = handle_irq_enable,
+ .handle_irq_entry = handle_irq_entry,
+ .handle_sched_switch = handle_sched_switch,
+ .handle_schedule_entry = handle_schedule_entry,
+ .handle_schedule_exit = handle_schedule_exit,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sts_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sts/sts_kunit.c b/kernel/trace/rv/monitors/sts/sts_kunit.c
new file mode 100644
index 000000000000..a07316fff091
--- /dev/null
+++ b/kernel/trace/rv/monitors/sts/sts_kunit.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "sts_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_STS)
+
+static void rv_test_sts(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_sts_ops.mon);
+ /* Per-CPU monitor, make sure we don't change CPU mid-test */
+ guard(migrate)();
+
+ /* Switch without disabling interrupts */
+ rv_sts_ops.handle_schedule_exit(NULL, false);
+ rv_sts_ops.handle_schedule_entry(NULL, false);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sts_ops.handle_sched_switch(NULL, 0, target, other, TASK_RUNNING);
+
+ rv_sts_ops.handle_schedule_exit(NULL, false);
+
+ /* Schedule from interrupt context */
+ rv_sts_ops.handle_schedule_entry(NULL, false);
+ rv_sts_ops.handle_irq_disable(NULL, 0, 0);
+ rv_sts_ops.handle_irq_entry(NULL, 0, NULL);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sts_ops.handle_sched_switch(NULL, 0, target, other, TASK_RUNNING);
+ rv_sts_ops.handle_irq_enable(NULL, 0, 0);
+}
+
+#else
+#define rv_test_sts rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sts/sts_kunit.h b/kernel/trace/rv/monitors/sts/sts_kunit.h
new file mode 100644
index 000000000000..dede4e098c1f
--- /dev/null
+++ b/kernel/trace/rv/monitors/sts/sts_kunit.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __STS_KUNIT_H
+#define __STS_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sts_ops {
+ struct rv_kunit_mon mon;
+#ifdef CONFIG_X86_LOCAL_APIC
+ void (*handle_vector_irq_entry)(void *data, int vector);
+#endif
+ void (*handle_irq_disable)(void *data, unsigned long ip, unsigned long parent_ip);
+ void (*handle_irq_enable)(void *data, unsigned long ip, unsigned long parent_ip);
+ void (*handle_irq_entry)(void *data, int irq, struct irqaction *action);
+ void (*handle_sched_switch)(void *data, bool preempt,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned int prev_state);
+ void (*handle_schedule_entry)(void *data, bool preempt);
+ void (*handle_schedule_exit)(void *data, bool is_switch);
+} rv_sts_ops;
+#endif
+
+#endif /* __STS_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/wakeup/Kconfig b/kernel/trace/rv/monitors/wakeup/Kconfig
new file mode 100644
index 000000000000..98f618f0e01d
--- /dev/null
+++ b/kernel/trace/rv/monitors/wakeup/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_WAKEUP
+ depends on RV
+ depends on RV_MON_RTAPP
+ depends on HAVE_SYSCALL_TRACEPOINTS
+ default y
+ select LTL_MON_EVENTS_ID
+ bool "wakeup monitor"
+ help
+ This monitor detects a lower-priority task waking up a
+ higher-priority task. The RV_MON_SLEEP monitor already
+ detects this case, but this monitor detects in the context
+ of the waker task instead. This and RV_MON_SLEEP can be
+ enabled together to get the stacktrace of both the waker
+ task and the wakee task.
diff --git a/kernel/trace/rv/monitors/wakeup/wakeup.c b/kernel/trace/rv/monitors/wakeup/wakeup.c
new file mode 100644
index 000000000000..01b47416f24e
--- /dev/null
+++ b/kernel/trace/rv/monitors/wakeup/wakeup.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/ftrace.h>
+#include <linux/tracepoint.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rv.h>
+#include <rv/instrumentation.h>
+
+#define MODULE_NAME "wakeup"
+
+#include <trace/events/syscalls.h>
+#include <trace/events/sched.h>
+#include <trace/events/lock.h>
+#include <uapi/linux/futex.h>
+
+#include <rv_trace.h>
+#include <monitors/rtapp/rtapp.h>
+
+
+#ifndef __NR_futex
+#define __NR_futex (-__COUNTER__)
+#endif
+#ifndef __NR_futex_time64
+#define __NR_futex_time64 (-__COUNTER__)
+#endif
+
+#include "wakeup.h"
+#include <rv/ltl_monitor.h>
+
+static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon)
+{
+ /*
+ * This includes "actual" real-time tasks and also PI-boosted
+ * tasks. A task being PI-boosted means it is blocking an "actual"
+ * real-task, therefore it should also obey the monitor's rule,
+ * otherwise the "actual" real-task may be delayed.
+ */
+ ltl_atom_set(mon, LTL_RT, rt_or_dl_task(task));
+}
+
+static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
+{
+ ltl_atom_set(mon, LTL_WOKEN_BY_LOWER_PRIO, false);
+ ltl_atom_set(mon, LTL_WOKEN_BY_SOFTIRQ, false);
+
+ if (task_creation) {
+ ltl_atom_set(mon, LTL_BLOCK_ON_RT_MUTEX, false);
+ ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
+ }
+
+ ltl_atom_set(mon, LTL_USER_THREAD, !(task->flags & PF_KTHREAD));
+}
+
+static void handle_sched_waking(void *data, struct task_struct *task)
+{
+ if (in_task()) {
+ if (current->prio > task->prio)
+ ltl_atom_pulse(task, LTL_WOKEN_BY_LOWER_PRIO, true);
+ } else if (in_serving_softirq()) {
+ ltl_atom_pulse(task, LTL_WOKEN_BY_SOFTIRQ, true);
+ }
+}
+
+static void handle_contention_begin(void *data, void *lock, unsigned int flags)
+{
+ if (flags & LCB_F_RT)
+ ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, true);
+}
+
+static void handle_contention_end(void *data, void *lock, int ret)
+{
+ ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, false);
+}
+
+static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
+{
+ unsigned long args[6];
+ int op, cmd;
+
+ switch (id) {
+ case __NR_futex:
+ case __NR_futex_time64:
+ syscall_get_arguments(current, regs, args);
+ op = args[1];
+ cmd = op & FUTEX_CMD_MASK;
+
+ switch (cmd) {
+ case FUTEX_LOCK_PI:
+ case FUTEX_LOCK_PI2:
+ ltl_atom_update(current, LTL_FUTEX_LOCK_PI, true);
+ break;
+ }
+ break;
+ }
+}
+
+static void handle_sys_exit(void *data, struct pt_regs *regs, long ret)
+{
+ ltl_atom_update(current, LTL_FUTEX_LOCK_PI, false);
+}
+
+static int enable_wakeup(void)
+{
+ int retval;
+
+ retval = ltl_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("rtapp_wakeup", sched_waking, handle_sched_waking);
+ rv_attach_trace_probe("rtapp_wakeup", contention_begin, handle_contention_begin);
+ rv_attach_trace_probe("rtapp_wakeup", contention_end, handle_contention_end);
+ rv_attach_trace_probe("rtapp_wakeup", sys_enter, handle_sys_enter);
+ rv_attach_trace_probe("rtapp_wakeup", sys_exit, handle_sys_exit);
+
+ return 0;
+}
+
+static void disable_wakeup(void)
+{
+ rv_detach_trace_probe("rtapp_wakeup", sched_waking, handle_sched_waking);
+ rv_detach_trace_probe("rtapp_wakeup", contention_begin, handle_contention_begin);
+ rv_detach_trace_probe("rtapp_wakeup", contention_end, handle_contention_end);
+ rv_detach_trace_probe("rtapp_wakeup", sys_enter, handle_sys_enter);
+ rv_detach_trace_probe("rtapp_wakeup", sys_exit, handle_sys_exit);
+
+ ltl_monitor_destroy();
+}
+
+static struct rv_monitor rv_wakeup = {
+ .name = "wakeup",
+ .description = "Monitor that real-time tasks are not woken by lower-priority tasks",
+ .enable = enable_wakeup,
+ .disable = disable_wakeup,
+};
+
+static int __init register_wakeup(void)
+{
+ return rv_register_monitor(&rv_wakeup, &rv_rtapp);
+}
+
+static void __exit unregister_wakeup(void)
+{
+ rv_unregister_monitor(&rv_wakeup);
+}
+
+module_init(register_wakeup);
+module_exit(unregister_wakeup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nam Cao <namcao@linutronix.de>");
+MODULE_DESCRIPTION("Monitor that real-time tasks are not woken by lower-priority tasks");
diff --git a/kernel/trace/rv/monitors/wakeup/wakeup.h b/kernel/trace/rv/monitors/wakeup/wakeup.h
new file mode 100644
index 000000000000..6f80da64e0e1
--- /dev/null
+++ b/kernel/trace/rv/monitors/wakeup/wakeup.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * C implementation of Buchi automaton, automatically generated by
+ * tools/verification/rvgen from the linear temporal logic specification.
+ * For further information, see kernel documentation:
+ * Documentation/trace/rv/linear_temporal_logic.rst
+ */
+
+#include <linux/rv.h>
+
+#define MONITOR_NAME wakeup
+
+enum ltl_atom {
+ LTL_BLOCK_ON_RT_MUTEX,
+ LTL_FUTEX_LOCK_PI,
+ LTL_RT,
+ LTL_USER_THREAD,
+ LTL_WOKEN_BY_LOWER_PRIO,
+ LTL_WOKEN_BY_SOFTIRQ,
+ LTL_NUM_ATOM
+};
+static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);
+
+static const char *ltl_atom_str(enum ltl_atom atom)
+{
+ static const char *const names[] = {
+ "bl_on_rt_mu",
+ "fu_lo_pi",
+ "rt",
+ "us_th",
+ "wo_lo_pr",
+ "wo_so",
+ };
+
+ return names[atom];
+}
+
+enum ltl_buchi_state {
+ S0,
+ RV_NUM_BA_STATES
+};
+static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);
+
+static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
+{
+ bool woken_by_softirq = test_bit(LTL_WOKEN_BY_SOFTIRQ, mon->atoms);
+ bool woken_by_lower_prio = test_bit(LTL_WOKEN_BY_LOWER_PRIO, mon->atoms);
+ bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
+ bool rt = test_bit(LTL_RT, mon->atoms);
+ bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
+ bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
+ bool val9 = block_on_rt_mutex || futex_lock_pi;
+ bool val6 = !woken_by_softirq;
+ bool val5 = !woken_by_lower_prio;
+ bool val8 = val5 && val6;
+ bool val10 = val8 || val9;
+ bool val3 = !user_thread;
+ bool val2 = !rt;
+ bool val4 = val2 || val3;
+ bool val11 = val4 || val10;
+
+ if (val11)
+ __set_bit(S0, mon->states);
+}
+
+static void
+ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)
+{
+ bool woken_by_softirq = test_bit(LTL_WOKEN_BY_SOFTIRQ, mon->atoms);
+ bool woken_by_lower_prio = test_bit(LTL_WOKEN_BY_LOWER_PRIO, mon->atoms);
+ bool user_thread = test_bit(LTL_USER_THREAD, mon->atoms);
+ bool rt = test_bit(LTL_RT, mon->atoms);
+ bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
+ bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
+ bool val9 = block_on_rt_mutex || futex_lock_pi;
+ bool val6 = !woken_by_softirq;
+ bool val5 = !woken_by_lower_prio;
+ bool val8 = val5 && val6;
+ bool val10 = val8 || val9;
+ bool val3 = !user_thread;
+ bool val2 = !rt;
+ bool val4 = val2 || val3;
+ bool val11 = val4 || val10;
+
+ switch (state) {
+ case S0:
+ if (val11)
+ __set_bit(S0, next);
+ break;
+ }
+}
diff --git a/kernel/trace/rv/monitors/wakeup/wakeup_trace.h b/kernel/trace/rv/monitors/wakeup/wakeup_trace.h
new file mode 100644
index 000000000000..7e056183f920
--- /dev/null
+++ b/kernel/trace/rv/monitors/wakeup/wakeup_trace.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_WAKEUP
+DEFINE_EVENT(event_ltl_monitor_id, event_wakeup,
+ TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next),
+ TP_ARGS(task, states, atoms, next));
+DEFINE_EVENT(error_ltl_monitor_id, error_wakeup,
+ TP_PROTO(struct task_struct *task),
+ TP_ARGS(task));
+#endif /* CONFIG_RV_MON_WAKEUP */
diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c
index ee4e68102f17..29f155c6968b 100644
--- a/kernel/trace/rv/rv.c
+++ b/kernel/trace/rv/rv.c
@@ -164,7 +164,6 @@ struct dentry *get_monitors_root(void)
*/
LIST_HEAD(rv_monitors_list);
-static int task_monitor_count;
static bool task_monitor_slots[CONFIG_RV_PER_TASK_MONITORS];
int rv_get_task_monitor_slot(void)
@@ -173,22 +172,16 @@ int rv_get_task_monitor_slot(void)
lockdep_assert_held(&rv_interface_lock);
- if (task_monitor_count == CONFIG_RV_PER_TASK_MONITORS)
- return -EBUSY;
-
- task_monitor_count++;
-
for (i = 0; i < CONFIG_RV_PER_TASK_MONITORS; i++) {
- if (task_monitor_slots[i] == false) {
+ if (!task_monitor_slots[i]) {
task_monitor_slots[i] = true;
return i;
}
}
- WARN_ONCE(1, "RV task_monitor_count and slots are out of sync\n");
-
- return -EINVAL;
+ return -EBUSY;
}
+EXPORT_SYMBOL_GPL(rv_get_task_monitor_slot);
void rv_put_task_monitor_slot(int slot)
{
@@ -199,12 +192,13 @@ void rv_put_task_monitor_slot(int slot)
return;
}
- WARN_ONCE(!task_monitor_slots[slot], "RV releasing unused task_monitor_slots: %d\n",
- slot);
+ if (WARN_ONCE(!task_monitor_slots[slot],
+ "RV releasing unused task monitor slot: %d\n", slot))
+ return;
- task_monitor_count--;
task_monitor_slots[slot] = false;
}
+EXPORT_SYMBOL_GPL(rv_put_task_monitor_slot);
/*
* Monitors with a parent are nested,
@@ -852,3 +846,69 @@ int __init rv_init_interface(void)
return 0;
}
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <rv/kunit.h>
+#include <kunit/visibility.h>
+
+/*
+ * rv_set_testing - ensure mutual exclusion between KUnit tests and real monitors
+ *
+ * KUnit tests for RV monitors rely on stubs that are incompatible with
+ * the execution of real monitors. Ensure mutual exclusion by acquiring
+ * the rv_interface_lock for the duration of the suite.
+ *
+ * Returns 0 on success, -EBUSY if any real monitor is already enabled.
+ */
+int rv_set_testing(struct kunit_suite *suite)
+{
+ struct rv_monitor *mon;
+
+ mutex_lock(&rv_interface_lock);
+
+ list_for_each_entry(mon, &rv_monitors_list, list) {
+ if (mon->enabled) {
+ mutex_unlock(&rv_interface_lock);
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(rv_set_testing);
+
+/*
+ * rv_clear_testing - allow real monitors to run again after KUnit tests
+ */
+void rv_clear_testing(struct kunit_suite *suite)
+{
+ mutex_unlock(&rv_interface_lock);
+}
+EXPORT_SYMBOL_IF_KUNIT(rv_clear_testing);
+
+/*
+ * rv_get_mock_current() is called only if we are running from a KUnit test.
+ * This can occur from a legitimate RV test or any unrelated test running when
+ * a real RV monitor is active and triggering events.
+ * We assume the former case is the only one where mock_current is not NULL and
+ * can occur only sequentially (KUnit doesn't run tests in parallel).
+ * We cannot rely on the test's context because there is no way to safely
+ * understand from which test we are running and KUnit utilities require
+ * locking, which is unsafe from NMI or scheduling context.
+ * Note that it is not possible for a real RV monitor to run when the RV KUnit
+ * tests are running (see rv_set_testing()).
+ */
+static struct task_struct *mock_current;
+
+void rv_mock_current(struct task_struct *tsk)
+{
+ mock_current = tsk;
+}
+EXPORT_SYMBOL_IF_KUNIT(rv_mock_current);
+
+struct task_struct *rv_get_mock_current(void)
+{
+ return mock_current ?: current;
+}
+EXPORT_SYMBOL_GPL(rv_get_mock_current);
+#endif
diff --git a/kernel/trace/rv/rv_monitors_test.c b/kernel/trace/rv/rv_monitors_test.c
new file mode 100644
index 000000000000..3ad11195e664
--- /dev/null
+++ b/kernel/trace/rv/rv_monitors_test.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
+ *
+ * RV monitor kunit tests:
+ * Tests the RV monitors by triggering fake events to verify monitor
+ * behavior and reactions. Tests start from the first defined event and
+ * trigger events in order to verify error detection.
+ */
+#include <rv/kunit.h>
+#include <kunit/test-bug.h>
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include "rv.h"
+
+/*
+ * An easy way to pass the context is to use kunit_get_current_test()->priv,
+ * but this doesn't always work (e.g. a reactor running from another context
+ * like softirq). Store the current value here whenever a test is running.
+ */
+static struct rv_kunit_ctx *active_ctx;
+
+__printf(1, 0)
+static void rv_kunit_mock_react(const char *msg, va_list args)
+{
+ if (active_ctx)
+ ++active_ctx->reactions;
+}
+
+/*
+ * teardown_test - Disable the monitor for a kunit test
+ *
+ * Since per-task monitors are special, make sure we reset all the ones we
+ * started manually here, if required.
+ */
+void teardown_test(void *arg)
+{
+ const struct rv_kunit_mon *mon = arg;
+ struct kunit *test = kunit_get_current_test();
+
+ if (test) {
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ RV_KUNIT_EXPECT_NO_REACTION(test, ctx);
+
+ if (mon->is_per_task && mon->task_reset) {
+ for (int i = 0; i < ctx->mock_task_count; i++)
+ mon->task_reset(ctx->mock_tasks[i]);
+ synchronize_rcu();
+ }
+ }
+
+ mon->rv_this->enabled = 0;
+
+ if (mon->rv_this->reactor)
+ mon->rv_this->react = mon->rv_this->reactor->react;
+ else
+ mon->rv_this->react = NULL;
+ active_ctx = NULL;
+ rv_mock_current(NULL);
+
+ if (mon->is_per_task)
+ *mon->task_slot = RV_PER_TASK_MONITOR_INIT;
+ else
+ mon->monitor_destroy();
+}
+
+/*
+ * prepare_test - Enable the monitor for a kunit test
+ *
+ * Do the bare minimum to set up the monitor, per-task monitors are special as
+ * "real" initialisation/destruction iterates over real tasks, and may register
+ * handlers. All we need is to select the right slot in the task_struct.
+ */
+void prepare_test(struct kunit *test, const struct rv_kunit_mon *mon)
+{
+ KUNIT_ASSERT_FALSE(test, mon->rv_this->enabled);
+
+ active_ctx = test->priv;
+ mon->rv_this->react = rv_kunit_mock_react;
+
+ if (mon->is_per_task)
+ *mon->task_slot = 0;
+ else
+ KUNIT_ASSERT_EQ(test, mon->monitor_init(), 0);
+
+ mon->rv_this->enabled = 1;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test, teardown_test, (void *)mon));
+}
+
+struct task_struct *rv_kunit_alloc_mock_task(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ struct task_struct *tsk;
+
+ KUNIT_ASSERT_LT(test, ctx->mock_task_count, RV_KUNIT_MAX_MOCK_TASKS);
+
+ tsk = kunit_kzalloc(test, sizeof(struct task_struct), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, tsk);
+
+ if (!IS_ENABLED(CONFIG_THREAD_INFO_IN_TASK)) {
+ tsk->stack = kunit_kzalloc(test, sizeof(struct thread_info), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, tsk->stack);
+ }
+
+ ctx->mock_tasks[ctx->mock_task_count++] = tsk;
+ return tsk;
+}
+
+static int rv_mon_test_init(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ test->priv = ctx;
+
+ return 0;
+}
+
+static void __maybe_unused rv_test_stub(struct kunit *test)
+{
+ kunit_skip(test, "Monitor not enabled\n");
+}
+
+/*
+ * rv_test_dummy - test reactions work as expected
+ */
+static void rv_test_dummy(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ static struct rv_monitor dummy_monitor = {
+ .name = "dummy",
+ .react = rv_kunit_mock_react,
+ };
+
+ active_ctx = ctx;
+
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_react(&dummy_monitor, "dummy");
+ RV_KUNIT_EXPECT_NO_REACTION(test, ctx);
+
+ active_ctx = NULL;
+}
+
+#include "monitors/sco/sco_kunit.c"
+#include "monitors/sssw/sssw_kunit.c"
+#include "monitors/sts/sts_kunit.c"
+#include "monitors/opid/opid_kunit.c"
+#include "monitors/nomiss/nomiss_kunit.c"
+#include "monitors/pagefault/pagefault_kunit.c"
+#include "monitors/sleep/sleep_kunit.c"
+
+static struct kunit_case rv_mon_test_cases[] = {
+ KUNIT_CASE(rv_test_dummy),
+ KUNIT_CASE(rv_test_sco),
+ KUNIT_CASE(rv_test_sssw),
+ KUNIT_CASE(rv_test_sts),
+ KUNIT_CASE(rv_test_opid),
+ KUNIT_CASE(rv_test_nomiss),
+ KUNIT_CASE(rv_test_pagefault),
+ KUNIT_CASE(rv_test_sleep),
+ {}
+};
+
+static struct kunit_suite rv_mon_test_suite = {
+ .name = "rv_mon",
+ .suite_init = rv_set_testing,
+ .suite_exit = rv_clear_testing,
+ .init = rv_mon_test_init,
+ .test_cases = rv_mon_test_cases,
+};
+
+kunit_test_suites(&rv_mon_test_suite);
+
+MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
+MODULE_DESCRIPTION("RV monitor kunit tests: test monitors by triggering reactions");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/kernel/trace/rv/rv_reactors.c b/kernel/trace/rv/rv_reactors.c
index 460af07f7aba..2f5fc8d18dea 100644
--- a/kernel/trace/rv/rv_reactors.c
+++ b/kernel/trace/rv/rv_reactors.c
@@ -479,3 +479,4 @@ void rv_react(struct rv_monitor *monitor, const char *msg, ...)
va_end(args);
}
+EXPORT_SYMBOL_GPL(rv_react);
diff --git a/kernel/trace/rv/rv_trace.h b/kernel/trace/rv/rv_trace.h
index 9622c269789c..2f8a932432c9 100644
--- a/kernel/trace/rv/rv_trace.h
+++ b/kernel/trace/rv/rv_trace.h
@@ -241,6 +241,7 @@ DECLARE_EVENT_CLASS(error_ltl_monitor_id,
);
#include <monitors/pagefault/pagefault_trace.h>
#include <monitors/sleep/sleep_trace.h>
+#include <monitors/wakeup/wakeup_trace.h>
// Add new monitors based on CONFIG_LTL_MON_EVENTS_ID here
#endif /* CONFIG_LTL_MON_EVENTS_ID */