rv: Add nrp and sssw per-task monitors

Add 2 per-task monitors as part of the sched model:

* nrp: need-resched preempts
    Monitor to ensure preemption requires need resched.
* sssw: set state sleep and wakeup
    Monitor to ensure sched_set_state to sleepable leads to sleeping and
    sleeping tasks require wakeup.

Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Tomas Glozar <tglozar@redhat.com>
Cc: Juri Lelli <jlelli@redhat.com>
Cc: Clark Williams <williams@redhat.com>
Cc: John Kacur <jkacur@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/20250728135022.255578-9-gmonaco@redhat.com
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
Acked-by: Nam Cao <namcao@linutronix.de>
Tested-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
This commit is contained in:
Gabriele Monaco
2025-07-28 15:50:20 +02:00
committed by Steven Rostedt (Google)
parent d0096c2f9c
commit e8440a88e5
15 changed files with 728 additions and 0 deletions
+2
View File
@@ -55,6 +55,8 @@ source "kernel/trace/rv/monitors/snroc/Kconfig"
source "kernel/trace/rv/monitors/scpd/Kconfig"
source "kernel/trace/rv/monitors/snep/Kconfig"
source "kernel/trace/rv/monitors/sts/Kconfig"
source "kernel/trace/rv/monitors/nrp/Kconfig"
source "kernel/trace/rv/monitors/sssw/Kconfig"
# Add new sched monitors here
source "kernel/trace/rv/monitors/rtapp/Kconfig"
+2
View File
@@ -14,6 +14,8 @@ obj-$(CONFIG_RV_MON_RTAPP) += monitors/rtapp/rtapp.o
obj-$(CONFIG_RV_MON_PAGEFAULT) += monitors/pagefault/pagefault.o
obj-$(CONFIG_RV_MON_SLEEP) += monitors/sleep/sleep.o
obj-$(CONFIG_RV_MON_STS) += monitors/sts/sts.o
obj-$(CONFIG_RV_MON_NRP) += monitors/nrp/nrp.o
obj-$(CONFIG_RV_MON_SSSW) += monitors/sssw/sssw.o
# Add new monitors here
obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
+16
View File
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_NRP
depends on RV
depends on RV_MON_SCHED
default y if !ARM64
select DA_MON_EVENTS_ID
bool "nrp monitor"
help
Monitor to ensure preemption requires need resched.
This monitor is part of the sched monitors collection.
This monitor is unstable on arm64, say N unless you are testing it.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
+138
View File
@@ -0,0 +1,138 @@
// 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>
#include <rv/da_monitor.h>
#define MODULE_NAME "nrp"
#include <trace/events/irq.h>
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#include "nrp.h"
static struct rv_monitor rv_nrp;
DECLARE_DA_MON_PER_TASK(nrp, unsigned char);
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
da_handle_event_nrp(current, irq_entry_nrp);
}
static void attach_vector_irq(void)
{
rv_attach_trace_probe("nrp", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_attach_trace_probe("nrp", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_attach_trace_probe("nrp", reschedule_entry, handle_vector_irq_entry);
rv_attach_trace_probe("nrp", call_function_entry, handle_vector_irq_entry);
rv_attach_trace_probe("nrp", call_function_single_entry, handle_vector_irq_entry);
}
}
static void detach_vector_irq(void)
{
rv_detach_trace_probe("nrp", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_detach_trace_probe("nrp", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_detach_trace_probe("nrp", reschedule_entry, handle_vector_irq_entry);
rv_detach_trace_probe("nrp", call_function_entry, handle_vector_irq_entry);
rv_detach_trace_probe("nrp", call_function_single_entry, handle_vector_irq_entry);
}
}
#else
/* We assume irq_entry tracepoints are sufficient on other architectures */
static void attach_vector_irq(void) { }
static void detach_vector_irq(void) { }
#endif
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
da_handle_event_nrp(current, irq_entry_nrp);
}
static void handle_sched_need_resched(void *data, struct task_struct *tsk,
int cpu, int tif)
{
/*
* Although need_resched leads to both the rescheduling and preempt_irq
* states, it is safer to start the monitor always in preempt_irq,
* which may not mirror the system state but makes the monitor simpler,
*/
if (tif == TIF_NEED_RESCHED)
da_handle_start_event_nrp(tsk, sched_need_resched_nrp);
}
static void handle_schedule_entry(void *data, bool preempt)
{
if (preempt)
da_handle_event_nrp(current, schedule_entry_preempt_nrp);
else
da_handle_event_nrp(current, schedule_entry_nrp);
}
static int enable_nrp(void)
{
int retval;
retval = da_monitor_init_nrp();
if (retval)
return retval;
rv_attach_trace_probe("nrp", irq_handler_entry, handle_irq_entry);
rv_attach_trace_probe("nrp", sched_set_need_resched_tp, handle_sched_need_resched);
rv_attach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry);
attach_vector_irq();
return 0;
}
static void disable_nrp(void)
{
rv_nrp.enabled = 0;
rv_detach_trace_probe("nrp", irq_handler_entry, handle_irq_entry);
rv_detach_trace_probe("nrp", sched_set_need_resched_tp, handle_sched_need_resched);
rv_detach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry);
detach_vector_irq();
da_monitor_destroy_nrp();
}
static struct rv_monitor rv_nrp = {
.name = "nrp",
.description = "need resched preempts.",
.enable = enable_nrp,
.disable = disable_nrp,
.reset = da_monitor_reset_all_nrp,
.enabled = 0,
};
static int __init register_nrp(void)
{
return rv_register_monitor(&rv_nrp, &rv_sched);
}
static void __exit unregister_nrp(void)
{
rv_unregister_monitor(&rv_nrp);
}
module_init(register_nrp);
module_exit(unregister_nrp);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("nrp: need resched preempts.");
+75
View File
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of nrp automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
enum states_nrp {
preempt_irq_nrp = 0,
any_thread_running_nrp,
nested_preempt_nrp,
rescheduling_nrp,
state_max_nrp
};
#define INVALID_STATE state_max_nrp
enum events_nrp {
irq_entry_nrp = 0,
sched_need_resched_nrp,
schedule_entry_nrp,
schedule_entry_preempt_nrp,
event_max_nrp
};
struct automaton_nrp {
char *state_names[state_max_nrp];
char *event_names[event_max_nrp];
unsigned char function[state_max_nrp][event_max_nrp];
unsigned char initial_state;
bool final_states[state_max_nrp];
};
static const struct automaton_nrp automaton_nrp = {
.state_names = {
"preempt_irq",
"any_thread_running",
"nested_preempt",
"rescheduling"
},
.event_names = {
"irq_entry",
"sched_need_resched",
"schedule_entry",
"schedule_entry_preempt"
},
.function = {
{
preempt_irq_nrp,
preempt_irq_nrp,
nested_preempt_nrp,
nested_preempt_nrp
},
{
any_thread_running_nrp,
rescheduling_nrp,
any_thread_running_nrp,
INVALID_STATE
},
{
nested_preempt_nrp,
preempt_irq_nrp,
any_thread_running_nrp,
any_thread_running_nrp
},
{
preempt_irq_nrp,
rescheduling_nrp,
any_thread_running_nrp,
any_thread_running_nrp
},
},
.initial_state = preempt_irq_nrp,
.final_states = { 0, 1, 0, 0 },
};
+15
View File
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_NRP
DEFINE_EVENT(event_da_monitor_id, event_nrp,
TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
TP_ARGS(id, state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor_id, error_nrp,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
#endif /* CONFIG_RV_MON_NRP */
+1
View File
@@ -2,6 +2,7 @@
#
config RV_MON_SCHED
depends on RV
depends on RV_PER_TASK_MONITORS >= 3
bool "sched monitor"
help
Collection of monitors to check the scheduler behaves according to specifications.
+15
View File
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SSSW
depends on RV
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_ID
bool "sssw monitor"
help
Monitor to ensure sched_set_state to sleepable leads to sleeping and
sleeping tasks require wakeup.
This monitor is part of the sched monitors collection.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
+116
View File
@@ -0,0 +1,116 @@
// 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>
#include <rv/da_monitor.h>
#define MODULE_NAME "sssw"
#include <trace/events/sched.h>
#include <trace/events/signal.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#include "sssw.h"
static struct rv_monitor rv_sssw;
DECLARE_DA_MON_PER_TASK(sssw, unsigned char);
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
if (state == TASK_RUNNING)
da_handle_start_event_sssw(tsk, sched_set_state_runnable_sssw);
else
da_handle_event_sssw(tsk, sched_set_state_sleepable_sssw);
}
static void handle_sched_switch(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
{
if (preempt)
da_handle_event_sssw(prev, sched_switch_preempt_sssw);
else if (prev_state == TASK_RUNNING)
da_handle_event_sssw(prev, sched_switch_yield_sssw);
else if (prev_state == TASK_RTLOCK_WAIT)
/* special case of sleeping task with racy conditions */
da_handle_event_sssw(prev, sched_switch_blocking_sssw);
else
da_handle_event_sssw(prev, sched_switch_suspend_sssw);
da_handle_event_sssw(next, sched_switch_in_sssw);
}
static void handle_sched_wakeup(void *data, struct task_struct *p)
{
/*
* Wakeup can also lead to signal_wakeup although the system is
* actually runnable. The monitor can safely start with this event.
*/
da_handle_start_event_sssw(p, sched_wakeup_sssw);
}
static void handle_signal_deliver(void *data, int sig,
struct kernel_siginfo *info,
struct k_sigaction *ka)
{
da_handle_event_sssw(current, signal_deliver_sssw);
}
static int enable_sssw(void)
{
int retval;
retval = da_monitor_init_sssw();
if (retval)
return retval;
rv_attach_trace_probe("sssw", sched_set_state_tp, handle_sched_set_state);
rv_attach_trace_probe("sssw", sched_switch, handle_sched_switch);
rv_attach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup);
rv_attach_trace_probe("sssw", signal_deliver, handle_signal_deliver);
return 0;
}
static void disable_sssw(void)
{
rv_sssw.enabled = 0;
rv_detach_trace_probe("sssw", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("sssw", sched_switch, handle_sched_switch);
rv_detach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup);
rv_detach_trace_probe("sssw", signal_deliver, handle_signal_deliver);
da_monitor_destroy_sssw();
}
static struct rv_monitor rv_sssw = {
.name = "sssw",
.description = "set state sleep and wakeup.",
.enable = enable_sssw,
.disable = disable_sssw,
.reset = da_monitor_reset_all_sssw,
.enabled = 0,
};
static int __init register_sssw(void)
{
return rv_register_monitor(&rv_sssw, &rv_sched);
}
static void __exit unregister_sssw(void)
{
rv_unregister_monitor(&rv_sssw);
}
module_init(register_sssw);
module_exit(unregister_sssw);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sssw: set state sleep and wakeup.");
+105
View File
@@ -0,0 +1,105 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of sssw automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
enum states_sssw {
runnable_sssw = 0,
signal_wakeup_sssw,
sleepable_sssw,
sleeping_sssw,
state_max_sssw
};
#define INVALID_STATE state_max_sssw
enum events_sssw {
sched_set_state_runnable_sssw = 0,
sched_set_state_sleepable_sssw,
sched_switch_blocking_sssw,
sched_switch_in_sssw,
sched_switch_preempt_sssw,
sched_switch_suspend_sssw,
sched_switch_yield_sssw,
sched_wakeup_sssw,
signal_deliver_sssw,
event_max_sssw
};
struct automaton_sssw {
char *state_names[state_max_sssw];
char *event_names[event_max_sssw];
unsigned char function[state_max_sssw][event_max_sssw];
unsigned char initial_state;
bool final_states[state_max_sssw];
};
static const struct automaton_sssw automaton_sssw = {
.state_names = {
"runnable",
"signal_wakeup",
"sleepable",
"sleeping"
},
.event_names = {
"sched_set_state_runnable",
"sched_set_state_sleepable",
"sched_switch_blocking",
"sched_switch_in",
"sched_switch_preempt",
"sched_switch_suspend",
"sched_switch_yield",
"sched_wakeup",
"signal_deliver"
},
.function = {
{
runnable_sssw,
sleepable_sssw,
sleeping_sssw,
runnable_sssw,
runnable_sssw,
INVALID_STATE,
runnable_sssw,
runnable_sssw,
runnable_sssw
},
{
INVALID_STATE,
sleepable_sssw,
INVALID_STATE,
signal_wakeup_sssw,
signal_wakeup_sssw,
INVALID_STATE,
signal_wakeup_sssw,
signal_wakeup_sssw,
runnable_sssw
},
{
runnable_sssw,
sleepable_sssw,
sleeping_sssw,
sleepable_sssw,
sleepable_sssw,
sleeping_sssw,
signal_wakeup_sssw,
runnable_sssw,
sleepable_sssw
},
{
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
runnable_sssw,
INVALID_STATE
},
},
.initial_state = runnable_sssw,
.final_states = { 1, 0, 0, 0 },
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SSSW
DEFINE_EVENT(event_da_monitor_id, event_sssw,
TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
TP_ARGS(id, state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor_id, error_sssw,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
#endif /* CONFIG_RV_MON_SSSW */
+2
View File
@@ -123,6 +123,8 @@ DECLARE_EVENT_CLASS(error_da_monitor_id,
#include <monitors/wwnr/wwnr_trace.h>
#include <monitors/snroc/snroc_trace.h>
#include <monitors/nrp/nrp_trace.h>
#include <monitors/sssw/sssw_trace.h>
// Add new monitors based on CONFIG_DA_MON_EVENTS_ID here
#endif /* CONFIG_DA_MON_EVENTS_ID */