Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_EVENTS
bool
config RV_MON_MAINTENANCE_EVENTS
bool
config DA_MON_EVENTS_IMPLICIT
select RV_MON_EVENTS
select RV_MON_MAINTENANCE_EVENTS
bool
config DA_MON_EVENTS_ID
select RV_MON_EVENTS
select RV_MON_MAINTENANCE_EVENTS
bool
config LTL_MON_EVENTS_ID
select RV_MON_EVENTS
bool
config RV_LTL_MONITOR
bool
config RV_HA_MONITOR
bool
config HA_MON_EVENTS_IMPLICIT
select DA_MON_EVENTS_IMPLICIT
select RV_HA_MONITOR
bool
config HA_MON_EVENTS_ID
select DA_MON_EVENTS_ID
select RV_HA_MONITOR
bool
menuconfig RV
bool "Runtime Verification"
select TRACING
help
Enable the kernel runtime verification infrastructure. RV is a
lightweight (yet rigorous) method that complements classical
exhaustive verification techniques (such as model checking and
theorem proving). RV works by analyzing the trace of the system's
actual execution, comparing it against a formal specification of
the system behavior.
For further information, see:
Documentation/trace/rv/runtime-verification.rst
config RV_PER_TASK_MONITORS
int "Maximum number of per-task monitor"
depends on RV
range 1 8
default 2
help
This option configures the maximum number of per-task RV monitors that can run
simultaneously.
source "kernel/trace/rv/monitors/wip/Kconfig"
source "kernel/trace/rv/monitors/wwnr/Kconfig"
source "kernel/trace/rv/monitors/sched/Kconfig"
source "kernel/trace/rv/monitors/sco/Kconfig"
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"
source "kernel/trace/rv/monitors/opid/Kconfig"
# Add new sched monitors here
source "kernel/trace/rv/monitors/rtapp/Kconfig"
source "kernel/trace/rv/monitors/pagefault/Kconfig"
source "kernel/trace/rv/monitors/sleep/Kconfig"
# Add new rtapp monitors here
source "kernel/trace/rv/monitors/stall/Kconfig"
source "kernel/trace/rv/monitors/deadline/Kconfig"
source "kernel/trace/rv/monitors/nomiss/Kconfig"
# Add new deadline monitors here
# Add new monitors here
config RV_REACTORS
bool "Runtime verification reactors"
default y
depends on RV
help
Enables the online runtime verification reactors. A runtime
monitor can cause a reaction to the detection of an exception
on the model's execution. By default, the monitors have
tracing reactions, printing the monitor output via tracepoints,
but other reactions can be added (on-demand) via this interface.
config RV_REACT_PRINTK
bool "Printk reactor"
depends on RV_REACTORS
default y
help
Enables the printk reactor. The printk reactor emits a printk()
message if an exception is found.
config RV_REACT_PANIC
bool "Panic reactor"
depends on RV_REACTORS
default y
help
Enables the panic reactor. The panic reactor emits a printk()
message if an exception is found and panic()s the system.
+26
View File
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: GPL-2.0
ccflags-y += -I $(src) # needed for trace events
obj-$(CONFIG_RV) += rv.o
obj-$(CONFIG_RV_MON_WIP) += monitors/wip/wip.o
obj-$(CONFIG_RV_MON_WWNR) += monitors/wwnr/wwnr.o
obj-$(CONFIG_RV_MON_SCHED) += monitors/sched/sched.o
obj-$(CONFIG_RV_MON_SCO) += monitors/sco/sco.o
obj-$(CONFIG_RV_MON_SNROC) += monitors/snroc/snroc.o
obj-$(CONFIG_RV_MON_SCPD) += monitors/scpd/scpd.o
obj-$(CONFIG_RV_MON_SNEP) += monitors/snep/snep.o
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
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
# 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
@@ -0,0 +1,10 @@
config RV_MON_DEADLINE
depends on RV
bool "deadline monitor"
help
Collection of monitors to check the deadline scheduler and server
behave according to specifications. Enable this to enable all
scheduler specification supported by the current kernel.
For further information, see:
Documentation/trace/rv/monitor_deadline.rst
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <linux/kallsyms.h>
#define MODULE_NAME "deadline"
#include "deadline.h"
struct rv_monitor rv_deadline = {
.name = "deadline",
.description = "container for several deadline scheduler specifications.",
.enable = NULL,
.disable = NULL,
.reset = NULL,
.enabled = 0,
};
/* Used by other monitors */
struct sched_class *rv_ext_sched_class;
static int __init register_deadline(void)
{
if (IS_ENABLED(CONFIG_SCHED_CLASS_EXT)) {
rv_ext_sched_class = (void *)kallsyms_lookup_name("ext_sched_class");
if (!rv_ext_sched_class)
pr_warn("rv: Missing ext_sched_class, monitors may not work.\n");
}
return rv_register_monitor(&rv_deadline, NULL);
}
static void __exit unregister_deadline(void)
{
rv_unregister_monitor(&rv_deadline);
}
module_init(register_deadline);
module_exit(unregister_deadline);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("deadline: container for several deadline scheduler specifications.");
@@ -0,0 +1,203 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/sched/deadline.h>
#include <asm/syscall.h>
#include <uapi/linux/sched/types.h>
#include <trace/events/sched.h>
/*
* Dummy values if not available
*/
#ifndef __NR_sched_setscheduler
#define __NR_sched_setscheduler -__COUNTER__
#endif
#ifndef __NR_sched_setattr
#define __NR_sched_setattr -__COUNTER__
#endif
extern struct rv_monitor rv_deadline;
/* Initialised when registering the deadline container */
extern struct sched_class *rv_ext_sched_class;
/*
* If both have dummy values, the syscalls are not supported and we don't even
* need to register the handler.
*/
static inline bool should_skip_syscall_handle(void)
{
return __NR_sched_setattr < 0 && __NR_sched_setscheduler < 0;
}
/*
* is_supported_type - return true if @type is supported by the deadline monitors
*/
static inline bool is_supported_type(u8 type)
{
return type == DL_TASK || type == DL_SERVER_FAIR || type == DL_SERVER_EXT;
}
/*
* is_server_type - return true if @type is a supported server
*/
static inline bool is_server_type(u8 type)
{
return is_supported_type(type) && type != DL_TASK;
}
/*
* Use negative numbers for the server.
* Currently only one fair server per CPU, may change in the future.
*/
#define fair_server_id(cpu) (-cpu)
#define ext_server_id(cpu) (-cpu - num_possible_cpus())
#define NO_SERVER_ID (-2 * num_possible_cpus())
/*
* Get a unique id used for dl entities
*
* The cpu is not required for tasks as the pid is used there, if this function
* is called on a dl_se that for sure corresponds to a task, DL_TASK can be
* used in place of cpu.
* We need the cpu for servers as it is provided in the tracepoint and we
* cannot easily retrieve it from the dl_se (requires the struct rq definition).
*/
static inline int get_entity_id(struct sched_dl_entity *dl_se, int cpu, u8 type)
{
if (dl_server(dl_se) && type != DL_TASK) {
if (type == DL_SERVER_FAIR)
return fair_server_id(cpu);
if (type == DL_SERVER_EXT)
return ext_server_id(cpu);
return NO_SERVER_ID;
}
return dl_task_of(dl_se)->pid;
}
static inline bool task_is_scx_enabled(struct task_struct *tsk)
{
return IS_ENABLED(CONFIG_SCHED_CLASS_EXT) &&
tsk->sched_class == rv_ext_sched_class;
}
/* Expand id and target as arguments for da functions */
#define EXPAND_ID(dl_se, cpu, type) get_entity_id(dl_se, cpu, type), dl_se
#define EXPAND_ID_TASK(tsk) get_entity_id(&tsk->dl, task_cpu(tsk), DL_TASK), &tsk->dl
static inline u8 get_server_type(struct task_struct *tsk)
{
if (tsk->policy == SCHED_NORMAL || tsk->policy == SCHED_EXT ||
tsk->policy == SCHED_BATCH || tsk->policy == SCHED_IDLE)
return task_is_scx_enabled(tsk) ? DL_SERVER_EXT : DL_SERVER_FAIR;
return DL_OTHER;
}
static inline int extract_params(struct pt_regs *regs, long id, pid_t *pid_out)
{
size_t size = offsetofend(struct sched_attr, sched_flags);
struct sched_attr __user *uattr;
struct sched_attr attr;
int new_policy = -1, ret;
unsigned long args[6];
switch (id) {
case __NR_sched_setscheduler:
syscall_get_arguments(current, regs, args);
*pid_out = args[0];
new_policy = args[1];
break;
case __NR_sched_setattr:
syscall_get_arguments(current, regs, args);
*pid_out = args[0];
uattr = (struct sched_attr __user *)args[1];
/*
* Just copy up to sched_flags, we are not interested after that
*/
ret = copy_struct_from_user(&attr, size, uattr, size);
if (ret)
return ret;
if (attr.sched_flags & SCHED_FLAG_KEEP_POLICY)
return -EINVAL;
new_policy = attr.sched_policy;
break;
default:
return -EINVAL;
}
return new_policy & ~SCHED_RESET_ON_FORK;
}
/* Helper functions requiring DA/HA utilities */
#ifdef RV_MON_TYPE
/*
* get_fair_server - get the fair server associated to a task
*
* If the task is a boosted task, the server is available in the task_struct,
* otherwise grab the dl entity saved for the CPU where the task is enqueued.
* This function assumes the task is enqueued somewhere.
*/
static inline struct sched_dl_entity *get_server(struct task_struct *tsk, u8 type)
{
if (tsk->dl_server && get_server_type(tsk) == type)
return tsk->dl_server;
if (type == DL_SERVER_FAIR)
return da_get_target_by_id(fair_server_id(task_cpu(tsk)));
if (type == DL_SERVER_EXT)
return da_get_target_by_id(ext_server_id(task_cpu(tsk)));
return NULL;
}
/*
* Initialise monitors for all tasks and pre-allocate the storage for servers.
* This is necessary since we don't have access to the servers here and
* allocation can cause deadlocks from their tracepoints. We can only fill
* pre-initialised storage from there.
*/
static inline int init_storage(bool skip_tasks)
{
struct task_struct *g, *p;
int cpu;
for_each_possible_cpu(cpu) {
if (!da_create_empty_storage(fair_server_id(cpu)))
goto fail;
if (IS_ENABLED(CONFIG_SCHED_CLASS_EXT) &&
!da_create_empty_storage(ext_server_id(cpu)))
goto fail;
}
if (skip_tasks)
return 0;
read_lock(&tasklist_lock);
for_each_process_thread(g, p) {
if (p->policy == SCHED_DEADLINE) {
if (!da_create_storage(EXPAND_ID_TASK(p), NULL)) {
read_unlock(&tasklist_lock);
goto fail;
}
}
}
read_unlock(&tasklist_lock);
return 0;
fail:
da_monitor_destroy();
return -ENOMEM;
}
static void __maybe_unused handle_newtask(void *data, struct task_struct *task, u64 flags)
{
/* Might be superfluous as tasks are not started with this policy.. */
if (task->policy == SCHED_DEADLINE)
da_create_storage(EXPAND_ID_TASK(task), NULL);
}
static void __maybe_unused handle_exit(void *data, struct task_struct *p, bool group_dead)
{
if (p->policy == SCHED_DEADLINE)
da_destroy_storage(get_entity_id(&p->dl, DL_TASK, DL_TASK));
}
#endif
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_NOMISS
depends on RV
depends on HAVE_SYSCALL_TRACEPOINTS
depends on RV_MON_DEADLINE
default y
select HA_MON_EVENTS_ID
bool "nomiss monitor"
help
Monitor to ensure dl entities run to completion before their deadiline.
This monitor is part of the deadline monitors collection.
For further information, see:
Documentation/trace/rv/monitor_deadline.rst
@@ -0,0 +1,293 @@
// 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 "nomiss"
#include <uapi/linux/sched/types.h>
#include <trace/events/syscalls.h>
#include <trace/events/sched.h>
#include <trace/events/task.h>
#include <rv_trace.h>
#define RV_MON_TYPE RV_MON_PER_OBJ
#define HA_TIMER_TYPE HA_TIMER_WHEEL
/* The start condition is on sched_switch, it's dangerous to allocate there */
#define DA_SKIP_AUTO_ALLOC
typedef struct sched_dl_entity *monitor_target;
#include "nomiss.h"
#include <rv/ha_monitor.h>
#include <monitors/deadline/deadline.h>
/*
* User configurable deadline threshold. If the total utilisation of deadline
* tasks is larger than 1, they are only guaranteed bounded tardiness. See
* Documentation/scheduler/sched-deadline.rst for more details.
* The minimum tardiness without sched_feat(HRTICK_DL) is 1 tick to accommodate
* for throttle enforced on the next tick.
*/
static u64 deadline_thresh = TICK_NSEC;
module_param(deadline_thresh, ullong, 0644);
#define DEADLINE_NS(ha_mon) (ha_get_target(ha_mon)->dl_deadline + deadline_thresh)
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_nomiss env, u64 time_ns)
{
if (env == clk_nomiss)
return ha_get_clk_ns(ha_mon, env, time_ns);
else if (env == is_constr_dl_nomiss)
return !dl_is_implicit(ha_get_target(ha_mon));
else if (env == is_defer_nomiss)
return ha_get_target(ha_mon)->dl_defer;
return ENV_INVALID_VALUE;
}
static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_nomiss env, u64 time_ns)
{
if (env == clk_nomiss)
ha_reset_clk_ns(ha_mon, env, time_ns);
}
static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
if (curr_state == ready_nomiss)
return ha_check_invariant_ns(ha_mon, clk_nomiss, time_ns);
else if (curr_state == running_nomiss)
return ha_check_invariant_ns(ha_mon, clk_nomiss, time_ns);
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)
{
bool res = true;
if (curr_state == ready_nomiss && event == dl_replenish_nomiss)
ha_reset_env(ha_mon, clk_nomiss, time_ns);
else if (curr_state == ready_nomiss && event == dl_throttle_nomiss)
res = ha_get_env(ha_mon, is_defer_nomiss, time_ns) == 1ull;
else if (curr_state == idle_nomiss && event == dl_replenish_nomiss)
ha_reset_env(ha_mon, clk_nomiss, time_ns);
else if (curr_state == running_nomiss && event == dl_replenish_nomiss)
ha_reset_env(ha_mon, clk_nomiss, time_ns);
else if (curr_state == sleeping_nomiss && event == dl_replenish_nomiss)
ha_reset_env(ha_mon, clk_nomiss, time_ns);
else if (curr_state == sleeping_nomiss && event == dl_throttle_nomiss)
res = ha_get_env(ha_mon, is_constr_dl_nomiss, time_ns) == 1ull ||
ha_get_env(ha_mon, is_defer_nomiss, time_ns) == 1ull;
else if (curr_state == throttled_nomiss && event == dl_replenish_nomiss)
ha_reset_env(ha_mon, clk_nomiss, time_ns);
return res;
}
static inline void ha_setup_invariants(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
if (next_state == curr_state && event != dl_replenish_nomiss)
return;
if (next_state == ready_nomiss)
ha_start_timer_ns(ha_mon, clk_nomiss, DEADLINE_NS(ha_mon), time_ns);
else if (next_state == running_nomiss)
ha_start_timer_ns(ha_mon, clk_nomiss, DEADLINE_NS(ha_mon), time_ns);
else if (curr_state == ready_nomiss)
ha_cancel_timer(ha_mon);
else if (curr_state == running_nomiss)
ha_cancel_timer(ha_mon);
}
static bool ha_verify_constraint(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
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;
ha_setup_invariants(ha_mon, curr_state, event, next_state, time_ns);
return true;
}
static void handle_dl_replenish(void *data, struct sched_dl_entity *dl_se,
int cpu, u8 type)
{
if (is_supported_type(type))
da_handle_event(EXPAND_ID(dl_se, cpu, type), dl_replenish_nomiss);
}
static void handle_dl_throttle(void *data, struct sched_dl_entity *dl_se,
int cpu, u8 type)
{
if (is_supported_type(type))
da_handle_event(EXPAND_ID(dl_se, cpu, type), dl_throttle_nomiss);
}
static void handle_dl_server_stop(void *data, struct sched_dl_entity *dl_se,
int cpu, u8 type)
{
/*
* This isn't the standard use of da_handle_start_run_event since this
* event cannot only occur from the initial state.
* It is fine to use here because it always brings to a known state and
* the fact we "pretend" the transition starts from the initial state
* has no side effect.
*/
if (is_supported_type(type))
da_handle_start_run_event(EXPAND_ID(dl_se, cpu, type), dl_server_stop_nomiss);
}
static inline void handle_server_switch(struct task_struct *next, int cpu, u8 type)
{
struct sched_dl_entity *dl_se = get_server(next, type);
if (dl_se && is_idle_task(next))
da_handle_event(EXPAND_ID(dl_se, cpu, type), dl_server_idle_nomiss);
}
static void handle_sched_switch(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
{
int cpu = task_cpu(next);
if (prev_state != TASK_RUNNING && !preempt && prev->policy == SCHED_DEADLINE)
da_handle_event(EXPAND_ID_TASK(prev), sched_switch_suspend_nomiss);
if (next->policy == SCHED_DEADLINE)
da_handle_start_run_event(EXPAND_ID_TASK(next), sched_switch_in_nomiss);
/*
* The server is available in next only if the next task is boosted,
* otherwise we need to retrieve it.
* Here the server continues in the state running/armed until actually
* stopped, this works since we continue expecting a throttle.
*/
if (next->dl_server)
da_handle_start_event(EXPAND_ID(next->dl_server, cpu,
get_server_type(next)),
sched_switch_in_nomiss);
else {
handle_server_switch(next, cpu, DL_SERVER_FAIR);
if (IS_ENABLED(CONFIG_SCHED_CLASS_EXT))
handle_server_switch(next, cpu, DL_SERVER_EXT);
}
}
static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
{
struct task_struct *p;
int new_policy = -1;
pid_t pid = 0;
new_policy = extract_params(regs, id, &pid);
if (new_policy < 0)
return;
guard(rcu)();
p = pid ? find_task_by_vpid(pid) : current;
if (unlikely(!p) || new_policy == p->policy)
return;
if (p->policy == SCHED_DEADLINE)
da_reset(EXPAND_ID_TASK(p));
else if (new_policy == SCHED_DEADLINE)
da_create_or_get(EXPAND_ID_TASK(p));
}
static void handle_sched_wakeup(void *data, struct task_struct *tsk)
{
if (tsk->policy == SCHED_DEADLINE)
da_handle_event(EXPAND_ID_TASK(tsk), sched_wakeup_nomiss);
}
static int enable_nomiss(void)
{
int retval;
retval = ha_monitor_init();
if (retval)
return retval;
retval = init_storage(false);
if (retval)
return retval;
rv_attach_trace_probe("nomiss", sched_dl_replenish_tp, handle_dl_replenish);
rv_attach_trace_probe("nomiss", sched_dl_throttle_tp, handle_dl_throttle);
rv_attach_trace_probe("nomiss", sched_dl_server_stop_tp, handle_dl_server_stop);
rv_attach_trace_probe("nomiss", sched_switch, handle_sched_switch);
rv_attach_trace_probe("nomiss", sched_wakeup, handle_sched_wakeup);
if (!should_skip_syscall_handle())
rv_attach_trace_probe("nomiss", sys_enter, handle_sys_enter);
rv_attach_trace_probe("nomiss", task_newtask, handle_newtask);
rv_attach_trace_probe("nomiss", sched_process_exit, handle_exit);
return 0;
}
static void disable_nomiss(void)
{
rv_this.enabled = 0;
/* Those are RCU writers, detach earlier hoping to close a bit faster */
rv_detach_trace_probe("nomiss", task_newtask, handle_newtask);
rv_detach_trace_probe("nomiss", sched_process_exit, handle_exit);
if (!should_skip_syscall_handle())
rv_detach_trace_probe("nomiss", sys_enter, handle_sys_enter);
rv_detach_trace_probe("nomiss", sched_dl_replenish_tp, handle_dl_replenish);
rv_detach_trace_probe("nomiss", sched_dl_throttle_tp, handle_dl_throttle);
rv_detach_trace_probe("nomiss", sched_dl_server_stop_tp, handle_dl_server_stop);
rv_detach_trace_probe("nomiss", sched_switch, handle_sched_switch);
rv_detach_trace_probe("nomiss", sched_wakeup, handle_sched_wakeup);
ha_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "nomiss",
.description = "dl entities run to completion before their deadline.",
.enable = enable_nomiss,
.disable = disable_nomiss,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_nomiss(void)
{
return rv_register_monitor(&rv_this, &rv_deadline);
}
static void __exit unregister_nomiss(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_nomiss);
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.");
@@ -0,0 +1,123 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of nomiss automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME nomiss
enum states_nomiss {
ready_nomiss,
idle_nomiss,
running_nomiss,
sleeping_nomiss,
throttled_nomiss,
state_max_nomiss,
};
#define INVALID_STATE state_max_nomiss
enum events_nomiss {
dl_replenish_nomiss,
dl_server_idle_nomiss,
dl_server_stop_nomiss,
dl_throttle_nomiss,
sched_switch_in_nomiss,
sched_switch_suspend_nomiss,
sched_wakeup_nomiss,
event_max_nomiss,
};
enum envs_nomiss {
clk_nomiss,
is_constr_dl_nomiss,
is_defer_nomiss,
env_max_nomiss,
env_max_stored_nomiss = is_constr_dl_nomiss,
};
_Static_assert(env_max_stored_nomiss <= MAX_HA_ENV_LEN, "Not enough slots");
#define HA_CLK_NS
struct automaton_nomiss {
char *state_names[state_max_nomiss];
char *event_names[event_max_nomiss];
char *env_names[env_max_nomiss];
unsigned char function[state_max_nomiss][event_max_nomiss];
unsigned char initial_state;
bool final_states[state_max_nomiss];
};
static const struct automaton_nomiss automaton_nomiss = {
.state_names = {
"ready",
"idle",
"running",
"sleeping",
"throttled",
},
.event_names = {
"dl_replenish",
"dl_server_idle",
"dl_server_stop",
"dl_throttle",
"sched_switch_in",
"sched_switch_suspend",
"sched_wakeup",
},
.env_names = {
"clk",
"is_constr_dl",
"is_defer",
},
.function = {
{
ready_nomiss,
idle_nomiss,
sleeping_nomiss,
throttled_nomiss,
running_nomiss,
INVALID_STATE,
ready_nomiss,
},
{
ready_nomiss,
idle_nomiss,
sleeping_nomiss,
throttled_nomiss,
running_nomiss,
INVALID_STATE,
INVALID_STATE,
},
{
running_nomiss,
idle_nomiss,
sleeping_nomiss,
throttled_nomiss,
running_nomiss,
sleeping_nomiss,
running_nomiss,
},
{
ready_nomiss,
sleeping_nomiss,
sleeping_nomiss,
throttled_nomiss,
running_nomiss,
INVALID_STATE,
ready_nomiss,
},
{
ready_nomiss,
throttled_nomiss,
INVALID_STATE,
throttled_nomiss,
INVALID_STATE,
throttled_nomiss,
throttled_nomiss,
},
},
.initial_state = ready_nomiss,
.final_states = { 1, 0, 0, 0, 0 },
};
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_NOMISS
DEFINE_EVENT(event_da_monitor_id, event_nomiss,
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_nomiss,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
DEFINE_EVENT(error_env_da_monitor_id, error_env_nomiss,
TP_PROTO(int id, char *state, char *event, char *env),
TP_ARGS(id, state, event, env));
#endif /* CONFIG_RV_MON_NOMISS */
@@ -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,136 @@
// 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 "nrp"
#include <trace/events/irq.h>
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_TASK
#include "nrp.h"
#include <rv/da_monitor.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
da_handle_event(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(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(tsk, sched_need_resched_nrp);
}
static void handle_schedule_entry(void *data, bool preempt)
{
if (preempt)
da_handle_event(current, schedule_entry_preempt_nrp);
else
da_handle_event(current, schedule_entry_nrp);
}
static int enable_nrp(void)
{
int retval;
retval = da_monitor_init();
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_this.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();
}
static struct rv_monitor rv_this = {
.name = "nrp",
.description = "need resched preempts.",
.enable = enable_nrp,
.disable = disable_nrp,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_nrp(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_nrp(void)
{
rv_unregister_monitor(&rv_this);
}
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,77 @@
/* 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
*/
#define MONITOR_NAME nrp
enum states_nrp {
preempt_irq_nrp,
any_thread_running_nrp,
nested_preempt_nrp,
rescheduling_nrp,
state_max_nrp,
};
#define INVALID_STATE state_max_nrp
enum events_nrp {
irq_entry_nrp,
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 */
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_OPID
depends on RV
depends on RV_MON_SCHED
default y
select HA_MON_EVENTS_IMPLICIT
bool "opid monitor"
help
Monitor to ensure operations like wakeup and need resched occur with
interrupts and preemption disabled.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
@@ -0,0 +1,117 @@
// 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 "opid"
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_CPU
#include "opid.h"
#include <rv/ha_monitor.h>
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_opid env, u64 time_ns)
{
if (env == irq_off_opid)
return irqs_disabled();
else if (env == preempt_off_opid) {
if (IS_ENABLED(CONFIG_PREEMPTION))
return (preempt_count() & PREEMPT_MASK) > 0;
return true;
}
return ENV_INVALID_VALUE;
}
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)
{
bool res = true;
if (curr_state == any_opid && event == sched_need_resched_opid)
res = ha_get_env(ha_mon, irq_off_opid, time_ns) == 1ull;
else if (curr_state == any_opid && event == sched_waking_opid)
res = ha_get_env(ha_mon, irq_off_opid, time_ns) == 1ull &&
ha_get_env(ha_mon, preempt_off_opid, time_ns) == 1ull;
return res;
}
static bool ha_verify_constraint(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
return false;
return true;
}
static void handle_sched_need_resched(void *data, struct task_struct *tsk, int cpu, int tif)
{
da_handle_start_run_event(sched_need_resched_opid);
}
static void handle_sched_waking(void *data, struct task_struct *p)
{
da_handle_start_run_event(sched_waking_opid);
}
static int enable_opid(void)
{
int retval;
retval = ha_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
rv_attach_trace_probe("opid", sched_waking, handle_sched_waking);
return 0;
}
static void disable_opid(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
rv_detach_trace_probe("opid", sched_waking, handle_sched_waking);
ha_monitor_destroy();
}
/*
* This is the monitor register section.
*/
static struct rv_monitor rv_this = {
.name = "opid",
.description = "operations with preemption and irq disabled.",
.enable = enable_opid,
.disable = disable_opid,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_opid(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_opid(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_opid);
module_exit(unregister_opid);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("opid: operations with preemption and irq disabled.");
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of opid automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME opid
enum states_opid {
any_opid,
state_max_opid,
};
#define INVALID_STATE state_max_opid
enum events_opid {
sched_need_resched_opid,
sched_waking_opid,
event_max_opid,
};
enum envs_opid {
irq_off_opid,
preempt_off_opid,
env_max_opid,
env_max_stored_opid = irq_off_opid,
};
_Static_assert(env_max_stored_opid <= MAX_HA_ENV_LEN, "Not enough slots");
struct automaton_opid {
char *state_names[state_max_opid];
char *event_names[event_max_opid];
char *env_names[env_max_opid];
unsigned char function[state_max_opid][event_max_opid];
unsigned char initial_state;
bool final_states[state_max_opid];
};
static const struct automaton_opid automaton_opid = {
.state_names = {
"any",
},
.event_names = {
"sched_need_resched",
"sched_waking",
},
.env_names = {
"irq_off",
"preempt_off",
},
.function = {
{ any_opid, any_opid },
},
.initial_state = any_opid,
.final_states = { 1 },
};
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_OPID
DEFINE_EVENT(event_da_monitor, event_opid,
TP_PROTO(char *state, char *event, char *next_state, bool final_state),
TP_ARGS(state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor, error_opid,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
DEFINE_EVENT(error_env_da_monitor, error_env_opid,
TP_PROTO(char *state, char *event, char *env),
TP_ARGS(state, event, env));
#endif /* CONFIG_RV_MON_OPID */
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_PAGEFAULT
depends on RV
select RV_LTL_MONITOR
depends on RV_MON_RTAPP
depends on X86 || RISCV
depends on MMU
default y
select LTL_MON_EVENTS_ID
bool "pagefault monitor"
help
Monitor that real-time tasks do not raise page faults, causing
undesirable latency.
If you are developing a real-time system and not entirely sure whether
the applications are designed correctly for real-time, you want to say
Y here.
This monitor does not affect execution speed while it is not running,
therefore it is safe to enable this in production kernel.
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rv.h>
#include <linux/sched/deadline.h>
#include <linux/sched/rt.h>
#include <linux/tracepoint.h>
#include <rv/instrumentation.h>
#define MODULE_NAME "pagefault"
#include <rv_trace.h>
#include <trace/events/exceptions.h>
#include <monitors/rtapp/rtapp.h>
#include "pagefault.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)
{
if (task_creation)
ltl_atom_set(mon, LTL_PAGEFAULT, false);
}
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);
}
static int enable_pagefault(void)
{
int retval;
retval = ltl_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("rtapp_pagefault", page_fault_kernel, handle_page_fault);
rv_attach_trace_probe("rtapp_pagefault", page_fault_user, handle_page_fault);
return 0;
}
static void disable_pagefault(void)
{
rv_detach_trace_probe("rtapp_pagefault", page_fault_kernel, handle_page_fault);
rv_detach_trace_probe("rtapp_pagefault", page_fault_user, handle_page_fault);
ltl_monitor_destroy();
}
static struct rv_monitor rv_pagefault = {
.name = "pagefault",
.description = "Monitor that RT tasks do not raise page faults",
.enable = enable_pagefault,
.disable = disable_pagefault,
};
static int __init register_pagefault(void)
{
return rv_register_monitor(&rv_pagefault, &rv_rtapp);
}
static void __exit unregister_pagefault(void)
{
rv_unregister_monitor(&rv_pagefault);
}
module_init(register_pagefault);
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");
@@ -0,0 +1,64 @@
/* 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 pagefault
enum ltl_atom {
LTL_PAGEFAULT,
LTL_RT,
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[] = {
"pa",
"rt",
};
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 pagefault = test_bit(LTL_PAGEFAULT, mon->atoms);
bool val3 = !pagefault;
bool rt = test_bit(LTL_RT, mon->atoms);
bool val1 = !rt;
bool val4 = val1 || val3;
if (val4)
__set_bit(S0, mon->states);
}
static void
ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)
{
bool pagefault = test_bit(LTL_PAGEFAULT, mon->atoms);
bool val3 = !pagefault;
bool rt = test_bit(LTL_RT, mon->atoms);
bool val1 = !rt;
bool val4 = val1 || val3;
switch (state) {
case S0:
if (val4)
__set_bit(S0, next);
break;
}
}
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_PAGEFAULT
DEFINE_EVENT(event_ltl_monitor_id, event_pagefault,
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_pagefault,
TP_PROTO(struct task_struct *task),
TP_ARGS(task));
#endif /* CONFIG_RV_MON_PAGEFAULT */
@@ -0,0 +1,11 @@
config RV_MON_RTAPP
depends on RV
depends on RV_PER_TASK_MONITORS >= 2
bool "rtapp monitor"
help
Collection of monitors to check for common problems with real-time
application that may cause unexpected latency.
If you are developing a real-time system and not entirely sure whether
the applications are designed correctly for real-time, you want to say
Y here.
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#define MODULE_NAME "rtapp"
#include "rtapp.h"
struct rv_monitor rv_rtapp = {
.name = "rtapp",
.description = "Collection of monitors for detecting problems with real-time applications",
};
static int __init register_rtapp(void)
{
return rv_register_monitor(&rv_rtapp, NULL);
}
static void __exit unregister_rtapp(void)
{
rv_unregister_monitor(&rv_rtapp);
}
module_init(register_rtapp);
module_exit(unregister_rtapp);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nam Cao <namcao@linutronix.de>");
MODULE_DESCRIPTION("Collection of monitors for detecting problems with real-time applications");
@@ -0,0 +1,3 @@
/* SPDX-License-Identifier: GPL-2.0 */
extern struct rv_monitor rv_rtapp;
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
#
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.
Enable this to enable all scheduler specification supported by the current kernel.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#define MODULE_NAME "sched"
#include "sched.h"
struct rv_monitor rv_sched = {
.name = "sched",
.description = "container for several scheduler monitor specifications.",
.enable = NULL,
.disable = NULL,
.reset = NULL,
.enabled = 0,
};
static int __init register_sched(void)
{
return rv_register_monitor(&rv_sched, NULL);
}
static void __exit unregister_sched(void)
{
rv_unregister_monitor(&rv_sched);
}
module_init(register_sched);
module_exit(unregister_sched);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sched: container for several scheduler monitor specifications.");
@@ -0,0 +1,3 @@
/* SPDX-License-Identifier: GPL-2.0 */
extern struct rv_monitor rv_sched;
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SCO
depends on RV
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
bool "sco monitor"
help
Monitor to ensure sched_set_state happens only in thread context.
This monitor is part of the sched monitors collection.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
@@ -0,0 +1,85 @@
// 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 "sco"
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_CPU
#include "sco.h"
#include <rv/da_monitor.h>
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
da_handle_start_event(sched_set_state_sco);
}
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event(schedule_entry_sco);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_start_event(schedule_exit_sco);
}
static int enable_sco(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("sco", sched_set_state_tp, handle_sched_set_state);
rv_attach_trace_probe("sco", sched_entry_tp, handle_schedule_entry);
rv_attach_trace_probe("sco", sched_exit_tp, handle_schedule_exit);
return 0;
}
static void disable_sco(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("sco", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("sco", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("sco", sched_exit_tp, handle_schedule_exit);
da_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "sco",
.description = "scheduling context operations.",
.enable = enable_sco,
.disable = disable_sco,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_sco(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_sco(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_sco);
module_exit(unregister_sco);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sco: scheduling context operations.");
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of sco automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME sco
enum states_sco {
thread_context_sco,
scheduling_context_sco,
state_max_sco,
};
#define INVALID_STATE state_max_sco
enum events_sco {
sched_set_state_sco,
schedule_entry_sco,
schedule_exit_sco,
event_max_sco,
};
struct automaton_sco {
char *state_names[state_max_sco];
char *event_names[event_max_sco];
unsigned char function[state_max_sco][event_max_sco];
unsigned char initial_state;
bool final_states[state_max_sco];
};
static const struct automaton_sco automaton_sco = {
.state_names = {
"thread_context",
"scheduling_context",
},
.event_names = {
"sched_set_state",
"schedule_entry",
"schedule_exit",
},
.function = {
{ thread_context_sco, scheduling_context_sco, INVALID_STATE },
{ INVALID_STATE, INVALID_STATE, thread_context_sco },
},
.initial_state = thread_context_sco,
.final_states = { 1, 0 },
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SCO
DEFINE_EVENT(event_da_monitor, event_sco,
TP_PROTO(char *state, char *event, char *next_state, bool final_state),
TP_ARGS(state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor, error_sco,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
#endif /* CONFIG_RV_MON_SCO */
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SCPD
depends on RV
depends on TRACE_PREEMPT_TOGGLE
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
bool "scpd monitor"
help
Monitor to ensure schedule is called with preemption disabled.
This monitor is part of the sched monitors collection.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
@@ -0,0 +1,93 @@
// 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 "scpd"
#include <trace/events/sched.h>
#include <trace/events/preemptirq.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_CPU
#include "scpd.h"
#include <rv/da_monitor.h>
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event(preempt_disable_scpd);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_start_event(preempt_enable_scpd);
}
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event(schedule_entry_scpd);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_event(schedule_exit_scpd);
}
static int enable_scpd(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("scpd", preempt_disable, handle_preempt_disable);
rv_attach_trace_probe("scpd", preempt_enable, handle_preempt_enable);
rv_attach_trace_probe("scpd", sched_entry_tp, handle_schedule_entry);
rv_attach_trace_probe("scpd", sched_exit_tp, handle_schedule_exit);
return 0;
}
static void disable_scpd(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("scpd", preempt_disable, handle_preempt_disable);
rv_detach_trace_probe("scpd", preempt_enable, handle_preempt_enable);
rv_detach_trace_probe("scpd", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("scpd", sched_exit_tp, handle_schedule_exit);
da_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "scpd",
.description = "schedule called with preemption disabled.",
.enable = enable_scpd,
.disable = disable_scpd,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_scpd(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_scpd(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_scpd);
module_exit(unregister_scpd);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("scpd: schedule called with preemption disabled.");
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of scpd automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME scpd
enum states_scpd {
cant_sched_scpd,
can_sched_scpd,
state_max_scpd,
};
#define INVALID_STATE state_max_scpd
enum events_scpd {
preempt_disable_scpd,
preempt_enable_scpd,
schedule_entry_scpd,
schedule_exit_scpd,
event_max_scpd,
};
struct automaton_scpd {
char *state_names[state_max_scpd];
char *event_names[event_max_scpd];
unsigned char function[state_max_scpd][event_max_scpd];
unsigned char initial_state;
bool final_states[state_max_scpd];
};
static const struct automaton_scpd automaton_scpd = {
.state_names = {
"cant_sched",
"can_sched",
},
.event_names = {
"preempt_disable",
"preempt_enable",
"schedule_entry",
"schedule_exit",
},
.function = {
{ can_sched_scpd, INVALID_STATE, INVALID_STATE, INVALID_STATE },
{ INVALID_STATE, cant_sched_scpd, can_sched_scpd, can_sched_scpd },
},
.initial_state = cant_sched_scpd,
.final_states = { 1, 0 },
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SCPD
DEFINE_EVENT(event_da_monitor, event_scpd,
TP_PROTO(char *state, char *event, char *next_state, bool final_state),
TP_ARGS(state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor, error_scpd,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
#endif /* CONFIG_RV_MON_SCPD */
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SLEEP
depends on RV
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"
help
Monitor that real-time tasks do not sleep in a manner that may
cause undesirable latency.
If you are developing a real-time system and not entirely sure whether
the applications are designed correctly for real-time, you want to say
Y here.
Enabling this monitor may have performance impact (due to select
TRACE_IRQFLAGS). Therefore, you probably should say N for
production kernel.
@@ -0,0 +1,249 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/init.h>
#include <linux/irqflags.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rv.h>
#include <linux/sched/deadline.h>
#include <linux/sched/rt.h>
#include <rv/instrumentation.h>
#define MODULE_NAME "sleep"
#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>
#include "sleep.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_SLEEP, false);
ltl_atom_set(mon, LTL_WAKE, 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_TIMER_ABSTIME, false);
ltl_atom_set(mon, LTL_CLOCK_NANOSLEEP, false);
ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
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);
}
}
static void handle_sched_set_state(void *data, struct task_struct *task, int state)
{
if (state & TASK_INTERRUPTIBLE)
ltl_atom_pulse(task, LTL_SLEEP, true);
else if (state == TASK_RUNNING)
ltl_atom_pulse(task, LTL_ABORT_SLEEP, true);
}
static void handle_sched_wakeup(void *data, struct task_struct *task)
{
ltl_atom_pulse(task, LTL_WAKE, true);
}
static void handle_sched_waking(void *data, struct task_struct *task)
{
if (this_cpu_read(hardirq_context)) {
ltl_atom_pulse(task, LTL_WOKEN_BY_HARDIRQ, true);
} else if (in_task()) {
if (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);
}
}
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)
{
struct ltl_monitor *mon;
unsigned long args[6];
int op, cmd;
mon = ltl_get_monitor(current);
switch (id) {
#ifdef __NR_clock_nanosleep
case __NR_clock_nanosleep:
#endif
#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);
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, args[1] == TIMER_ABSTIME);
ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, true);
break;
#ifdef __NR_futex
case __NR_futex:
#endif
#ifdef __NR_futex_time64
case __NR_futex_time64:
#endif
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;
case FUTEX_WAIT:
case FUTEX_WAIT_BITSET:
case FUTEX_WAIT_REQUEUE_PI:
ltl_atom_update(current, LTL_FUTEX_WAIT, true);
break;
}
break;
#ifdef __NR_epoll_wait
case __NR_epoll_wait:
ltl_atom_update(current, LTL_EPOLL_WAIT, true);
break;
#endif
}
}
static void handle_sys_exit(void *data, struct pt_regs *regs, long ret)
{
struct ltl_monitor *mon = ltl_get_monitor(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_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);
}
static int enable_sleep(void)
{
int retval;
retval = ltl_monitor_init();
if (retval)
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_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;
}
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_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 = {
.name = "sleep",
.description = "Monitor that RT tasks do not undesirably sleep",
.enable = enable_sleep,
.disable = disable_sleep,
};
static int __init register_sleep(void)
{
return rv_register_monitor(&rv_sleep, &rv_rtapp);
}
static void __exit unregister_sleep(void)
{
rv_unregister_monitor(&rv_sleep);
}
module_init(register_sleep);
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");
@@ -0,0 +1,263 @@
/* 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 sleep
enum ltl_atom {
LTL_ABORT_SLEEP,
LTL_BLOCK_ON_RT_MUTEX,
LTL_CLOCK_NANOSLEEP,
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_TIMER_ABSTIME,
LTL_RT,
LTL_SLEEP,
LTL_TASK_IS_MIGRATION,
LTL_TASK_IS_RCU,
LTL_WAKE,
LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
LTL_WOKEN_BY_HARDIRQ,
LTL_WOKEN_BY_NMI,
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[] = {
"ab_sl",
"bl_on_rt_mu",
"cl_na",
"ep_wa",
"fu_lo_pi",
"fu_wa",
"ker_th",
"kth_sh_st",
"na_cl_mo",
"na_cl_ta",
"na_ti_ab",
"rt",
"sl",
"ta_mi",
"ta_rc",
"wak",
"wo_eq_hi_pr",
"wo_ha",
"wo_nm",
};
return names[atom];
}
enum ltl_buchi_state {
S0,
S1,
S2,
S3,
S4,
S5,
S6,
S7,
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_nmi = test_bit(LTL_WOKEN_BY_NMI, mon->atoms);
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 sleep = test_bit(LTL_SLEEP, 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 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 val20 = val18 || epoll_wait;
bool val9 = futex_wait || val20;
bool val11 = val9 || kernel_thread;
bool val2 = !sleep;
bool val1 = !rt;
bool val3 = val1 || val2;
if (val3)
__set_bit(S0, mon->states);
if (val11 && val13)
__set_bit(S1, mon->states);
if (val11 && val14)
__set_bit(S4, mon->states);
if (val5)
__set_bit(S5, mon->states);
}
static void
ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)
{
bool woken_by_nmi = test_bit(LTL_WOKEN_BY_NMI, mon->atoms);
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 sleep = test_bit(LTL_SLEEP, 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 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 val20 = val18 || epoll_wait;
bool val9 = futex_wait || val20;
bool val11 = val9 || kernel_thread;
bool val2 = !sleep;
bool val1 = !rt;
bool val3 = val1 || val2;
switch (state) {
case S0:
if (val3)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
if (val5)
__set_bit(S5, next);
break;
case S1:
if (val11 && val13)
__set_bit(S1, next);
if (val13 && val3)
__set_bit(S2, next);
if (val14 && val3)
__set_bit(S3, next);
if (val11 && val14)
__set_bit(S4, next);
if (val13 && val5)
__set_bit(S6, next);
if (val14 && val5)
__set_bit(S7, next);
break;
case S2:
if (val11 && val13)
__set_bit(S1, next);
if (val13 && val3)
__set_bit(S2, next);
if (val14 && val3)
__set_bit(S3, next);
if (val11 && val14)
__set_bit(S4, next);
if (val13 && val5)
__set_bit(S6, next);
if (val14 && val5)
__set_bit(S7, next);
break;
case S3:
if (val3)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
if (val5)
__set_bit(S5, next);
break;
case S4:
if (val3)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
if (val5)
__set_bit(S5, next);
break;
case S5:
if (val3)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
if (val5)
__set_bit(S5, next);
break;
case S6:
if (val11 && val13)
__set_bit(S1, next);
if (val13 && val3)
__set_bit(S2, next);
if (val14 && val3)
__set_bit(S3, next);
if (val11 && val14)
__set_bit(S4, next);
if (val13 && val5)
__set_bit(S6, next);
if (val14 && val5)
__set_bit(S7, next);
break;
case S7:
if (val3)
__set_bit(S0, next);
if (val11 && val13)
__set_bit(S1, next);
if (val11 && val14)
__set_bit(S4, next);
if (val5)
__set_bit(S5, next);
break;
}
}
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SLEEP
DEFINE_EVENT(event_ltl_monitor_id, event_sleep,
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_sleep,
TP_PROTO(struct task_struct *task),
TP_ARGS(task));
#endif /* CONFIG_RV_MON_SLEEP */
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SNEP
depends on RV
depends on TRACE_PREEMPT_TOGGLE
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
bool "snep monitor"
help
Monitor to ensure schedule does not enable preempt.
This monitor is part of the sched monitors collection.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
@@ -0,0 +1,93 @@
// 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 "snep"
#include <trace/events/sched.h>
#include <trace/events/preemptirq.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_CPU
#include "snep.h"
#include <rv/da_monitor.h>
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_start_event(preempt_disable_snep);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_start_event(preempt_enable_snep);
}
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event(schedule_entry_snep);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_start_event(schedule_exit_snep);
}
static int enable_snep(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("snep", preempt_disable, handle_preempt_disable);
rv_attach_trace_probe("snep", preempt_enable, handle_preempt_enable);
rv_attach_trace_probe("snep", sched_entry_tp, handle_schedule_entry);
rv_attach_trace_probe("snep", sched_exit_tp, handle_schedule_exit);
return 0;
}
static void disable_snep(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("snep", preempt_disable, handle_preempt_disable);
rv_detach_trace_probe("snep", preempt_enable, handle_preempt_enable);
rv_detach_trace_probe("snep", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("snep", sched_exit_tp, handle_schedule_exit);
da_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "snep",
.description = "schedule does not enable preempt.",
.enable = enable_snep,
.disable = disable_snep,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_snep(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_snep(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_snep);
module_exit(unregister_snep);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("snep: schedule does not enable preempt.");
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of snep automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME snep
enum states_snep {
non_scheduling_context_snep,
scheduling_contex_snep,
state_max_snep,
};
#define INVALID_STATE state_max_snep
enum events_snep {
preempt_disable_snep,
preempt_enable_snep,
schedule_entry_snep,
schedule_exit_snep,
event_max_snep,
};
struct automaton_snep {
char *state_names[state_max_snep];
char *event_names[event_max_snep];
unsigned char function[state_max_snep][event_max_snep];
unsigned char initial_state;
bool final_states[state_max_snep];
};
static const struct automaton_snep automaton_snep = {
.state_names = {
"non_scheduling_context",
"scheduling_contex",
},
.event_names = {
"preempt_disable",
"preempt_enable",
"schedule_entry",
"schedule_exit",
},
.function = {
{
non_scheduling_context_snep,
non_scheduling_context_snep,
scheduling_contex_snep,
INVALID_STATE,
},
{
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
non_scheduling_context_snep,
},
},
.initial_state = non_scheduling_context_snep,
.final_states = { 1, 0 },
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SNEP
DEFINE_EVENT(event_da_monitor, event_snep,
TP_PROTO(char *state, char *event, char *next_state, bool final_state),
TP_ARGS(state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor, error_snep,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
#endif /* CONFIG_RV_MON_SNEP */
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SNROC
depends on RV
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_ID
bool "snroc monitor"
help
Monitor to ensure sched_set_state happens only in the respective task's context.
This monitor is part of the sched monitors collection.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
@@ -0,0 +1,82 @@
// 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 "snroc"
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_TASK
#include "snroc.h"
#include <rv/da_monitor.h>
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
da_handle_event(tsk, sched_set_state_snroc);
}
static void handle_sched_switch(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
{
da_handle_start_event(prev, sched_switch_out_snroc);
da_handle_event(next, sched_switch_in_snroc);
}
static int enable_snroc(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("snroc", sched_set_state_tp, handle_sched_set_state);
rv_attach_trace_probe("snroc", sched_switch, handle_sched_switch);
return 0;
}
static void disable_snroc(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("snroc", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("snroc", sched_switch, handle_sched_switch);
da_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "snroc",
.description = "set non runnable on its own context.",
.enable = enable_snroc,
.disable = disable_snroc,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_snroc(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_snroc(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_snroc);
module_exit(unregister_snroc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("snroc: set non runnable on its own context.");
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of snroc automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME snroc
enum states_snroc {
other_context_snroc,
own_context_snroc,
state_max_snroc,
};
#define INVALID_STATE state_max_snroc
enum events_snroc {
sched_set_state_snroc,
sched_switch_in_snroc,
sched_switch_out_snroc,
event_max_snroc,
};
struct automaton_snroc {
char *state_names[state_max_snroc];
char *event_names[event_max_snroc];
unsigned char function[state_max_snroc][event_max_snroc];
unsigned char initial_state;
bool final_states[state_max_snroc];
};
static const struct automaton_snroc automaton_snroc = {
.state_names = {
"other_context",
"own_context",
},
.event_names = {
"sched_set_state",
"sched_switch_in",
"sched_switch_out",
},
.function = {
{ INVALID_STATE, own_context_snroc, INVALID_STATE },
{ own_context_snroc, INVALID_STATE, other_context_snroc },
},
.initial_state = other_context_snroc,
.final_states = { 1, 0 },
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SNROC
DEFINE_EVENT(event_da_monitor_id, event_snroc,
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_snroc,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
#endif /* CONFIG_RV_MON_SNROC */
@@ -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,114 @@
// 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 "sssw"
#include <trace/events/sched.h>
#include <trace/events/signal.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_TASK
#include "sssw.h"
#include <rv/da_monitor.h>
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
if (state == TASK_RUNNING)
da_handle_start_event(tsk, sched_set_state_runnable_sssw);
else
da_handle_event(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(prev, sched_switch_preempt_sssw);
else if (prev_state == TASK_RUNNING)
da_handle_event(prev, sched_switch_yield_sssw);
else if (prev_state == TASK_RTLOCK_WAIT)
/* special case of sleeping task with racy conditions */
da_handle_event(prev, sched_switch_blocking_sssw);
else
da_handle_event(prev, sched_switch_suspend_sssw);
da_handle_event(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(p, sched_wakeup_sssw);
}
static void handle_signal_deliver(void *data, int sig,
struct kernel_siginfo *info,
struct k_sigaction *ka)
{
da_handle_event(current, signal_deliver_sssw);
}
static int enable_sssw(void)
{
int retval;
retval = da_monitor_init();
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_this.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();
}
static struct rv_monitor rv_this = {
.name = "sssw",
.description = "set state sleep and wakeup.",
.enable = enable_sssw,
.disable = disable_sssw,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_sssw(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_sssw(void)
{
rv_unregister_monitor(&rv_this);
}
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,107 @@
/* 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
*/
#define MONITOR_NAME sssw
enum states_sssw {
runnable_sssw,
signal_wakeup_sssw,
sleepable_sssw,
sleeping_sssw,
state_max_sssw,
};
#define INVALID_STATE state_max_sssw
enum events_sssw {
sched_set_state_runnable_sssw,
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 */
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_STALL
depends on RV
select HA_MON_EVENTS_ID
bool "stall monitor"
help
Enable the stall sample monitor that illustrates the usage of hybrid
automata monitors. It can be used to identify tasks stalled for
longer than a threshold.
For further information, see:
Documentation/trace/rv/monitor_stall.rst
@@ -0,0 +1,150 @@
// 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 "stall"
#include <trace/events/sched.h>
#include <rv_trace.h>
#define RV_MON_TYPE RV_MON_PER_TASK
#define HA_TIMER_TYPE HA_TIMER_WHEEL
#include "stall.h"
#include <rv/ha_monitor.h>
static u64 threshold_jiffies = 1000;
module_param(threshold_jiffies, ullong, 0644);
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_stall env, u64 time_ns)
{
if (env == clk_stall)
return ha_get_clk_jiffy(ha_mon, env);
return ENV_INVALID_VALUE;
}
static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_stall env, u64 time_ns)
{
if (env == clk_stall)
ha_reset_clk_jiffy(ha_mon, env);
}
static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
if (curr_state == enqueued_stall)
return ha_check_invariant_jiffy(ha_mon, clk_stall, time_ns);
return true;
}
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)
{
bool res = true;
if (curr_state == dequeued_stall && event == sched_wakeup_stall)
ha_reset_env(ha_mon, clk_stall, time_ns);
else if (curr_state == running_stall && event == sched_switch_preempt_stall)
ha_reset_env(ha_mon, clk_stall, time_ns);
return res;
}
static inline void ha_setup_invariants(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
if (next_state == curr_state)
return;
if (next_state == enqueued_stall)
ha_start_timer_jiffy(ha_mon, clk_stall, threshold_jiffies, time_ns);
else if (curr_state == enqueued_stall)
ha_cancel_timer(ha_mon);
}
static bool ha_verify_constraint(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
if (!ha_verify_invariants(ha_mon, curr_state, event, next_state, time_ns))
return false;
if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
return false;
ha_setup_invariants(ha_mon, curr_state, event, next_state, time_ns);
return true;
}
static void handle_sched_switch(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
{
if (!preempt && prev_state != TASK_RUNNING)
da_handle_start_event(prev, sched_switch_wait_stall);
else
da_handle_event(prev, sched_switch_preempt_stall);
da_handle_event(next, sched_switch_in_stall);
}
static void handle_sched_wakeup(void *data, struct task_struct *p)
{
da_handle_event(p, sched_wakeup_stall);
}
static int enable_stall(void)
{
int retval;
retval = ha_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("stall", sched_switch, handle_sched_switch);
rv_attach_trace_probe("stall", sched_wakeup, handle_sched_wakeup);
return 0;
}
static void disable_stall(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("stall", sched_switch, handle_sched_switch);
rv_detach_trace_probe("stall", sched_wakeup, handle_sched_wakeup);
ha_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "stall",
.description = "identify tasks stalled for longer than a threshold.",
.enable = enable_stall,
.disable = disable_stall,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_stall(void)
{
return rv_register_monitor(&rv_this, NULL);
}
static void __exit unregister_stall(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_stall);
module_exit(unregister_stall);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("stall: identify tasks stalled for longer than a threshold.");
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of stall automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME stall
enum states_stall {
dequeued_stall,
enqueued_stall,
running_stall,
state_max_stall,
};
#define INVALID_STATE state_max_stall
enum events_stall {
sched_switch_in_stall,
sched_switch_preempt_stall,
sched_switch_wait_stall,
sched_wakeup_stall,
event_max_stall,
};
enum envs_stall {
clk_stall,
env_max_stall,
env_max_stored_stall = env_max_stall,
};
_Static_assert(env_max_stored_stall <= MAX_HA_ENV_LEN, "Not enough slots");
struct automaton_stall {
char *state_names[state_max_stall];
char *event_names[event_max_stall];
char *env_names[env_max_stall];
unsigned char function[state_max_stall][event_max_stall];
unsigned char initial_state;
bool final_states[state_max_stall];
};
static const struct automaton_stall automaton_stall = {
.state_names = {
"dequeued",
"enqueued",
"running",
},
.event_names = {
"sched_switch_in",
"sched_switch_preempt",
"sched_switch_wait",
"sched_wakeup",
},
.env_names = {
"clk",
},
.function = {
{
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
enqueued_stall,
},
{
running_stall,
INVALID_STATE,
INVALID_STATE,
enqueued_stall,
},
{
running_stall,
enqueued_stall,
dequeued_stall,
running_stall,
},
},
.initial_state = dequeued_stall,
.final_states = { 1, 0, 0 },
};
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_STALL
DEFINE_EVENT(event_da_monitor_id, event_stall,
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_stall,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
DEFINE_EVENT(error_env_da_monitor_id, error_env_stall,
TP_PROTO(int id, char *state, char *event, char *env),
TP_ARGS(id, state, event, env));
#endif /* CONFIG_RV_MON_STALL */
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_STS
depends on RV
depends on TRACE_IRQFLAGS
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
bool "sts monitor"
help
Monitor to ensure relationships between scheduler and task switches
* the scheduler is called and returns with interrupts disabled
* each call to the scheduler has up to one switch
* switches only happen inside the scheduler
* each call to the scheduler disables interrupts to switch
This monitor is part of the sched monitors collection.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
@@ -0,0 +1,154 @@
// 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 "sts"
#include <trace/events/sched.h>
#include <trace/events/irq.h>
#include <trace/events/preemptirq.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_CPU
#include "sts.h"
#include <rv/da_monitor.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
da_handle_event(irq_entry_sts);
}
static void attach_vector_irq(void)
{
rv_attach_trace_probe("sts", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_attach_trace_probe("sts", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_attach_trace_probe("sts", reschedule_entry, handle_vector_irq_entry);
rv_attach_trace_probe("sts", call_function_entry, handle_vector_irq_entry);
rv_attach_trace_probe("sts", call_function_single_entry, handle_vector_irq_entry);
}
}
static void detach_vector_irq(void)
{
rv_detach_trace_probe("sts", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_detach_trace_probe("sts", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_detach_trace_probe("sts", reschedule_entry, handle_vector_irq_entry);
rv_detach_trace_probe("sts", call_function_entry, handle_vector_irq_entry);
rv_detach_trace_probe("sts", 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_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event(irq_disable_sts);
}
static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event(irq_enable_sts);
}
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
da_handle_event(irq_entry_sts);
}
static void handle_sched_switch(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
{
da_handle_event(sched_switch_sts);
}
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event(schedule_entry_sts);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_start_event(schedule_exit_sts);
}
static int enable_sts(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("sts", irq_disable, handle_irq_disable);
rv_attach_trace_probe("sts", irq_enable, handle_irq_enable);
rv_attach_trace_probe("sts", irq_handler_entry, handle_irq_entry);
rv_attach_trace_probe("sts", sched_switch, handle_sched_switch);
rv_attach_trace_probe("sts", sched_entry_tp, handle_schedule_entry);
rv_attach_trace_probe("sts", sched_exit_tp, handle_schedule_exit);
attach_vector_irq();
return 0;
}
static void disable_sts(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("sts", irq_disable, handle_irq_disable);
rv_detach_trace_probe("sts", irq_enable, handle_irq_enable);
rv_detach_trace_probe("sts", irq_handler_entry, handle_irq_entry);
rv_detach_trace_probe("sts", sched_switch, handle_sched_switch);
rv_detach_trace_probe("sts", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("sts", sched_exit_tp, handle_schedule_exit);
detach_vector_irq();
da_monitor_destroy();
}
/*
* This is the monitor register section.
*/
static struct rv_monitor rv_this = {
.name = "sts",
.description = "schedule implies task switch.",
.enable = enable_sts,
.disable = disable_sts,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_sts(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_sts(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_sts);
module_exit(unregister_sts);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sts: schedule implies task switch.");
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of sts automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME sts
enum states_sts {
can_sched_sts,
cant_sched_sts,
disable_to_switch_sts,
enable_to_exit_sts,
in_irq_sts,
scheduling_sts,
switching_sts,
state_max_sts,
};
#define INVALID_STATE state_max_sts
enum events_sts {
irq_disable_sts,
irq_enable_sts,
irq_entry_sts,
sched_switch_sts,
schedule_entry_sts,
schedule_exit_sts,
event_max_sts,
};
struct automaton_sts {
char *state_names[state_max_sts];
char *event_names[event_max_sts];
unsigned char function[state_max_sts][event_max_sts];
unsigned char initial_state;
bool final_states[state_max_sts];
};
static const struct automaton_sts automaton_sts = {
.state_names = {
"can_sched",
"cant_sched",
"disable_to_switch",
"enable_to_exit",
"in_irq",
"scheduling",
"switching",
},
.event_names = {
"irq_disable",
"irq_enable",
"irq_entry",
"sched_switch",
"schedule_entry",
"schedule_exit",
},
.function = {
{
cant_sched_sts,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
scheduling_sts,
INVALID_STATE,
},
{
INVALID_STATE,
can_sched_sts,
cant_sched_sts,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
},
{
INVALID_STATE,
enable_to_exit_sts,
in_irq_sts,
switching_sts,
INVALID_STATE,
INVALID_STATE,
},
{
enable_to_exit_sts,
enable_to_exit_sts,
enable_to_exit_sts,
INVALID_STATE,
INVALID_STATE,
can_sched_sts,
},
{
INVALID_STATE,
scheduling_sts,
in_irq_sts,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
},
{
disable_to_switch_sts,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
},
{
INVALID_STATE,
enable_to_exit_sts,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
},
},
.initial_state = can_sched_sts,
.final_states = { 1, 0, 0, 0, 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_STS
DEFINE_EVENT(event_da_monitor, event_sts,
TP_PROTO(char *state, char *event, char *next_state, bool final_state),
TP_ARGS(state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor, error_sts,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
#endif /* CONFIG_RV_MON_STS */
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_WIP
depends on RV
depends on TRACE_PREEMPT_TOGGLE
select DA_MON_EVENTS_IMPLICIT
bool "wip monitor"
help
Enable wip (wakeup in preemptive) sample monitor that illustrates
the usage of per-cpu monitors, and one limitation of the
preempt_disable/enable events.
For further information, see:
Documentation/trace/rv/monitor_wip.rst
@@ -0,0 +1,85 @@
// 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 "wip"
#include <rv_trace.h>
#include <trace/events/sched.h>
#include <trace/events/preemptirq.h>
#define RV_MON_TYPE RV_MON_PER_CPU
#include "wip.h"
#include <rv/da_monitor.h>
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event(preempt_disable_wip);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_start_event(preempt_enable_wip);
}
static void handle_sched_waking(void *data, struct task_struct *task)
{
da_handle_event(sched_waking_wip);
}
static int enable_wip(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("wip", preempt_enable, handle_preempt_enable);
rv_attach_trace_probe("wip", sched_waking, handle_sched_waking);
rv_attach_trace_probe("wip", preempt_disable, handle_preempt_disable);
return 0;
}
static void disable_wip(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("wip", preempt_disable, handle_preempt_disable);
rv_detach_trace_probe("wip", preempt_enable, handle_preempt_enable);
rv_detach_trace_probe("wip", sched_waking, handle_sched_waking);
da_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "wip",
.description = "wakeup in preemptive per-cpu testing monitor.",
.enable = enable_wip,
.disable = disable_wip,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_wip(void)
{
return rv_register_monitor(&rv_this, NULL);
}
static void __exit unregister_wip(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_wip);
module_exit(unregister_wip);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Daniel Bristot de Oliveira <bristot@kernel.org>");
MODULE_DESCRIPTION("wip: wakeup in preemptive - per-cpu sample monitor.");
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of wip automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME wip
enum states_wip {
preemptive_wip,
non_preemptive_wip,
state_max_wip,
};
#define INVALID_STATE state_max_wip
enum events_wip {
preempt_disable_wip,
preempt_enable_wip,
sched_waking_wip,
event_max_wip,
};
struct automaton_wip {
char *state_names[state_max_wip];
char *event_names[event_max_wip];
unsigned char function[state_max_wip][event_max_wip];
unsigned char initial_state;
bool final_states[state_max_wip];
};
static const struct automaton_wip automaton_wip = {
.state_names = {
"preemptive",
"non_preemptive",
},
.event_names = {
"preempt_disable",
"preempt_enable",
"sched_waking",
},
.function = {
{ non_preemptive_wip, INVALID_STATE, INVALID_STATE },
{ INVALID_STATE, preemptive_wip, non_preemptive_wip },
},
.initial_state = preemptive_wip,
.final_states = { 1, 0 },
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_WIP
DEFINE_EVENT(event_da_monitor, event_wip,
TP_PROTO(char *state, char *event, char *next_state, bool final_state),
TP_ARGS(state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor, error_wip,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
#endif /* CONFIG_RV_MON_WIP */
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_WWNR
depends on RV
select DA_MON_EVENTS_ID
bool "wwnr monitor"
help
Enable wwnr (wakeup while not running) sample monitor, this is a
sample monitor that illustrates the usage of per-task monitor.
The model is borken on purpose: it serves to test reactors.
For further information, see:
Documentation/trace/rv/monitor_wwnr.rst
@@ -0,0 +1,84 @@
// 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 "wwnr"
#include <rv_trace.h>
#include <trace/events/sched.h>
#define RV_MON_TYPE RV_MON_PER_TASK
#include "wwnr.h"
#include <rv/da_monitor.h>
static void handle_switch(void *data, bool preempt, struct task_struct *p,
struct task_struct *n, unsigned int prev_state)
{
/* start monitoring only after the first suspension */
if (prev_state == TASK_INTERRUPTIBLE)
da_handle_start_event(p, switch_out_wwnr);
else
da_handle_event(p, switch_out_wwnr);
da_handle_event(n, switch_in_wwnr);
}
static void handle_wakeup(void *data, struct task_struct *p)
{
da_handle_event(p, wakeup_wwnr);
}
static int enable_wwnr(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("wwnr", sched_switch, handle_switch);
rv_attach_trace_probe("wwnr", sched_wakeup, handle_wakeup);
return 0;
}
static void disable_wwnr(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("wwnr", sched_switch, handle_switch);
rv_detach_trace_probe("wwnr", sched_wakeup, handle_wakeup);
da_monitor_destroy();
}
static struct rv_monitor rv_this = {
.name = "wwnr",
.description = "wakeup while not running per-task testing model.",
.enable = enable_wwnr,
.disable = disable_wwnr,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_wwnr(void)
{
return rv_register_monitor(&rv_this, NULL);
}
static void __exit unregister_wwnr(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_wwnr);
module_exit(unregister_wwnr);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Daniel Bristot de Oliveira <bristot@kernel.org>");
MODULE_DESCRIPTION("wwnr: wakeup while not running monitor");
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of wwnr automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
#define MONITOR_NAME wwnr
enum states_wwnr {
not_running_wwnr,
running_wwnr,
state_max_wwnr,
};
#define INVALID_STATE state_max_wwnr
enum events_wwnr {
switch_in_wwnr,
switch_out_wwnr,
wakeup_wwnr,
event_max_wwnr,
};
struct automaton_wwnr {
char *state_names[state_max_wwnr];
char *event_names[event_max_wwnr];
unsigned char function[state_max_wwnr][event_max_wwnr];
unsigned char initial_state;
bool final_states[state_max_wwnr];
};
static const struct automaton_wwnr automaton_wwnr = {
.state_names = {
"not_running",
"running",
},
.event_names = {
"switch_in",
"switch_out",
"wakeup",
},
.function = {
{ running_wwnr, INVALID_STATE, not_running_wwnr },
{ INVALID_STATE, not_running_wwnr, INVALID_STATE },
},
.initial_state = not_running_wwnr,
.final_states = { 1, 0 },
};
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_WWNR
/* id is the pid of the task */
DEFINE_EVENT(event_da_monitor_id, event_wwnr,
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_wwnr,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
#endif /* CONFIG_RV_MON_WWNR */
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* Panic RV reactor:
* Prints the exception msg to the kernel message log and panic().
*/
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
__printf(1, 0) static void rv_panic_reaction(const char *msg, va_list args)
{
vpanic(msg, args);
}
static struct rv_reactor rv_panic = {
.name = "panic",
.description = "panic the system if an exception is found.",
.react = rv_panic_reaction
};
static int __init register_react_panic(void)
{
rv_register_reactor(&rv_panic);
return 0;
}
static void __exit unregister_react_panic(void)
{
rv_unregister_reactor(&rv_panic);
}
module_init(register_react_panic);
module_exit(unregister_react_panic);
MODULE_AUTHOR("Daniel Bristot de Oliveira");
MODULE_DESCRIPTION("panic rv reactor: panic if an exception is found.");
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* Printk RV reactor:
* Prints the exception msg to the kernel message log.
*/
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
__printf(1, 0) static void rv_printk_reaction(const char *msg, va_list args)
{
vprintk_deferred(msg, args);
}
static struct rv_reactor rv_printk = {
.name = "printk",
.description = "prints the exception msg to the kernel message log.",
.react = rv_printk_reaction
};
static int __init register_react_printk(void)
{
rv_register_reactor(&rv_printk);
return 0;
}
static void __exit unregister_react_printk(void)
{
rv_unregister_reactor(&rv_printk);
}
module_init(register_react_printk);
module_exit(unregister_react_printk);
MODULE_AUTHOR("Daniel Bristot de Oliveira");
MODULE_DESCRIPTION("printk rv reactor: printk if an exception is hit.");
+854
View File
@@ -0,0 +1,854 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* This is the online Runtime Verification (RV) interface.
*
* RV is a lightweight (yet rigorous) method that complements classical
* exhaustive verification techniques (such as model checking and
* theorem proving) with a more practical approach to complex systems.
*
* RV works by analyzing the trace of the system's actual execution,
* comparing it against a formal specification of the system behavior.
* RV can give precise information on the runtime behavior of the
* monitored system while enabling the reaction for unexpected
* events, avoiding, for example, the propagation of a failure on
* safety-critical systems.
*
* The development of this interface roots in the development of the
* paper:
*
* De Oliveira, Daniel Bristot; Cucinotta, Tommaso; De Oliveira, Romulo
* Silva. Efficient formal verification for the Linux kernel. In:
* International Conference on Software Engineering and Formal Methods.
* Springer, Cham, 2019. p. 315-332.
*
* And:
*
* De Oliveira, Daniel Bristot, et al. Automata-based formal analysis
* and verification of the real-time Linux kernel. PhD Thesis, 2020.
*
* == Runtime monitor interface ==
*
* A monitor is the central part of the runtime verification of a system.
*
* The monitor stands in between the formal specification of the desired
* (or undesired) behavior, and the trace of the actual system.
*
* In Linux terms, the runtime verification monitors are encapsulated
* inside the "RV monitor" abstraction. A RV monitor includes a reference
* model of the system, a set of instances of the monitor (per-cpu monitor,
* per-task monitor, and so on), and the helper functions that glue the
* monitor to the system via trace. Generally, a monitor includes some form
* of trace output as a reaction for event parsing and exceptions,
* as depicted below:
*
* Linux +----- RV Monitor ----------------------------------+ Formal
* Realm | | Realm
* +-------------------+ +----------------+ +-----------------+
* | Linux kernel | | Monitor | | Reference |
* | Tracing | -> | Instance(s) | <- | Model |
* | (instrumentation) | | (verification) | | (specification) |
* +-------------------+ +----------------+ +-----------------+
* | | |
* | V |
* | +----------+ |
* | | Reaction | |
* | +--+--+--+-+ |
* | | | | |
* | | | +-> trace output ? |
* +------------------------|--|----------------------+
* | +----> panic ?
* +-------> <user-specified>
*
* This file implements the interface for loading RV monitors, and
* to control the verification session.
*
* == Registering monitors ==
*
* The struct rv_monitor defines a set of callback functions to control
* a verification session. For instance, when a given monitor is enabled,
* the "enable" callback function is called to hook the instrumentation
* functions to the kernel trace events. The "disable" function is called
* when disabling the verification session.
*
* A RV monitor is registered via:
* int rv_register_monitor(struct rv_monitor *monitor);
* And unregistered via:
* int rv_unregister_monitor(struct rv_monitor *monitor);
*
* == User interface ==
*
* The user interface resembles kernel tracing interface. It presents
* these files:
*
* "available_monitors"
* - List the available monitors, one per line.
*
* For example:
* # cat available_monitors
* wip
* wwnr
*
* "enabled_monitors"
* - Lists the enabled monitors, one per line;
* - Writing to it enables a given monitor;
* - Writing a monitor name with a '!' prefix disables it;
* - Truncating the file disables all enabled monitors.
*
* For example:
* # cat enabled_monitors
* # echo wip > enabled_monitors
* # echo wwnr >> enabled_monitors
* # cat enabled_monitors
* wip
* wwnr
* # echo '!wip' >> enabled_monitors
* # cat enabled_monitors
* wwnr
* # echo > enabled_monitors
* # cat enabled_monitors
* #
*
* Note that more than one monitor can be enabled concurrently.
*
* "monitoring_on"
* - It is an on/off general switcher for monitoring. Note
* that it does not disable enabled monitors or detach events,
* but stops the per-entity monitors from monitoring the events
* received from the instrumentation. It resembles the "tracing_on"
* switcher.
*
* "monitors/"
* Each monitor will have its own directory inside "monitors/". There
* the monitor specific files will be presented.
* The "monitors/" directory resembles the "events" directory on
* tracefs.
*
* For example:
* # cd monitors/wip/
* # ls
* desc enable
* # cat desc
* auto-generated wakeup in preemptive monitor.
* # cat enable
* 0
*
* For further information, see:
* Documentation/trace/rv/runtime-verification.rst
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#ifdef CONFIG_RV_MON_EVENTS
#define CREATE_TRACE_POINTS
#include <rv_trace.h>
#endif
#include "rv.h"
DEFINE_MUTEX(rv_interface_lock);
static struct rv_interface rv_root;
struct dentry *get_monitors_root(void)
{
return rv_root.monitors_dir;
}
/*
* Interface for the monitor register.
*/
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)
{
int i;
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) {
task_monitor_slots[i] = true;
return i;
}
}
WARN_ONCE(1, "RV task_monitor_count and slots are out of sync\n");
return -EINVAL;
}
void rv_put_task_monitor_slot(int slot)
{
lockdep_assert_held(&rv_interface_lock);
if (slot < 0 || slot >= CONFIG_RV_PER_TASK_MONITORS) {
WARN_ONCE(1, "RV releasing an invalid slot!: %d\n", slot);
return;
}
WARN_ONCE(!task_monitor_slots[slot], "RV releasing unused task_monitor_slots: %d\n",
slot);
task_monitor_count--;
task_monitor_slots[slot] = false;
}
/*
* Monitors with a parent are nested,
* Monitors without a parent could be standalone or containers.
*/
bool rv_is_nested_monitor(struct rv_monitor *mon)
{
return mon->parent != NULL;
}
/*
* We set our list to have nested monitors listed after their parent
* if a monitor has a child element its a container.
* Containers can be also identified based on their function pointers:
* as they are not real monitors they do not need function definitions
* for enable()/disable(). Use this condition to find empty containers.
* Keep both conditions in case we have some non-compliant containers.
*/
bool rv_is_container_monitor(struct rv_monitor *mon)
{
struct rv_monitor *next;
if (list_is_last(&mon->list, &rv_monitors_list))
return false;
next = list_next_entry(mon, list);
return next->parent == mon || !mon->enable;
}
/*
* This section collects the monitor/ files and folders.
*/
static ssize_t monitor_enable_read_data(struct file *filp, char __user *user_buf, size_t count,
loff_t *ppos)
{
struct rv_monitor *mon = filp->private_data;
const char *buff;
buff = mon->enabled ? "1\n" : "0\n";
return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff)+1);
}
/*
* __rv_disable_monitor - disabled an enabled monitor
*/
static int __rv_disable_monitor(struct rv_monitor *mon, bool sync)
{
lockdep_assert_held(&rv_interface_lock);
if (mon->enabled) {
mon->enabled = 0;
if (mon->disable)
mon->disable();
/*
* Wait for the execution of all events to finish.
* Otherwise, the data used by the monitor could
* be inconsistent. i.e., if the monitor is re-enabled.
*/
if (sync)
tracepoint_synchronize_unregister();
return 1;
}
return 0;
}
static void rv_disable_single(struct rv_monitor *mon)
{
__rv_disable_monitor(mon, true);
}
static int rv_enable_single(struct rv_monitor *mon)
{
int retval;
lockdep_assert_held(&rv_interface_lock);
if (mon->enabled)
return 0;
retval = mon->enable();
if (!retval)
mon->enabled = 1;
return retval;
}
static void rv_disable_container(struct rv_monitor *mon)
{
struct rv_monitor *p = mon;
int enabled = 0;
list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (p->parent != mon)
break;
enabled += __rv_disable_monitor(p, false);
}
if (enabled)
tracepoint_synchronize_unregister();
mon->enabled = 0;
}
static int rv_enable_container(struct rv_monitor *mon)
{
struct rv_monitor *p = mon;
int retval = 0;
list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (retval || p->parent != mon)
break;
retval = rv_enable_single(p);
}
if (retval)
rv_disable_container(mon);
else
mon->enabled = 1;
return retval;
}
/**
* rv_disable_monitor - disable a given runtime monitor
* @mon: Pointer to the monitor definition structure.
*
* Returns 0 on success.
*/
int rv_disable_monitor(struct rv_monitor *mon)
{
if (rv_is_container_monitor(mon))
rv_disable_container(mon);
else
rv_disable_single(mon);
return 0;
}
/**
* rv_enable_monitor - enable a given runtime monitor
* @mon: Pointer to the monitor definition structure.
*
* Returns 0 on success, error otherwise.
*/
int rv_enable_monitor(struct rv_monitor *mon)
{
int retval;
if (rv_is_container_monitor(mon))
retval = rv_enable_container(mon);
else
retval = rv_enable_single(mon);
return retval;
}
/*
* interface for enabling/disabling a monitor.
*/
static ssize_t monitor_enable_write_data(struct file *filp, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct rv_monitor *mon = filp->private_data;
int retval;
bool val;
retval = kstrtobool_from_user(user_buf, count, &val);
if (retval)
return retval;
guard(mutex)(&rv_interface_lock);
if (val)
retval = rv_enable_monitor(mon);
else
retval = rv_disable_monitor(mon);
return retval ? : count;
}
static const struct file_operations interface_enable_fops = {
.open = simple_open,
.write = monitor_enable_write_data,
.read = monitor_enable_read_data,
};
/*
* Interface to read monitors description.
*/
static ssize_t monitor_desc_read_data(struct file *filp, char __user *user_buf, size_t count,
loff_t *ppos)
{
struct rv_monitor *mon = filp->private_data;
char buff[256];
memset(buff, 0, sizeof(buff));
snprintf(buff, sizeof(buff), "%s\n", mon->description);
return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff) + 1);
}
static const struct file_operations interface_desc_fops = {
.open = simple_open,
.read = monitor_desc_read_data,
};
/*
* During the registration of a monitor, this function creates
* the monitor dir, where the specific options of the monitor
* are exposed.
*/
static int create_monitor_dir(struct rv_monitor *mon, struct rv_monitor *parent)
{
struct dentry *root = parent ? parent->root_d : get_monitors_root();
struct dentry *dir __free(rv_remove) = rv_create_dir(mon->name, root);
struct dentry *tmp;
int retval;
if (!dir)
return -ENOMEM;
tmp = rv_create_file("enable", RV_MODE_WRITE, dir, mon, &interface_enable_fops);
if (!tmp)
return -ENOMEM;
tmp = rv_create_file("desc", RV_MODE_READ, dir, mon, &interface_desc_fops);
if (!tmp)
return -ENOMEM;
retval = reactor_populate_monitor(mon, dir);
if (retval)
return retval;
mon->root_d = no_free_ptr(dir);
return 0;
}
/*
* Available/Enable monitor shared seq functions.
*/
static int monitors_show(struct seq_file *m, void *p)
{
struct rv_monitor *mon = container_of(p, struct rv_monitor, list);
if (mon->parent)
seq_printf(m, "%s:%s\n", mon->parent->name, mon->name);
else
seq_printf(m, "%s\n", mon->name);
return 0;
}
/*
* Used by the seq file operations at the end of a read
* operation.
*/
static void monitors_stop(struct seq_file *m, void *p)
{
mutex_unlock(&rv_interface_lock);
}
/*
* Available monitor seq functions.
*/
static void *available_monitors_start(struct seq_file *m, loff_t *pos)
{
mutex_lock(&rv_interface_lock);
return seq_list_start(&rv_monitors_list, *pos);
}
static void *available_monitors_next(struct seq_file *m, void *p, loff_t *pos)
{
return seq_list_next(p, &rv_monitors_list, pos);
}
/*
* Enable monitor seq functions.
*/
static void *enabled_monitors_next(struct seq_file *m, void *p, loff_t *pos)
{
struct rv_monitor *mon = container_of(p, struct rv_monitor, list);
(*pos)++;
list_for_each_entry_continue(mon, &rv_monitors_list, list) {
if (mon->enabled)
return &mon->list;
}
return NULL;
}
static void *enabled_monitors_start(struct seq_file *m, loff_t *pos)
{
struct list_head *head;
loff_t l;
mutex_lock(&rv_interface_lock);
if (list_empty(&rv_monitors_list))
return NULL;
head = &rv_monitors_list;
for (l = 0; l <= *pos; ) {
head = enabled_monitors_next(m, head, &l);
if (!head)
break;
}
return head;
}
/*
* available/enabled monitors seq definition.
*/
static const struct seq_operations available_monitors_seq_ops = {
.start = available_monitors_start,
.next = available_monitors_next,
.stop = monitors_stop,
.show = monitors_show
};
static const struct seq_operations enabled_monitors_seq_ops = {
.start = enabled_monitors_start,
.next = enabled_monitors_next,
.stop = monitors_stop,
.show = monitors_show
};
/*
* available_monitors interface.
*/
static int available_monitors_open(struct inode *inode, struct file *file)
{
return seq_open(file, &available_monitors_seq_ops);
};
static const struct file_operations available_monitors_ops = {
.open = available_monitors_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
/*
* enabled_monitors interface.
*/
static void disable_all_monitors(void)
{
struct rv_monitor *mon;
int enabled = 0;
guard(mutex)(&rv_interface_lock);
list_for_each_entry(mon, &rv_monitors_list, list)
enabled += __rv_disable_monitor(mon, false);
if (enabled) {
/*
* Wait for the execution of all events to finish.
* Otherwise, the data used by the monitor could
* be inconsistent. i.e., if the monitor is re-enabled.
*/
tracepoint_synchronize_unregister();
}
}
static int enabled_monitors_open(struct inode *inode, struct file *file)
{
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC))
disable_all_monitors();
return seq_open(file, &enabled_monitors_seq_ops);
};
static ssize_t enabled_monitors_write(struct file *filp, const char __user *user_buf,
size_t count, loff_t *ppos)
{
char buff[MAX_RV_MONITOR_NAME_SIZE + 2];
struct rv_monitor *mon;
int retval = -EINVAL;
bool enable = true;
char *ptr, *tmp;
int len;
if (count < 1 || count > MAX_RV_MONITOR_NAME_SIZE + 1)
return -EINVAL;
memset(buff, 0, sizeof(buff));
retval = simple_write_to_buffer(buff, sizeof(buff) - 1, ppos, user_buf, count);
if (retval < 0)
return -EFAULT;
ptr = strim(buff);
if (ptr[0] == '!') {
enable = false;
ptr++;
}
len = strlen(ptr);
if (!len)
return count;
guard(mutex)(&rv_interface_lock);
retval = -EINVAL;
/* we support 1 nesting level, trim the parent */
tmp = strstr(ptr, ":");
if (tmp)
ptr = tmp+1;
list_for_each_entry(mon, &rv_monitors_list, list) {
if (strcmp(ptr, mon->name) != 0)
continue;
/*
* Monitor found!
*/
if (enable)
retval = rv_enable_monitor(mon);
else
retval = rv_disable_monitor(mon);
if (retval)
return retval;
return count;
}
return retval;
}
static const struct file_operations enabled_monitors_ops = {
.open = enabled_monitors_open,
.read = seq_read,
.write = enabled_monitors_write,
.llseek = seq_lseek,
.release = seq_release,
};
/*
* Monitoring on global switcher!
*/
static bool __read_mostly monitoring_on;
/**
* rv_monitoring_on - checks if monitoring is on
*
* Returns 1 if on, 0 otherwise.
*/
bool rv_monitoring_on(void)
{
return READ_ONCE(monitoring_on);
}
/*
* monitoring_on general switcher.
*/
static ssize_t monitoring_on_read_data(struct file *filp, char __user *user_buf,
size_t count, loff_t *ppos)
{
const char *buff;
buff = rv_monitoring_on() ? "1\n" : "0\n";
return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff) + 1);
}
static void turn_monitoring_off(void)
{
WRITE_ONCE(monitoring_on, false);
}
static void reset_all_monitors(void)
{
struct rv_monitor *mon;
list_for_each_entry(mon, &rv_monitors_list, list) {
if (mon->enabled && mon->reset)
mon->reset();
}
}
static void turn_monitoring_on(void)
{
WRITE_ONCE(monitoring_on, true);
}
static void turn_monitoring_on_with_reset(void)
{
lockdep_assert_held(&rv_interface_lock);
if (rv_monitoring_on())
return;
/*
* Monitors might be out of sync with the system if events were not
* processed because of !rv_monitoring_on().
*
* Reset all monitors, forcing a re-sync.
*/
reset_all_monitors();
turn_monitoring_on();
}
static ssize_t monitoring_on_write_data(struct file *filp, const char __user *user_buf,
size_t count, loff_t *ppos)
{
int retval;
bool val;
retval = kstrtobool_from_user(user_buf, count, &val);
if (retval)
return retval;
guard(mutex)(&rv_interface_lock);
if (val)
turn_monitoring_on_with_reset();
else
turn_monitoring_off();
/*
* Wait for the execution of all events to finish
* before returning to user-space.
*/
tracepoint_synchronize_unregister();
return count;
}
static const struct file_operations monitoring_on_fops = {
.open = simple_open,
.write = monitoring_on_write_data,
.read = monitoring_on_read_data,
};
static void destroy_monitor_dir(struct rv_monitor *mon)
{
rv_remove(mon->root_d);
}
/**
* rv_register_monitor - register a rv monitor.
* @monitor: The rv_monitor to be registered.
* @parent: The parent of the monitor to be registered, NULL if not nested.
*
* Returns 0 if successful, error otherwise.
*/
int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent)
{
struct rv_monitor *r;
int retval = 0;
if (strlen(monitor->name) >= MAX_RV_MONITOR_NAME_SIZE) {
pr_info("Monitor %s has a name longer than %d\n", monitor->name,
MAX_RV_MONITOR_NAME_SIZE);
return -EINVAL;
}
guard(mutex)(&rv_interface_lock);
list_for_each_entry(r, &rv_monitors_list, list) {
if (strcmp(monitor->name, r->name) == 0) {
pr_info("Monitor %s is already registered\n", monitor->name);
return -EEXIST;
}
}
if (parent && rv_is_nested_monitor(parent)) {
pr_info("Parent monitor %s is already nested, cannot nest further\n",
parent->name);
return -EINVAL;
}
monitor->parent = parent;
retval = create_monitor_dir(monitor, parent);
if (retval)
return retval;
/* keep children close to the parent for easier visualisation */
if (parent)
list_add(&monitor->list, &parent->list);
else
list_add_tail(&monitor->list, &rv_monitors_list);
return 0;
}
/**
* rv_unregister_monitor - unregister a rv monitor.
* @monitor: The rv_monitor to be unregistered.
*
* Returns 0 if successful, error otherwise.
*/
int rv_unregister_monitor(struct rv_monitor *monitor)
{
guard(mutex)(&rv_interface_lock);
rv_disable_monitor(monitor);
list_del(&monitor->list);
destroy_monitor_dir(monitor);
return 0;
}
int __init rv_init_interface(void)
{
struct dentry *tmp;
int retval;
struct dentry *root_dir __free(rv_remove) = rv_create_dir("rv", NULL);
if (!root_dir)
return 1;
rv_root.monitors_dir = rv_create_dir("monitors", root_dir);
if (!rv_root.monitors_dir)
return 1;
tmp = rv_create_file("available_monitors", RV_MODE_READ, root_dir, NULL,
&available_monitors_ops);
if (!tmp)
return 1;
tmp = rv_create_file("enabled_monitors", RV_MODE_WRITE, root_dir, NULL,
&enabled_monitors_ops);
if (!tmp)
return 1;
tmp = rv_create_file("monitoring_on", RV_MODE_WRITE, root_dir, NULL,
&monitoring_on_fops);
if (!tmp)
return 1;
retval = init_rv_reactors(root_dir);
if (retval)
return 1;
turn_monitoring_on();
rv_root.root_dir = no_free_ptr(root_dir);
return 0;
}
+47
View File
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/mutex.h>
struct rv_interface {
struct dentry *root_dir;
struct dentry *monitors_dir;
};
#include "../trace.h"
#include <linux/tracefs.h>
#include <linux/rv.h>
#define RV_MODE_WRITE TRACE_MODE_WRITE
#define RV_MODE_READ TRACE_MODE_READ
#define rv_create_dir tracefs_create_dir
#define rv_create_file tracefs_create_file
#define rv_remove tracefs_remove
DEFINE_FREE(rv_remove, struct dentry *, if (_T) rv_remove(_T));
#define MAX_RV_MONITOR_NAME_SIZE 32
#define MAX_RV_REACTOR_NAME_SIZE 32
extern struct mutex rv_interface_lock;
extern struct list_head rv_monitors_list;
struct dentry *get_monitors_root(void);
int rv_disable_monitor(struct rv_monitor *mon);
int rv_enable_monitor(struct rv_monitor *mon);
bool rv_is_container_monitor(struct rv_monitor *mon);
bool rv_is_nested_monitor(struct rv_monitor *mon);
#ifdef CONFIG_RV_REACTORS
int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root);
int init_rv_reactors(struct dentry *root_dir);
#else
static inline int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root)
{
return 0;
}
static inline int init_rv_reactors(struct dentry *root_dir)
{
return 0;
}
#endif
+481
View File
@@ -0,0 +1,481 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* Runtime reactor interface.
*
* A runtime monitor can cause a reaction to the detection of an
* exception on the model's execution. By default, the monitors have
* tracing reactions, printing the monitor output via tracepoints.
* But other reactions can be added (on-demand) via this interface.
*
* == Registering reactors ==
*
* The struct rv_reactor defines a callback function to be executed
* in case of a model exception happens. The callback function
* receives a message to be (optionally) printed before executing
* the reaction.
*
* A RV reactor is registered via:
* int rv_register_reactor(struct rv_reactor *reactor)
* And unregistered via:
* int rv_unregister_reactor(struct rv_reactor *reactor)
*
* These functions are exported to modules, enabling reactors to be
* dynamically loaded.
*
* == User interface ==
*
* The user interface resembles the kernel tracing interface and
* presents these files:
*
* "available_reactors"
* - List the available reactors, one per line.
*
* For example:
* # cat available_reactors
* nop
* panic
* printk
*
* "reacting_on"
* - It is an on/off general switch for reactors, disabling
* all reactions.
*
* "monitors/MONITOR/reactors"
* - List available reactors, with the select reaction for the given
* MONITOR inside []. The default one is the nop (no operation)
* reactor.
* - Writing the name of an reactor enables it to the given
* MONITOR.
*
* For example:
* # cat monitors/wip/reactors
* [nop]
* panic
* printk
* # echo panic > monitors/wip/reactors
* # cat monitors/wip/reactors
* nop
* [panic]
* printk
*/
#include <linux/lockdep.h>
#include <linux/slab.h>
#include "rv.h"
/*
* Interface for the reactor register.
*/
static LIST_HEAD(rv_reactors_list);
static struct rv_reactor *get_reactor_rdef_by_name(char *name)
{
struct rv_reactor *r;
list_for_each_entry(r, &rv_reactors_list, list) {
if (strcmp(name, r->name) == 0)
return r;
}
return NULL;
}
/*
* Available reactors seq functions.
*/
static int reactors_show(struct seq_file *m, void *p)
{
struct rv_reactor *reactor = container_of(p, struct rv_reactor, list);
seq_printf(m, "%s\n", reactor->name);
return 0;
}
static void reactors_stop(struct seq_file *m, void *p)
{
mutex_unlock(&rv_interface_lock);
}
static void *reactors_start(struct seq_file *m, loff_t *pos)
{
mutex_lock(&rv_interface_lock);
return seq_list_start(&rv_reactors_list, *pos);
}
static void *reactors_next(struct seq_file *m, void *p, loff_t *pos)
{
return seq_list_next(p, &rv_reactors_list, pos);
}
/*
* available_reactors seq definition.
*/
static const struct seq_operations available_reactors_seq_ops = {
.start = reactors_start,
.next = reactors_next,
.stop = reactors_stop,
.show = reactors_show
};
/*
* available_reactors interface.
*/
static int available_reactors_open(struct inode *inode, struct file *file)
{
return seq_open(file, &available_reactors_seq_ops);
};
static const struct file_operations available_reactors_ops = {
.open = available_reactors_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
/*
* Monitor's reactor file.
*/
static int monitor_reactor_show(struct seq_file *m, void *p)
{
struct rv_monitor *mon = m->private;
struct rv_reactor *reactor = container_of(p, struct rv_reactor, list);
if (mon->reactor == reactor)
seq_printf(m, "[%s]\n", reactor->name);
else
seq_printf(m, "%s\n", reactor->name);
return 0;
}
/*
* available_reactors seq definition.
*/
static const struct seq_operations monitor_reactors_seq_ops = {
.start = reactors_start,
.next = reactors_next,
.stop = reactors_stop,
.show = monitor_reactor_show
};
static void monitor_swap_reactors_single(struct rv_monitor *mon,
struct rv_reactor *reactor,
bool nested)
{
bool monitor_enabled;
/* nothing to do */
if (mon->reactor == reactor)
return;
monitor_enabled = mon->enabled;
if (monitor_enabled)
rv_disable_monitor(mon);
mon->reactor = reactor;
mon->react = reactor->react;
/* enable only once if iterating through a container */
if (monitor_enabled && !nested)
rv_enable_monitor(mon);
}
static void monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor)
{
struct rv_monitor *p = mon;
if (rv_is_container_monitor(mon))
list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (p->parent != mon)
break;
monitor_swap_reactors_single(p, reactor, true);
}
/*
* This call enables and disables the monitor if they were active.
* In case of a container, we already disabled all and will enable all.
* All nested monitors are enabled also if they were off, we may refine
* this logic in the future.
*/
monitor_swap_reactors_single(mon, reactor, false);
}
static ssize_t
monitor_reactors_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
char buff[MAX_RV_REACTOR_NAME_SIZE + 2];
struct rv_monitor *mon;
struct rv_reactor *reactor;
struct seq_file *seq_f;
int retval = -EINVAL;
char *ptr;
int len;
if (count < 1 || count > MAX_RV_REACTOR_NAME_SIZE + 1)
return -EINVAL;
memset(buff, 0, sizeof(buff));
retval = simple_write_to_buffer(buff, sizeof(buff) - 1, ppos, user_buf, count);
if (retval < 0)
return -EFAULT;
ptr = strim(buff);
len = strlen(ptr);
if (!len)
return count;
/*
* See monitor_reactors_open()
*/
seq_f = file->private_data;
mon = seq_f->private;
guard(mutex)(&rv_interface_lock);
list_for_each_entry(reactor, &rv_reactors_list, list) {
if (strcmp(ptr, reactor->name) != 0)
continue;
monitor_swap_reactors(mon, reactor);
return count;
}
return -EINVAL;
}
/*
* available_reactors interface.
*/
static int monitor_reactors_open(struct inode *inode, struct file *file)
{
struct rv_monitor *mon = inode->i_private;
struct seq_file *seq_f;
int ret;
ret = seq_open(file, &monitor_reactors_seq_ops);
if (ret < 0)
return ret;
/*
* seq_open stores the seq_file on the file->private data.
*/
seq_f = file->private_data;
/*
* Copy the create file "private" data to the seq_file private data.
*/
seq_f->private = mon;
return 0;
};
static const struct file_operations monitor_reactors_ops = {
.open = monitor_reactors_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.write = monitor_reactors_write
};
static int __rv_register_reactor(struct rv_reactor *reactor)
{
struct rv_reactor *r;
list_for_each_entry(r, &rv_reactors_list, list) {
if (strcmp(reactor->name, r->name) == 0) {
pr_info("Reactor %s is already registered\n", reactor->name);
return -EINVAL;
}
}
list_add_tail(&reactor->list, &rv_reactors_list);
return 0;
}
/**
* rv_register_reactor - register a rv reactor.
* @reactor: The rv_reactor to be registered.
*
* Returns 0 if successful, error otherwise.
*/
int rv_register_reactor(struct rv_reactor *reactor)
{
if (strlen(reactor->name) >= MAX_RV_REACTOR_NAME_SIZE) {
pr_info("Reactor %s has a name longer than %d\n",
reactor->name, MAX_RV_MONITOR_NAME_SIZE);
return -EINVAL;
}
guard(mutex)(&rv_interface_lock);
return __rv_register_reactor(reactor);
}
/**
* rv_unregister_reactor - unregister a rv reactor.
* @reactor: The rv_reactor to be unregistered.
*
* Returns 0 if successful, error otherwise.
*/
int rv_unregister_reactor(struct rv_reactor *reactor)
{
guard(mutex)(&rv_interface_lock);
list_del(&reactor->list);
return 0;
}
/*
* reacting_on interface.
*/
static bool __read_mostly reacting_on;
/**
* rv_reacting_on - checks if reacting is on
*
* Returns 1 if on, 0 otherwise.
*/
static bool rv_reacting_on(void)
{
/* Ensures that concurrent monitors read consistent reacting_on */
smp_rmb();
return READ_ONCE(reacting_on);
}
static ssize_t reacting_on_read_data(struct file *filp,
char __user *user_buf,
size_t count, loff_t *ppos)
{
char *buff;
buff = rv_reacting_on() ? "1\n" : "0\n";
return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff)+1);
}
static void turn_reacting_off(void)
{
WRITE_ONCE(reacting_on, false);
/* Ensures that concurrent monitors read consistent reacting_on */
smp_wmb();
}
static void turn_reacting_on(void)
{
WRITE_ONCE(reacting_on, true);
/* Ensures that concurrent monitors read consistent reacting_on */
smp_wmb();
}
static ssize_t reacting_on_write_data(struct file *filp, const char __user *user_buf,
size_t count, loff_t *ppos)
{
int retval;
bool val;
retval = kstrtobool_from_user(user_buf, count, &val);
if (retval)
return retval;
guard(mutex)(&rv_interface_lock);
if (val)
turn_reacting_on();
else
turn_reacting_off();
/*
* Wait for the execution of all events to finish
* before returning to user-space.
*/
tracepoint_synchronize_unregister();
return count;
}
static const struct file_operations reacting_on_fops = {
.open = simple_open,
.write = reacting_on_write_data,
.read = reacting_on_read_data,
};
/**
* reactor_populate_monitor - creates per monitor reactors file
* @mon: The monitor.
* @root: The directory of the monitor.
*
* Returns 0 if successful, error otherwise.
*/
int reactor_populate_monitor(struct rv_monitor *mon, struct dentry *root)
{
struct dentry *tmp;
tmp = rv_create_file("reactors", RV_MODE_WRITE, root, mon, &monitor_reactors_ops);
if (!tmp)
return -ENOMEM;
/*
* Configure as the rv_nop reactor.
*/
mon->reactor = get_reactor_rdef_by_name("nop");
return 0;
}
/*
* Nop reactor register
*/
__printf(1, 0) static void rv_nop_reaction(const char *msg, va_list args)
{
}
static struct rv_reactor rv_nop = {
.name = "nop",
.description = "no-operation reactor: do nothing.",
.react = rv_nop_reaction
};
int init_rv_reactors(struct dentry *root_dir)
{
int retval;
struct dentry *available __free(rv_remove) =
rv_create_file("available_reactors", RV_MODE_READ, root_dir,
NULL, &available_reactors_ops);
struct dentry *reacting __free(rv_remove) =
rv_create_file("reacting_on", RV_MODE_WRITE, root_dir, NULL, &reacting_on_fops);
if (!reacting || !available)
return -ENOMEM;
retval = __rv_register_reactor(&rv_nop);
if (retval)
return retval;
turn_reacting_on();
retain_and_null_ptr(available);
retain_and_null_ptr(reacting);
return 0;
}
void rv_react(struct rv_monitor *monitor, const char *msg, ...)
{
static DEFINE_WAIT_OVERRIDE_MAP(rv_react_map, LD_WAIT_FREE);
va_list args;
if (!rv_reacting_on() || !monitor->react)
return;
va_start(args, msg);
lock_map_acquire_try(&rv_react_map);
monitor->react(msg, args);
lock_map_release(&rv_react_map);
va_end(args);
}
+277
View File
@@ -0,0 +1,277 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM rv
#if !defined(_TRACE_RV_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_RV_H
#include <linux/rv.h>
#include <linux/tracepoint.h>
#ifdef CONFIG_DA_MON_EVENTS_IMPLICIT
DECLARE_EVENT_CLASS(event_da_monitor,
TP_PROTO(char *state, char *event, char *next_state, bool final_state),
TP_ARGS(state, event, next_state, final_state),
TP_STRUCT__entry(
__string( state, state )
__string( event, event )
__string( next_state, next_state )
__field( bool, final_state )
),
TP_fast_assign(
__assign_str(state);
__assign_str(event);
__assign_str(next_state);
__entry->final_state = final_state;
),
TP_printk("%s x %s -> %s%s",
__get_str(state),
__get_str(event),
__get_str(next_state),
__entry->final_state ? " (final)" : "")
);
DECLARE_EVENT_CLASS(error_da_monitor,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event),
TP_STRUCT__entry(
__string( state, state )
__string( event, event )
),
TP_fast_assign(
__assign_str(state);
__assign_str(event);
),
TP_printk("event %s not expected in the state %s",
__get_str(event),
__get_str(state))
);
#include <monitors/wip/wip_trace.h>
#include <monitors/sco/sco_trace.h>
#include <monitors/scpd/scpd_trace.h>
#include <monitors/snep/snep_trace.h>
#include <monitors/sts/sts_trace.h>
// Add new monitors based on CONFIG_DA_MON_EVENTS_IMPLICIT here
#ifdef CONFIG_HA_MON_EVENTS_IMPLICIT
/* For simplicity this class is marked as DA although relevant only for HA */
DECLARE_EVENT_CLASS(error_env_da_monitor,
TP_PROTO(char *state, char *event, char *env),
TP_ARGS(state, event, env),
TP_STRUCT__entry(
__string( state, state )
__string( event, event )
__string( env, env )
),
TP_fast_assign(
__assign_str(state);
__assign_str(event);
__assign_str(env);
),
TP_printk("event %s not expected in the state %s with env %s",
__get_str(event),
__get_str(state),
__get_str(env))
);
#include <monitors/opid/opid_trace.h>
// Add new monitors based on CONFIG_HA_MON_EVENTS_IMPLICIT here
#endif
#endif /* CONFIG_DA_MON_EVENTS_IMPLICIT */
#ifdef CONFIG_DA_MON_EVENTS_ID
DECLARE_EVENT_CLASS(event_da_monitor_id,
TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
TP_ARGS(id, state, event, next_state, final_state),
TP_STRUCT__entry(
__field( int, id )
__string( state, state )
__string( event, event )
__string( next_state, next_state )
__field( bool, final_state )
),
TP_fast_assign(
__assign_str(state);
__assign_str(event);
__assign_str(next_state);
__entry->id = id;
__entry->final_state = final_state;
),
TP_printk("%d: %s x %s -> %s%s",
__entry->id,
__get_str(state),
__get_str(event),
__get_str(next_state),
__entry->final_state ? " (final)" : "")
);
DECLARE_EVENT_CLASS(error_da_monitor_id,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event),
TP_STRUCT__entry(
__field( int, id )
__string( state, state )
__string( event, event )
),
TP_fast_assign(
__assign_str(state);
__assign_str(event);
__entry->id = id;
),
TP_printk("%d: event %s not expected in the state %s",
__entry->id,
__get_str(event),
__get_str(state))
);
#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
#ifdef CONFIG_HA_MON_EVENTS_ID
/* For simplicity this class is marked as DA although relevant only for HA */
DECLARE_EVENT_CLASS(error_env_da_monitor_id,
TP_PROTO(int id, char *state, char *event, char *env),
TP_ARGS(id, state, event, env),
TP_STRUCT__entry(
__field( int, id )
__string( state, state )
__string( event, event )
__string( env, env )
),
TP_fast_assign(
__assign_str(state);
__assign_str(event);
__assign_str(env);
__entry->id = id;
),
TP_printk("%d: event %s not expected in the state %s with env %s",
__entry->id,
__get_str(event),
__get_str(state),
__get_str(env))
);
#include <monitors/stall/stall_trace.h>
#include <monitors/nomiss/nomiss_trace.h>
// Add new monitors based on CONFIG_HA_MON_EVENTS_ID here
#endif
#endif /* CONFIG_DA_MON_EVENTS_ID */
#ifdef CONFIG_LTL_MON_EVENTS_ID
DECLARE_EVENT_CLASS(event_ltl_monitor_id,
TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next),
TP_ARGS(task, states, atoms, next),
TP_STRUCT__entry(
__string(comm, task->comm)
__field(pid_t, pid)
__string(states, states)
__string(atoms, atoms)
__string(next, next)
),
TP_fast_assign(
__assign_str(comm);
__entry->pid = task->pid;
__assign_str(states);
__assign_str(atoms);
__assign_str(next);
),
TP_printk("%s[%d]: (%s) x (%s) -> (%s)", __get_str(comm), __entry->pid,
__get_str(states), __get_str(atoms), __get_str(next))
);
DECLARE_EVENT_CLASS(error_ltl_monitor_id,
TP_PROTO(struct task_struct *task),
TP_ARGS(task),
TP_STRUCT__entry(
__string(comm, task->comm)
__field(pid_t, pid)
),
TP_fast_assign(
__assign_str(comm);
__entry->pid = task->pid;
),
TP_printk("%s[%d]: violation detected", __get_str(comm), __entry->pid)
);
#include <monitors/pagefault/pagefault_trace.h>
#include <monitors/sleep/sleep_trace.h>
// Add new monitors based on CONFIG_LTL_MON_EVENTS_ID here
#endif /* CONFIG_LTL_MON_EVENTS_ID */
#ifdef CONFIG_RV_MON_MAINTENANCE_EVENTS
/* Tracepoint useful for monitors development, currenly only used in DA */
TRACE_EVENT(rv_retries_error,
TP_PROTO(char *name, char *event),
TP_ARGS(name, event),
TP_STRUCT__entry(
__string( name, name )
__string( event, event )
),
TP_fast_assign(
__assign_str(name);
__assign_str(event);
),
TP_printk(__stringify(MAX_DA_RETRY_RACING_EVENTS)
" retries reached for event %s, resetting monitor %s",
__get_str(event), __get_str(name))
);
#endif /* CONFIG_RV_MON_MAINTENANCE_EVENTS */
#endif /* _TRACE_RV_H */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE rv_trace
#include <trace/define_trace.h>