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:
committed by
Steven Rostedt (Google)
parent
d0096c2f9c
commit
e8440a88e5
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.");
|
||||
@@ -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 },
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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.");
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user