Merge tag 'trace-rv-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull runtime verification updates from Steven Rostedt:

 - Added Linear temporal logic monitors for RT application

   Real-time applications may have design flaws causing them to have
   unexpected latency. For example, the applications may raise page
   faults, or may be blocked trying to take a mutex without priority
   inheritance.

   However, while attempting to implement DA monitors for these
   real-time rules, deterministic automaton is found to be inappropriate
   as the specification language. The automaton is complicated, hard to
   understand, and error-prone.

   For these cases, linear temporal logic is found to be more suitable.
   The LTL is more concise and intuitive.

 - Make printk_deferred() public

   The new monitors needed access to printk_deferred(). Make them
   visible for the entire kernel.

 - Add a vpanic() to allow for va_list to be passed to panic.

 - Add rtapp container monitor.

   A collection of monitors that check for common problems with
   real-time applications that cause unexpected latency.

 - Add page fault tracepoints to risc-v

   These tracepoints are necessary to for the RV monitor to run on
   risc-v.

 - Fix the behaviour of the rv tool with -s and idle tasks.

 - Allow the rv tool to gracefully terminate with SIGTERM

 - Adjusts dot2c not to create lines over 100 columns

 - Properly order nested monitors in the RV Kconfig file

 - Return the registration error in all DA monitor instead of 0

 - Update and add new sched collection monitors

   Replace tss and sncid monitors with more complete sts:

   Not only prove that switches occur in scheduling context and scheduling
   needs interrupt disabled but also that each call to the scheduler
   disables interrupts to (optionally) switch.

   New monitor: nrp
     Preemption requires need resched which is cleared by any switch
     (includes a non optimal workaround for /nested/ preemptions)

   New monitor: sssw
     suspension requires setting the task to sleepable and, after the
     switch occurs, the task requires a wakeup to come back to runnable

   New monitor: opid
      waking and need-resched operations occur with interrupts and
      preemption disabled or in IRQ without explicitly disabling
      preemption"

* tag 'trace-rv-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (48 commits)
  rv: Add opid per-cpu monitor
  rv: Add nrp and sssw per-task monitors
  rv: Replace tss and sncid monitors with more complete sts
  sched: Adapt sched tracepoints for RV task model
  rv: Retry when da monitor detects race conditions
  rv: Adjust monitor dependencies
  rv: Use strings in da monitors tracepoints
  rv: Remove trailing whitespace from tracepoint string
  rv: Add da_handle_start_run_event_ to per-task monitors
  rv: Fix wrong type cast in reactors_show() and monitor_reactor_show()
  rv: Fix wrong type cast in monitors_show()
  rv: Remove struct rv_monitor::reacting
  rv: Remove rv_reactor's reference counter
  rv: Merge struct rv_reactor_def into struct rv_reactor
  rv: Merge struct rv_monitor_def into struct rv_monitor
  rv: Remove unused field in struct rv_monitor_def
  rv: Return init error when registering monitors
  verification/rvgen: Organise Kconfig entries for nested monitors
  tools/dot2c: Fix generated files going over 100 column limit
  tools/rv: Stop gracefully also on SIGTERM
  ...
This commit is contained in:
Linus Torvalds
2025-07-30 16:23:12 -07:00
101 changed files with 4860 additions and 1265 deletions
+1 -4
View File
@@ -1890,10 +1890,7 @@ static void copy_oom_score_adj(u64 clone_flags, struct task_struct *tsk)
#ifdef CONFIG_RV
static void rv_task_fork(struct task_struct *p)
{
int i;
for (i = 0; i < RV_PER_TASK_MONITORS; i++)
p->rv[i].da_mon.monitoring = false;
memset(&p->rv, 0, sizeof(p->rv));
}
#else
#define rv_task_fork(p) do {} while (0)
+13 -5
View File
@@ -367,15 +367,15 @@ static void panic_other_cpus_shutdown(bool crash_kexec)
}
/**
* panic - halt the system
* vpanic - halt the system
* @fmt: The text string to print
* @args: Arguments for the format string
*
* Display a message, then perform cleanups. This function never returns.
*/
void panic(const char *fmt, ...)
void vpanic(const char *fmt, va_list args)
{
static char buf[1024];
va_list args;
long i, i_next = 0, len;
int state = 0;
int old_cpu, this_cpu;
@@ -426,9 +426,7 @@ void panic(const char *fmt, ...)
console_verbose();
bust_spinlocks(1);
va_start(args, fmt);
len = vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (len && buf[len - 1] == '\n')
buf[len - 1] = '\0';
@@ -565,7 +563,17 @@ void panic(const char *fmt, ...)
mdelay(PANIC_TIMER_STEP);
}
}
EXPORT_SYMBOL(vpanic);
/* Identical to vpanic(), except it takes variadic arguments instead of va_list */
void panic(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vpanic(fmt, args);
va_end(args);
}
EXPORT_SYMBOL(panic);
#define TAINT_FLAG(taint, _c_true, _c_false, _module) \
-1
View File
@@ -72,7 +72,6 @@ int vprintk_store(int facility, int level,
const char *fmt, va_list args);
__printf(1, 0) int vprintk_default(const char *fmt, va_list args);
__printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
void __printk_safe_enter(void);
void __printk_safe_exit(void);
+10 -3
View File
@@ -1113,6 +1113,7 @@ static void __resched_curr(struct rq *rq, int tif)
cpu = cpu_of(rq);
trace_sched_set_need_resched_tp(curr, cpu, tif);
if (cpu == smp_processor_id()) {
set_ti_thread_flag(cti, tif);
if (tif == TIF_NEED_RESCHED)
@@ -1128,6 +1129,11 @@ static void __resched_curr(struct rq *rq, int tif)
}
}
void __trace_set_need_resched(struct task_struct *curr, int tif)
{
trace_sched_set_need_resched_tp(curr, smp_processor_id(), tif);
}
void resched_curr(struct rq *rq)
{
__resched_curr(rq, TIF_NEED_RESCHED);
@@ -5279,7 +5285,7 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev)
* switched the context for the first time. It is returning from
* schedule for the first time in this path.
*/
trace_sched_exit_tp(true, CALLER_ADDR0);
trace_sched_exit_tp(true);
preempt_enable();
if (current->set_child_tid)
@@ -6822,7 +6828,8 @@ static void __sched notrace __schedule(int sched_mode)
struct rq *rq;
int cpu;
trace_sched_entry_tp(preempt, CALLER_ADDR0);
/* Trace preemptions consistently with task switches */
trace_sched_entry_tp(sched_mode == SM_PREEMPT);
cpu = smp_processor_id();
rq = cpu_rq(cpu);
@@ -6961,7 +6968,7 @@ keep_resched:
__balance_callbacks(rq);
raw_spin_rq_unlock_irq(rq);
}
trace_sched_exit_tp(is_switch, CALLER_ADDR0);
trace_sched_exit_tp(is_switch);
}
void __noreturn do_task_dead(void)
+37 -6
View File
@@ -1,19 +1,31 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config DA_MON_EVENTS
config RV_MON_EVENTS
bool
config RV_MON_MAINTENANCE_EVENTS
bool
config DA_MON_EVENTS_IMPLICIT
select DA_MON_EVENTS
select RV_MON_EVENTS
select RV_MON_MAINTENANCE_EVENTS
bool
config DA_MON_EVENTS_ID
select DA_MON_EVENTS
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
menuconfig RV
bool "Runtime Verification"
depends on TRACING
select TRACING
help
Enable the kernel runtime verification infrastructure. RV is a
lightweight (yet rigorous) method that complements classical
@@ -25,15 +37,34 @@ menuconfig RV
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/tss/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/sncid/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
# Add new monitors here
config RV_REACTORS
+7 -2
View File
@@ -6,12 +6,17 @@ 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_TSS) += monitors/tss/tss.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_SNCID) += monitors/sncid/sncid.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
# Add new monitors here
obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
@@ -1,14 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_TSS
config RV_MON_NRP
depends on RV
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
bool "tss monitor"
default y if !ARM64
select DA_MON_EVENTS_ID
bool "nrp monitor"
help
Monitor to ensure sched_switch happens only in scheduling context.
Monitor to ensure preemption requires need resched.
This monitor is part of the sched monitors collection.
This monitor is unstable on arm64, say N unless you are testing it.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
+138
View File
@@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
#include <rv/da_monitor.h>
#define MODULE_NAME "nrp"
#include <trace/events/irq.h>
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#include "nrp.h"
static struct rv_monitor rv_nrp;
DECLARE_DA_MON_PER_TASK(nrp, unsigned char);
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
da_handle_event_nrp(current, irq_entry_nrp);
}
static void attach_vector_irq(void)
{
rv_attach_trace_probe("nrp", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_attach_trace_probe("nrp", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_attach_trace_probe("nrp", reschedule_entry, handle_vector_irq_entry);
rv_attach_trace_probe("nrp", call_function_entry, handle_vector_irq_entry);
rv_attach_trace_probe("nrp", call_function_single_entry, handle_vector_irq_entry);
}
}
static void detach_vector_irq(void)
{
rv_detach_trace_probe("nrp", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_detach_trace_probe("nrp", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_detach_trace_probe("nrp", reschedule_entry, handle_vector_irq_entry);
rv_detach_trace_probe("nrp", call_function_entry, handle_vector_irq_entry);
rv_detach_trace_probe("nrp", call_function_single_entry, handle_vector_irq_entry);
}
}
#else
/* We assume irq_entry tracepoints are sufficient on other architectures */
static void attach_vector_irq(void) { }
static void detach_vector_irq(void) { }
#endif
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
da_handle_event_nrp(current, irq_entry_nrp);
}
static void handle_sched_need_resched(void *data, struct task_struct *tsk,
int cpu, int tif)
{
/*
* Although need_resched leads to both the rescheduling and preempt_irq
* states, it is safer to start the monitor always in preempt_irq,
* which may not mirror the system state but makes the monitor simpler,
*/
if (tif == TIF_NEED_RESCHED)
da_handle_start_event_nrp(tsk, sched_need_resched_nrp);
}
static void handle_schedule_entry(void *data, bool preempt)
{
if (preempt)
da_handle_event_nrp(current, schedule_entry_preempt_nrp);
else
da_handle_event_nrp(current, schedule_entry_nrp);
}
static int enable_nrp(void)
{
int retval;
retval = da_monitor_init_nrp();
if (retval)
return retval;
rv_attach_trace_probe("nrp", irq_handler_entry, handle_irq_entry);
rv_attach_trace_probe("nrp", sched_set_need_resched_tp, handle_sched_need_resched);
rv_attach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry);
attach_vector_irq();
return 0;
}
static void disable_nrp(void)
{
rv_nrp.enabled = 0;
rv_detach_trace_probe("nrp", irq_handler_entry, handle_irq_entry);
rv_detach_trace_probe("nrp", sched_set_need_resched_tp, handle_sched_need_resched);
rv_detach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry);
detach_vector_irq();
da_monitor_destroy_nrp();
}
static struct rv_monitor rv_nrp = {
.name = "nrp",
.description = "need resched preempts.",
.enable = enable_nrp,
.disable = disable_nrp,
.reset = da_monitor_reset_all_nrp,
.enabled = 0,
};
static int __init register_nrp(void)
{
return rv_register_monitor(&rv_nrp, &rv_sched);
}
static void __exit unregister_nrp(void)
{
rv_unregister_monitor(&rv_nrp);
}
module_init(register_nrp);
module_exit(unregister_nrp);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("nrp: need resched preempts.");
+75
View File
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of nrp automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
enum states_nrp {
preempt_irq_nrp = 0,
any_thread_running_nrp,
nested_preempt_nrp,
rescheduling_nrp,
state_max_nrp
};
#define INVALID_STATE state_max_nrp
enum events_nrp {
irq_entry_nrp = 0,
sched_need_resched_nrp,
schedule_entry_nrp,
schedule_entry_preempt_nrp,
event_max_nrp
};
struct automaton_nrp {
char *state_names[state_max_nrp];
char *event_names[event_max_nrp];
unsigned char function[state_max_nrp][event_max_nrp];
unsigned char initial_state;
bool final_states[state_max_nrp];
};
static const struct automaton_nrp automaton_nrp = {
.state_names = {
"preempt_irq",
"any_thread_running",
"nested_preempt",
"rescheduling"
},
.event_names = {
"irq_entry",
"sched_need_resched",
"schedule_entry",
"schedule_entry_preempt"
},
.function = {
{
preempt_irq_nrp,
preempt_irq_nrp,
nested_preempt_nrp,
nested_preempt_nrp
},
{
any_thread_running_nrp,
rescheduling_nrp,
any_thread_running_nrp,
INVALID_STATE
},
{
nested_preempt_nrp,
preempt_irq_nrp,
any_thread_running_nrp,
any_thread_running_nrp
},
{
preempt_irq_nrp,
rescheduling_nrp,
any_thread_running_nrp,
any_thread_running_nrp
},
},
.initial_state = preempt_irq_nrp,
.final_states = { 0, 1, 0, 0 },
};
+15
View File
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_NRP
DEFINE_EVENT(event_da_monitor_id, event_nrp,
TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
TP_ARGS(id, state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor_id, error_nrp,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
#endif /* CONFIG_RV_MON_NRP */
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_OPID
depends on RV
depends on TRACE_IRQFLAGS
depends on TRACE_PREEMPT_TOGGLE
depends on RV_MON_SCHED
default y if PREEMPT_RT
select DA_MON_EVENTS_IMPLICIT
bool "opid monitor"
help
Monitor to ensure operations like wakeup and need resched occur with
interrupts and preemption disabled or during IRQs, where preemption
may not be disabled explicitly.
This monitor is unstable on !PREEMPT_RT, say N unless you are testing it.
For further information, see:
Documentation/trace/rv/monitor_sched.rst
+168
View File
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
#include <rv/da_monitor.h>
#define MODULE_NAME "opid"
#include <trace/events/sched.h>
#include <trace/events/irq.h>
#include <trace/events/preemptirq.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#include "opid.h"
static struct rv_monitor rv_opid;
DECLARE_DA_MON_PER_CPU(opid, unsigned char);
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
da_handle_event_opid(irq_entry_opid);
}
static void attach_vector_irq(void)
{
rv_attach_trace_probe("opid", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_attach_trace_probe("opid", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_attach_trace_probe("opid", reschedule_entry, handle_vector_irq_entry);
rv_attach_trace_probe("opid", call_function_entry, handle_vector_irq_entry);
rv_attach_trace_probe("opid", call_function_single_entry, handle_vector_irq_entry);
}
}
static void detach_vector_irq(void)
{
rv_detach_trace_probe("opid", local_timer_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_IRQ_WORK))
rv_detach_trace_probe("opid", irq_work_entry, handle_vector_irq_entry);
if (IS_ENABLED(CONFIG_SMP)) {
rv_detach_trace_probe("opid", reschedule_entry, handle_vector_irq_entry);
rv_detach_trace_probe("opid", call_function_entry, handle_vector_irq_entry);
rv_detach_trace_probe("opid", 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_opid(irq_disable_opid);
}
static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event_opid(irq_enable_opid);
}
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
da_handle_event_opid(irq_entry_opid);
}
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event_opid(preempt_disable_opid);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event_opid(preempt_enable_opid);
}
static void handle_sched_need_resched(void *data, struct task_struct *tsk, int cpu, int tif)
{
/* The monitor's intitial state is not in_irq */
if (this_cpu_read(hardirq_context))
da_handle_event_opid(sched_need_resched_opid);
else
da_handle_start_event_opid(sched_need_resched_opid);
}
static void handle_sched_waking(void *data, struct task_struct *p)
{
/* The monitor's intitial state is not in_irq */
if (this_cpu_read(hardirq_context))
da_handle_event_opid(sched_waking_opid);
else
da_handle_start_event_opid(sched_waking_opid);
}
static int enable_opid(void)
{
int retval;
retval = da_monitor_init_opid();
if (retval)
return retval;
rv_attach_trace_probe("opid", irq_disable, handle_irq_disable);
rv_attach_trace_probe("opid", irq_enable, handle_irq_enable);
rv_attach_trace_probe("opid", irq_handler_entry, handle_irq_entry);
rv_attach_trace_probe("opid", preempt_disable, handle_preempt_disable);
rv_attach_trace_probe("opid", preempt_enable, handle_preempt_enable);
rv_attach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
rv_attach_trace_probe("opid", sched_waking, handle_sched_waking);
attach_vector_irq();
return 0;
}
static void disable_opid(void)
{
rv_opid.enabled = 0;
rv_detach_trace_probe("opid", irq_disable, handle_irq_disable);
rv_detach_trace_probe("opid", irq_enable, handle_irq_enable);
rv_detach_trace_probe("opid", irq_handler_entry, handle_irq_entry);
rv_detach_trace_probe("opid", preempt_disable, handle_preempt_disable);
rv_detach_trace_probe("opid", preempt_enable, handle_preempt_enable);
rv_detach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
rv_detach_trace_probe("opid", sched_waking, handle_sched_waking);
detach_vector_irq();
da_monitor_destroy_opid();
}
/*
* This is the monitor register section.
*/
static struct rv_monitor rv_opid = {
.name = "opid",
.description = "operations with preemption and irq disabled.",
.enable = enable_opid,
.disable = disable_opid,
.reset = da_monitor_reset_all_opid,
.enabled = 0,
};
static int __init register_opid(void)
{
return rv_register_monitor(&rv_opid, &rv_sched);
}
static void __exit unregister_opid(void)
{
rv_unregister_monitor(&rv_opid);
}
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.");
+104
View File
@@ -0,0 +1,104 @@
/* 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
*/
enum states_opid {
disabled_opid = 0,
enabled_opid,
in_irq_opid,
irq_disabled_opid,
preempt_disabled_opid,
state_max_opid
};
#define INVALID_STATE state_max_opid
enum events_opid {
irq_disable_opid = 0,
irq_enable_opid,
irq_entry_opid,
preempt_disable_opid,
preempt_enable_opid,
sched_need_resched_opid,
sched_waking_opid,
event_max_opid
};
struct automaton_opid {
char *state_names[state_max_opid];
char *event_names[event_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 = {
"disabled",
"enabled",
"in_irq",
"irq_disabled",
"preempt_disabled"
},
.event_names = {
"irq_disable",
"irq_enable",
"irq_entry",
"preempt_disable",
"preempt_enable",
"sched_need_resched",
"sched_waking"
},
.function = {
{
INVALID_STATE,
preempt_disabled_opid,
disabled_opid,
INVALID_STATE,
irq_disabled_opid,
disabled_opid,
disabled_opid
},
{
irq_disabled_opid,
INVALID_STATE,
INVALID_STATE,
preempt_disabled_opid,
enabled_opid,
INVALID_STATE,
INVALID_STATE
},
{
INVALID_STATE,
enabled_opid,
in_irq_opid,
INVALID_STATE,
INVALID_STATE,
in_irq_opid,
in_irq_opid
},
{
INVALID_STATE,
enabled_opid,
in_irq_opid,
disabled_opid,
INVALID_STATE,
irq_disabled_opid,
INVALID_STATE
},
{
disabled_opid,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
enabled_opid,
INVALID_STATE,
INVALID_STATE
},
},
.initial_state = disabled_opid,
.final_states = { 0, 1, 0, 0, 0 },
};
@@ -4,12 +4,12 @@
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SNCID
DEFINE_EVENT(event_da_monitor, event_sncid,
#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_sncid,
DEFINE_EVENT(error_da_monitor, error_opid,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
#endif /* CONFIG_RV_MON_SNCID */
#endif /* CONFIG_RV_MON_OPID */
@@ -0,0 +1,20 @@
# 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
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 */
+11
View File
@@ -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.
+33
View File
@@ -0,0 +1,33 @@
// 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;
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");
+3
View File
@@ -0,0 +1,3 @@
/* SPDX-License-Identifier: GPL-2.0 */
extern struct rv_monitor rv_rtapp;
+1
View File
@@ -2,6 +2,7 @@
#
config RV_MON_SCHED
depends on RV
depends on RV_PER_TASK_MONITORS >= 3
bool "sched monitor"
help
Collection of monitors to check the scheduler behaves according to specifications.
+1 -2
View File
@@ -21,8 +21,7 @@ struct rv_monitor rv_sched = {
static int __init register_sched(void)
{
rv_register_monitor(&rv_sched, NULL);
return 0;
return rv_register_monitor(&rv_sched, NULL);
}
static void __exit unregister_sched(void)
+3 -4
View File
@@ -24,12 +24,12 @@ static void handle_sched_set_state(void *data, struct task_struct *tsk, int stat
da_handle_start_event_sco(sched_set_state_sco);
}
static void handle_schedule_entry(void *data, bool preempt, unsigned long ip)
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event_sco(schedule_entry_sco);
}
static void handle_schedule_exit(void *data, bool is_switch, unsigned long ip)
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_start_event_sco(schedule_exit_sco);
}
@@ -71,8 +71,7 @@ static struct rv_monitor rv_sco = {
static int __init register_sco(void)
{
rv_register_monitor(&rv_sco, &rv_sched);
return 0;
return rv_register_monitor(&rv_sco, &rv_sched);
}
static void __exit unregister_sco(void)
+1 -1
View File
@@ -2,7 +2,7 @@
#
config RV_MON_SCPD
depends on RV
depends on PREEMPT_TRACER
depends on TRACE_PREEMPT_TOGGLE
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
+3 -4
View File
@@ -30,12 +30,12 @@ static void handle_preempt_enable(void *data, unsigned long ip, unsigned long pa
da_handle_start_event_scpd(preempt_enable_scpd);
}
static void handle_schedule_entry(void *data, bool preempt, unsigned long ip)
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event_scpd(schedule_entry_scpd);
}
static void handle_schedule_exit(void *data, bool is_switch, unsigned long ip)
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_event_scpd(schedule_exit_scpd);
}
@@ -79,8 +79,7 @@ static struct rv_monitor rv_scpd = {
static int __init register_scpd(void)
{
rv_register_monitor(&rv_scpd, &rv_sched);
return 0;
return rv_register_monitor(&rv_scpd, &rv_sched);
}
static void __exit unregister_scpd(void)
+22
View File
@@ -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.
+237
View File
@@ -0,0 +1,237 @@
// 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_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);
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) {
case __NR_clock_nanosleep:
#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;
case __NR_futex:
#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;
}
}
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_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");
+257
View File
@@ -0,0 +1,257 @@
/* 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_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",
"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 task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
bool val40 = task_is_rcu || task_is_migration;
bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
bool val41 = futex_lock_pi || val40;
bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
bool val5 = block_on_rt_mutex || val41;
bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon->atoms);
bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
bool val32 = abort_sleep || kthread_should_stop;
bool woken_by_nmi = test_bit(LTL_WOKEN_BY_NMI, mon->atoms);
bool val33 = woken_by_nmi || val32;
bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
bool val34 = woken_by_hardirq || val33;
bool woken_by_equal_or_higher_prio = test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
mon->atoms);
bool val14 = woken_by_equal_or_higher_prio || val34;
bool wake = test_bit(LTL_WAKE, mon->atoms);
bool val13 = !wake;
bool kernel_thread = test_bit(LTL_KERNEL_THREAD, 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 val24 = nanosleep_clock_monotonic || nanosleep_clock_tai;
bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME, mon->atoms);
bool val25 = nanosleep_timer_abstime && val24;
bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
bool val18 = clock_nanosleep && val25;
bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
bool val9 = futex_wait || val18;
bool val11 = val9 || kernel_thread;
bool sleep = test_bit(LTL_SLEEP, mon->atoms);
bool val2 = !sleep;
bool rt = test_bit(LTL_RT, mon->atoms);
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 task_is_migration = test_bit(LTL_TASK_IS_MIGRATION, mon->atoms);
bool task_is_rcu = test_bit(LTL_TASK_IS_RCU, mon->atoms);
bool val40 = task_is_rcu || task_is_migration;
bool futex_lock_pi = test_bit(LTL_FUTEX_LOCK_PI, mon->atoms);
bool val41 = futex_lock_pi || val40;
bool block_on_rt_mutex = test_bit(LTL_BLOCK_ON_RT_MUTEX, mon->atoms);
bool val5 = block_on_rt_mutex || val41;
bool kthread_should_stop = test_bit(LTL_KTHREAD_SHOULD_STOP, mon->atoms);
bool abort_sleep = test_bit(LTL_ABORT_SLEEP, mon->atoms);
bool val32 = abort_sleep || kthread_should_stop;
bool woken_by_nmi = test_bit(LTL_WOKEN_BY_NMI, mon->atoms);
bool val33 = woken_by_nmi || val32;
bool woken_by_hardirq = test_bit(LTL_WOKEN_BY_HARDIRQ, mon->atoms);
bool val34 = woken_by_hardirq || val33;
bool woken_by_equal_or_higher_prio = test_bit(LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO,
mon->atoms);
bool val14 = woken_by_equal_or_higher_prio || val34;
bool wake = test_bit(LTL_WAKE, mon->atoms);
bool val13 = !wake;
bool kernel_thread = test_bit(LTL_KERNEL_THREAD, 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 val24 = nanosleep_clock_monotonic || nanosleep_clock_tai;
bool nanosleep_timer_abstime = test_bit(LTL_NANOSLEEP_TIMER_ABSTIME, mon->atoms);
bool val25 = nanosleep_timer_abstime && val24;
bool clock_nanosleep = test_bit(LTL_CLOCK_NANOSLEEP, mon->atoms);
bool val18 = clock_nanosleep && val25;
bool futex_wait = test_bit(LTL_FUTEX_WAIT, mon->atoms);
bool val9 = futex_wait || val18;
bool val11 = val9 || kernel_thread;
bool sleep = test_bit(LTL_SLEEP, mon->atoms);
bool val2 = !sleep;
bool rt = test_bit(LTL_RT, mon->atoms);
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 */
-96
View File
@@ -1,96 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
#include <rv/da_monitor.h>
#define MODULE_NAME "sncid"
#include <trace/events/sched.h>
#include <trace/events/preemptirq.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#include "sncid.h"
static struct rv_monitor rv_sncid;
DECLARE_DA_MON_PER_CPU(sncid, unsigned char);
static void handle_irq_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event_sncid(irq_disable_sncid);
}
static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_start_event_sncid(irq_enable_sncid);
}
static void handle_schedule_entry(void *data, bool preempt, unsigned long ip)
{
da_handle_start_event_sncid(schedule_entry_sncid);
}
static void handle_schedule_exit(void *data, bool is_switch, unsigned long ip)
{
da_handle_start_event_sncid(schedule_exit_sncid);
}
static int enable_sncid(void)
{
int retval;
retval = da_monitor_init_sncid();
if (retval)
return retval;
rv_attach_trace_probe("sncid", irq_disable, handle_irq_disable);
rv_attach_trace_probe("sncid", irq_enable, handle_irq_enable);
rv_attach_trace_probe("sncid", sched_entry_tp, handle_schedule_entry);
rv_attach_trace_probe("sncid", sched_exit_tp, handle_schedule_exit);
return 0;
}
static void disable_sncid(void)
{
rv_sncid.enabled = 0;
rv_detach_trace_probe("sncid", irq_disable, handle_irq_disable);
rv_detach_trace_probe("sncid", irq_enable, handle_irq_enable);
rv_detach_trace_probe("sncid", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("sncid", sched_exit_tp, handle_schedule_exit);
da_monitor_destroy_sncid();
}
static struct rv_monitor rv_sncid = {
.name = "sncid",
.description = "schedule not called with interrupt disabled.",
.enable = enable_sncid,
.disable = disable_sncid,
.reset = da_monitor_reset_all_sncid,
.enabled = 0,
};
static int __init register_sncid(void)
{
rv_register_monitor(&rv_sncid, &rv_sched);
return 0;
}
static void __exit unregister_sncid(void)
{
rv_unregister_monitor(&rv_sncid);
}
module_init(register_sncid);
module_exit(unregister_sncid);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sncid: schedule not called with interrupt disabled.");
-49
View File
@@ -1,49 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of sncid automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
enum states_sncid {
can_sched_sncid = 0,
cant_sched_sncid,
state_max_sncid
};
#define INVALID_STATE state_max_sncid
enum events_sncid {
irq_disable_sncid = 0,
irq_enable_sncid,
schedule_entry_sncid,
schedule_exit_sncid,
event_max_sncid
};
struct automaton_sncid {
char *state_names[state_max_sncid];
char *event_names[event_max_sncid];
unsigned char function[state_max_sncid][event_max_sncid];
unsigned char initial_state;
bool final_states[state_max_sncid];
};
static const struct automaton_sncid automaton_sncid = {
.state_names = {
"can_sched",
"cant_sched"
},
.event_names = {
"irq_disable",
"irq_enable",
"schedule_entry",
"schedule_exit"
},
.function = {
{ cant_sched_sncid, INVALID_STATE, can_sched_sncid, can_sched_sncid },
{ INVALID_STATE, can_sched_sncid, INVALID_STATE, INVALID_STATE },
},
.initial_state = can_sched_sncid,
.final_states = { 1, 0 },
};
+1 -1
View File
@@ -2,7 +2,7 @@
#
config RV_MON_SNEP
depends on RV
depends on PREEMPT_TRACER
depends on TRACE_PREEMPT_TOGGLE
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
+3 -4
View File
@@ -30,12 +30,12 @@ static void handle_preempt_enable(void *data, unsigned long ip, unsigned long pa
da_handle_start_event_snep(preempt_enable_snep);
}
static void handle_schedule_entry(void *data, bool preempt, unsigned long ip)
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event_snep(schedule_entry_snep);
}
static void handle_schedule_exit(void *data, bool is_switch, unsigned long ip)
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_start_event_snep(schedule_exit_snep);
}
@@ -79,8 +79,7 @@ static struct rv_monitor rv_snep = {
static int __init register_snep(void)
{
rv_register_monitor(&rv_snep, &rv_sched);
return 0;
return rv_register_monitor(&rv_snep, &rv_sched);
}
static void __exit unregister_snep(void)
+12 -2
View File
@@ -41,8 +41,18 @@ static const struct automaton_snep automaton_snep = {
"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 },
{
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 },
+1 -2
View File
@@ -68,8 +68,7 @@ static struct rv_monitor rv_snroc = {
static int __init register_snroc(void)
{
rv_register_monitor(&rv_snroc, &rv_sched);
return 0;
return rv_register_monitor(&rv_snroc, &rv_sched);
}
static void __exit unregister_snroc(void)
@@ -1,14 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_SNCID
config RV_MON_SSSW
depends on RV
depends on IRQSOFF_TRACER
depends on RV_MON_SCHED
default y
select DA_MON_EVENTS_IMPLICIT
bool "sncid monitor"
select DA_MON_EVENTS_ID
bool "sssw monitor"
help
Monitor to ensure schedule is not called with interrupt disabled.
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:
+116
View File
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
#include <rv/da_monitor.h>
#define MODULE_NAME "sssw"
#include <trace/events/sched.h>
#include <trace/events/signal.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#include "sssw.h"
static struct rv_monitor rv_sssw;
DECLARE_DA_MON_PER_TASK(sssw, unsigned char);
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
if (state == TASK_RUNNING)
da_handle_start_event_sssw(tsk, sched_set_state_runnable_sssw);
else
da_handle_event_sssw(tsk, sched_set_state_sleepable_sssw);
}
static void handle_sched_switch(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
{
if (preempt)
da_handle_event_sssw(prev, sched_switch_preempt_sssw);
else if (prev_state == TASK_RUNNING)
da_handle_event_sssw(prev, sched_switch_yield_sssw);
else if (prev_state == TASK_RTLOCK_WAIT)
/* special case of sleeping task with racy conditions */
da_handle_event_sssw(prev, sched_switch_blocking_sssw);
else
da_handle_event_sssw(prev, sched_switch_suspend_sssw);
da_handle_event_sssw(next, sched_switch_in_sssw);
}
static void handle_sched_wakeup(void *data, struct task_struct *p)
{
/*
* Wakeup can also lead to signal_wakeup although the system is
* actually runnable. The monitor can safely start with this event.
*/
da_handle_start_event_sssw(p, sched_wakeup_sssw);
}
static void handle_signal_deliver(void *data, int sig,
struct kernel_siginfo *info,
struct k_sigaction *ka)
{
da_handle_event_sssw(current, signal_deliver_sssw);
}
static int enable_sssw(void)
{
int retval;
retval = da_monitor_init_sssw();
if (retval)
return retval;
rv_attach_trace_probe("sssw", sched_set_state_tp, handle_sched_set_state);
rv_attach_trace_probe("sssw", sched_switch, handle_sched_switch);
rv_attach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup);
rv_attach_trace_probe("sssw", signal_deliver, handle_signal_deliver);
return 0;
}
static void disable_sssw(void)
{
rv_sssw.enabled = 0;
rv_detach_trace_probe("sssw", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("sssw", sched_switch, handle_sched_switch);
rv_detach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup);
rv_detach_trace_probe("sssw", signal_deliver, handle_signal_deliver);
da_monitor_destroy_sssw();
}
static struct rv_monitor rv_sssw = {
.name = "sssw",
.description = "set state sleep and wakeup.",
.enable = enable_sssw,
.disable = disable_sssw,
.reset = da_monitor_reset_all_sssw,
.enabled = 0,
};
static int __init register_sssw(void)
{
return rv_register_monitor(&rv_sssw, &rv_sched);
}
static void __exit unregister_sssw(void)
{
rv_unregister_monitor(&rv_sssw);
}
module_init(register_sssw);
module_exit(unregister_sssw);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sssw: set state sleep and wakeup.");
+105
View File
@@ -0,0 +1,105 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of sssw automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
enum states_sssw {
runnable_sssw = 0,
signal_wakeup_sssw,
sleepable_sssw,
sleeping_sssw,
state_max_sssw
};
#define INVALID_STATE state_max_sssw
enum events_sssw {
sched_set_state_runnable_sssw = 0,
sched_set_state_sleepable_sssw,
sched_switch_blocking_sssw,
sched_switch_in_sssw,
sched_switch_preempt_sssw,
sched_switch_suspend_sssw,
sched_switch_yield_sssw,
sched_wakeup_sssw,
signal_deliver_sssw,
event_max_sssw
};
struct automaton_sssw {
char *state_names[state_max_sssw];
char *event_names[event_max_sssw];
unsigned char function[state_max_sssw][event_max_sssw];
unsigned char initial_state;
bool final_states[state_max_sssw];
};
static const struct automaton_sssw automaton_sssw = {
.state_names = {
"runnable",
"signal_wakeup",
"sleepable",
"sleeping"
},
.event_names = {
"sched_set_state_runnable",
"sched_set_state_sleepable",
"sched_switch_blocking",
"sched_switch_in",
"sched_switch_preempt",
"sched_switch_suspend",
"sched_switch_yield",
"sched_wakeup",
"signal_deliver"
},
.function = {
{
runnable_sssw,
sleepable_sssw,
sleeping_sssw,
runnable_sssw,
runnable_sssw,
INVALID_STATE,
runnable_sssw,
runnable_sssw,
runnable_sssw
},
{
INVALID_STATE,
sleepable_sssw,
INVALID_STATE,
signal_wakeup_sssw,
signal_wakeup_sssw,
INVALID_STATE,
signal_wakeup_sssw,
signal_wakeup_sssw,
runnable_sssw
},
{
runnable_sssw,
sleepable_sssw,
sleeping_sssw,
sleepable_sssw,
sleepable_sssw,
sleeping_sssw,
signal_wakeup_sssw,
runnable_sssw,
sleepable_sssw
},
{
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
INVALID_STATE,
runnable_sssw,
INVALID_STATE
},
},
.initial_state = runnable_sssw,
.final_states = { 1, 0, 0, 0 },
};
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_SSSW
DEFINE_EVENT(event_da_monitor_id, event_sssw,
TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
TP_ARGS(id, state, event, next_state, final_state));
DEFINE_EVENT(error_da_monitor_id, error_sssw,
TP_PROTO(int id, char *state, char *event),
TP_ARGS(id, state, event));
#endif /* CONFIG_RV_MON_SSSW */
+19
View File
@@ -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
+156
View File
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
#include <rv/da_monitor.h>
#define MODULE_NAME "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>
#include "sts.h"
static struct rv_monitor rv_sts;
DECLARE_DA_MON_PER_CPU(sts, unsigned char);
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
da_handle_event_sts(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_sts(irq_disable_sts);
}
static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
da_handle_event_sts(irq_enable_sts);
}
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
da_handle_event_sts(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_sts(sched_switch_sts);
}
static void handle_schedule_entry(void *data, bool preempt)
{
da_handle_event_sts(schedule_entry_sts);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
da_handle_start_event_sts(schedule_exit_sts);
}
static int enable_sts(void)
{
int retval;
retval = da_monitor_init_sts();
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_sts.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_sts();
}
/*
* This is the monitor register section.
*/
static struct rv_monitor rv_sts = {
.name = "sts",
.description = "schedule implies task switch.",
.enable = enable_sts,
.disable = disable_sts,
.reset = da_monitor_reset_all_sts,
.enabled = 0,
};
static int __init register_sts(void)
{
return rv_register_monitor(&rv_sts, &rv_sched);
}
static void __exit unregister_sts(void)
{
rv_unregister_monitor(&rv_sts);
}
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.");
+117
View File
@@ -0,0 +1,117 @@
/* 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
*/
enum states_sts {
can_sched_sts = 0,
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 = 0,
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 },
};
@@ -4,12 +4,12 @@
* Snippet to be included in rv_trace.h
*/
#ifdef CONFIG_RV_MON_TSS
DEFINE_EVENT(event_da_monitor, event_tss,
#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_tss,
DEFINE_EVENT(error_da_monitor, error_sts,
TP_PROTO(char *state, char *event),
TP_ARGS(state, event));
#endif /* CONFIG_RV_MON_TSS */
#endif /* CONFIG_RV_MON_STS */
-91
View File
@@ -1,91 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
#include <rv/da_monitor.h>
#define MODULE_NAME "tss"
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#include "tss.h"
static struct rv_monitor rv_tss;
DECLARE_DA_MON_PER_CPU(tss, unsigned char);
static void handle_sched_switch(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
{
da_handle_event_tss(sched_switch_tss);
}
static void handle_schedule_entry(void *data, bool preempt, unsigned long ip)
{
da_handle_event_tss(schedule_entry_tss);
}
static void handle_schedule_exit(void *data, bool is_switch, unsigned long ip)
{
da_handle_start_event_tss(schedule_exit_tss);
}
static int enable_tss(void)
{
int retval;
retval = da_monitor_init_tss();
if (retval)
return retval;
rv_attach_trace_probe("tss", sched_switch, handle_sched_switch);
rv_attach_trace_probe("tss", sched_entry_tp, handle_schedule_entry);
rv_attach_trace_probe("tss", sched_exit_tp, handle_schedule_exit);
return 0;
}
static void disable_tss(void)
{
rv_tss.enabled = 0;
rv_detach_trace_probe("tss", sched_switch, handle_sched_switch);
rv_detach_trace_probe("tss", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("tss", sched_exit_tp, handle_schedule_exit);
da_monitor_destroy_tss();
}
static struct rv_monitor rv_tss = {
.name = "tss",
.description = "task switch while scheduling.",
.enable = enable_tss,
.disable = disable_tss,
.reset = da_monitor_reset_all_tss,
.enabled = 0,
};
static int __init register_tss(void)
{
rv_register_monitor(&rv_tss, &rv_sched);
return 0;
}
static void __exit unregister_tss(void)
{
rv_unregister_monitor(&rv_tss);
}
module_init(register_tss);
module_exit(unregister_tss);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("tss: task switch while scheduling.");
-47
View File
@@ -1,47 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Automatically generated C representation of tss automaton
* For further information about this format, see kernel documentation:
* Documentation/trace/rv/deterministic_automata.rst
*/
enum states_tss {
thread_tss = 0,
sched_tss,
state_max_tss
};
#define INVALID_STATE state_max_tss
enum events_tss {
sched_switch_tss = 0,
schedule_entry_tss,
schedule_exit_tss,
event_max_tss
};
struct automaton_tss {
char *state_names[state_max_tss];
char *event_names[event_max_tss];
unsigned char function[state_max_tss][event_max_tss];
unsigned char initial_state;
bool final_states[state_max_tss];
};
static const struct automaton_tss automaton_tss = {
.state_names = {
"thread",
"sched"
},
.event_names = {
"sched_switch",
"schedule_entry",
"schedule_exit"
},
.function = {
{ INVALID_STATE, sched_tss, INVALID_STATE },
{ sched_tss, INVALID_STATE, thread_tss },
},
.initial_state = thread_tss,
.final_states = { 1, 0 },
};
+1 -1
View File
@@ -2,7 +2,7 @@
#
config RV_MON_WIP
depends on RV
depends on PREEMPT_TRACER
depends on TRACE_PREEMPT_TOGGLE
select DA_MON_EVENTS_IMPLICIT
bool "wip monitor"
help
+1 -2
View File
@@ -71,8 +71,7 @@ static struct rv_monitor rv_wip = {
static int __init register_wip(void)
{
rv_register_monitor(&rv_wip, NULL);
return 0;
return rv_register_monitor(&rv_wip, NULL);
}
static void __exit unregister_wip(void)
+1 -2
View File
@@ -70,8 +70,7 @@ static struct rv_monitor rv_wwnr = {
static int __init register_wwnr(void)
{
rv_register_monitor(&rv_wwnr, NULL);
return 0;
return rv_register_monitor(&rv_wwnr, NULL);
}
static void __exit unregister_wwnr(void)
+6 -2
View File
@@ -13,9 +13,13 @@
#include <linux/init.h>
#include <linux/rv.h>
static void rv_panic_reaction(char *msg)
__printf(1, 2) static void rv_panic_reaction(const char *msg, ...)
{
panic(msg);
va_list args;
va_start(args, msg);
vpanic(msg, args);
va_end(args);
}
static struct rv_reactor rv_panic = {
+6 -2
View File
@@ -12,9 +12,13 @@
#include <linux/init.h>
#include <linux/rv.h>
static void rv_printk_reaction(char *msg)
__printf(1, 2) static void rv_printk_reaction(const char *msg, ...)
{
printk_deferred(msg);
va_list args;
va_start(args, msg);
vprintk_deferred(msg, args);
va_end(args);
}
static struct rv_reactor rv_printk = {
+97 -123
View File
@@ -143,7 +143,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#ifdef CONFIG_DA_MON_EVENTS
#ifdef CONFIG_RV_MON_EVENTS
#define CREATE_TRACE_POINTS
#include <rv_trace.h>
#endif
@@ -165,7 +165,7 @@ struct dentry *get_monitors_root(void)
LIST_HEAD(rv_monitors_list);
static int task_monitor_count;
static bool task_monitor_slots[RV_PER_TASK_MONITORS];
static bool task_monitor_slots[CONFIG_RV_PER_TASK_MONITORS];
int rv_get_task_monitor_slot(void)
{
@@ -173,12 +173,12 @@ int rv_get_task_monitor_slot(void)
lockdep_assert_held(&rv_interface_lock);
if (task_monitor_count == RV_PER_TASK_MONITORS)
if (task_monitor_count == CONFIG_RV_PER_TASK_MONITORS)
return -EBUSY;
task_monitor_count++;
for (i = 0; i < RV_PER_TASK_MONITORS; i++) {
for (i = 0; i < CONFIG_RV_PER_TASK_MONITORS; i++) {
if (task_monitor_slots[i] == false) {
task_monitor_slots[i] = true;
return i;
@@ -194,7 +194,7 @@ void rv_put_task_monitor_slot(int slot)
{
lockdep_assert_held(&rv_interface_lock);
if (slot < 0 || slot >= RV_PER_TASK_MONITORS) {
if (slot < 0 || slot >= CONFIG_RV_PER_TASK_MONITORS) {
WARN_ONCE(1, "RV releasing an invalid slot!: %d\n", slot);
return;
}
@@ -210,9 +210,9 @@ void rv_put_task_monitor_slot(int slot)
* Monitors with a parent are nested,
* Monitors without a parent could be standalone or containers.
*/
bool rv_is_nested_monitor(struct rv_monitor_def *mdef)
bool rv_is_nested_monitor(struct rv_monitor *mon)
{
return mdef->parent != NULL;
return mon->parent != NULL;
}
/*
@@ -223,16 +223,16 @@ bool rv_is_nested_monitor(struct rv_monitor_def *mdef)
* 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_def *mdef)
bool rv_is_container_monitor(struct rv_monitor *mon)
{
struct rv_monitor_def *next;
struct rv_monitor *next;
if (list_is_last(&mdef->list, &rv_monitors_list))
if (list_is_last(&mon->list, &rv_monitors_list))
return false;
next = list_next_entry(mdef, list);
next = list_next_entry(mon, list);
return next->parent == mdef->monitor || !mdef->monitor->enable;
return next->parent == mon || !mon->enable;
}
/*
@@ -241,10 +241,10 @@ bool rv_is_container_monitor(struct rv_monitor_def *mdef)
static ssize_t monitor_enable_read_data(struct file *filp, char __user *user_buf, size_t count,
loff_t *ppos)
{
struct rv_monitor_def *mdef = filp->private_data;
struct rv_monitor *mon = filp->private_data;
const char *buff;
buff = mdef->monitor->enabled ? "1\n" : "0\n";
buff = mon->enabled ? "1\n" : "0\n";
return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff)+1);
}
@@ -252,14 +252,14 @@ static ssize_t monitor_enable_read_data(struct file *filp, char __user *user_buf
/*
* __rv_disable_monitor - disabled an enabled monitor
*/
static int __rv_disable_monitor(struct rv_monitor_def *mdef, bool sync)
static int __rv_disable_monitor(struct rv_monitor *mon, bool sync)
{
lockdep_assert_held(&rv_interface_lock);
if (mdef->monitor->enabled) {
mdef->monitor->enabled = 0;
if (mdef->monitor->disable)
mdef->monitor->disable();
if (mon->enabled) {
mon->enabled = 0;
if (mon->disable)
mon->disable();
/*
* Wait for the execution of all events to finish.
@@ -273,90 +273,90 @@ static int __rv_disable_monitor(struct rv_monitor_def *mdef, bool sync)
return 0;
}
static void rv_disable_single(struct rv_monitor_def *mdef)
static void rv_disable_single(struct rv_monitor *mon)
{
__rv_disable_monitor(mdef, true);
__rv_disable_monitor(mon, true);
}
static int rv_enable_single(struct rv_monitor_def *mdef)
static int rv_enable_single(struct rv_monitor *mon)
{
int retval;
lockdep_assert_held(&rv_interface_lock);
if (mdef->monitor->enabled)
if (mon->enabled)
return 0;
retval = mdef->monitor->enable();
retval = mon->enable();
if (!retval)
mdef->monitor->enabled = 1;
mon->enabled = 1;
return retval;
}
static void rv_disable_container(struct rv_monitor_def *mdef)
static void rv_disable_container(struct rv_monitor *mon)
{
struct rv_monitor_def *p = mdef;
struct rv_monitor *p = mon;
int enabled = 0;
list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (p->parent != mdef->monitor)
if (p->parent != mon)
break;
enabled += __rv_disable_monitor(p, false);
}
if (enabled)
tracepoint_synchronize_unregister();
mdef->monitor->enabled = 0;
mon->enabled = 0;
}
static int rv_enable_container(struct rv_monitor_def *mdef)
static int rv_enable_container(struct rv_monitor *mon)
{
struct rv_monitor_def *p = mdef;
struct rv_monitor *p = mon;
int retval = 0;
list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (retval || p->parent != mdef->monitor)
if (retval || p->parent != mon)
break;
retval = rv_enable_single(p);
}
if (retval)
rv_disable_container(mdef);
rv_disable_container(mon);
else
mdef->monitor->enabled = 1;
mon->enabled = 1;
return retval;
}
/**
* rv_disable_monitor - disable a given runtime monitor
* @mdef: Pointer to the monitor definition structure.
* @mon: Pointer to the monitor definition structure.
*
* Returns 0 on success.
*/
int rv_disable_monitor(struct rv_monitor_def *mdef)
int rv_disable_monitor(struct rv_monitor *mon)
{
if (rv_is_container_monitor(mdef))
rv_disable_container(mdef);
if (rv_is_container_monitor(mon))
rv_disable_container(mon);
else
rv_disable_single(mdef);
rv_disable_single(mon);
return 0;
}
/**
* rv_enable_monitor - enable a given runtime monitor
* @mdef: Pointer to the monitor definition structure.
* @mon: Pointer to the monitor definition structure.
*
* Returns 0 on success, error otherwise.
*/
int rv_enable_monitor(struct rv_monitor_def *mdef)
int rv_enable_monitor(struct rv_monitor *mon)
{
int retval;
if (rv_is_container_monitor(mdef))
retval = rv_enable_container(mdef);
if (rv_is_container_monitor(mon))
retval = rv_enable_container(mon);
else
retval = rv_enable_single(mdef);
retval = rv_enable_single(mon);
return retval;
}
@@ -367,7 +367,7 @@ int rv_enable_monitor(struct rv_monitor_def *mdef)
static ssize_t monitor_enable_write_data(struct file *filp, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct rv_monitor_def *mdef = filp->private_data;
struct rv_monitor *mon = filp->private_data;
int retval;
bool val;
@@ -378,9 +378,9 @@ static ssize_t monitor_enable_write_data(struct file *filp, const char __user *u
mutex_lock(&rv_interface_lock);
if (val)
retval = rv_enable_monitor(mdef);
retval = rv_enable_monitor(mon);
else
retval = rv_disable_monitor(mdef);
retval = rv_disable_monitor(mon);
mutex_unlock(&rv_interface_lock);
@@ -399,12 +399,12 @@ static const struct file_operations interface_enable_fops = {
static ssize_t monitor_desc_read_data(struct file *filp, char __user *user_buf, size_t count,
loff_t *ppos)
{
struct rv_monitor_def *mdef = filp->private_data;
struct rv_monitor *mon = filp->private_data;
char buff[256];
memset(buff, 0, sizeof(buff));
snprintf(buff, sizeof(buff), "%s\n", mdef->monitor->description);
snprintf(buff, sizeof(buff), "%s\n", mon->description);
return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff) + 1);
}
@@ -419,37 +419,37 @@ static const struct file_operations interface_desc_fops = {
* the monitor dir, where the specific options of the monitor
* are exposed.
*/
static int create_monitor_dir(struct rv_monitor_def *mdef, struct rv_monitor_def *parent)
static int create_monitor_dir(struct rv_monitor *mon, struct rv_monitor *parent)
{
struct dentry *root = parent ? parent->root_d : get_monitors_root();
const char *name = mdef->monitor->name;
const char *name = mon->name;
struct dentry *tmp;
int retval;
mdef->root_d = rv_create_dir(name, root);
if (!mdef->root_d)
mon->root_d = rv_create_dir(name, root);
if (!mon->root_d)
return -ENOMEM;
tmp = rv_create_file("enable", RV_MODE_WRITE, mdef->root_d, mdef, &interface_enable_fops);
tmp = rv_create_file("enable", RV_MODE_WRITE, mon->root_d, mon, &interface_enable_fops);
if (!tmp) {
retval = -ENOMEM;
goto out_remove_root;
}
tmp = rv_create_file("desc", RV_MODE_READ, mdef->root_d, mdef, &interface_desc_fops);
tmp = rv_create_file("desc", RV_MODE_READ, mon->root_d, mon, &interface_desc_fops);
if (!tmp) {
retval = -ENOMEM;
goto out_remove_root;
}
retval = reactor_populate_monitor(mdef);
retval = reactor_populate_monitor(mon);
if (retval)
goto out_remove_root;
return 0;
out_remove_root:
rv_remove(mdef->root_d);
rv_remove(mon->root_d);
return retval;
}
@@ -458,13 +458,12 @@ out_remove_root:
*/
static int monitors_show(struct seq_file *m, void *p)
{
struct rv_monitor_def *mon_def = p;
struct rv_monitor *mon = container_of(p, struct rv_monitor, list);
if (mon_def->parent)
seq_printf(m, "%s:%s\n", mon_def->parent->name,
mon_def->monitor->name);
if (mon->parent)
seq_printf(m, "%s:%s\n", mon->parent->name, mon->name);
else
seq_printf(m, "%s\n", mon_def->monitor->name);
seq_printf(m, "%s\n", mon->name);
return 0;
}
@@ -496,13 +495,13 @@ static void *available_monitors_next(struct seq_file *m, void *p, loff_t *pos)
*/
static void *enabled_monitors_next(struct seq_file *m, void *p, loff_t *pos)
{
struct rv_monitor_def *m_def = p;
struct rv_monitor *mon = p;
(*pos)++;
list_for_each_entry_continue(m_def, &rv_monitors_list, list) {
if (m_def->monitor->enabled)
return m_def;
list_for_each_entry_continue(mon, &rv_monitors_list, list) {
if (mon->enabled)
return mon;
}
return NULL;
@@ -510,7 +509,7 @@ static void *enabled_monitors_next(struct seq_file *m, void *p, loff_t *pos)
static void *enabled_monitors_start(struct seq_file *m, loff_t *pos)
{
struct rv_monitor_def *m_def;
struct rv_monitor *mon;
loff_t l;
mutex_lock(&rv_interface_lock);
@@ -518,15 +517,15 @@ static void *enabled_monitors_start(struct seq_file *m, loff_t *pos)
if (list_empty(&rv_monitors_list))
return NULL;
m_def = list_entry(&rv_monitors_list, struct rv_monitor_def, list);
mon = list_entry(&rv_monitors_list, struct rv_monitor, list);
for (l = 0; l <= *pos; ) {
m_def = enabled_monitors_next(m, m_def, &l);
if (!m_def)
mon = enabled_monitors_next(m, mon, &l);
if (!mon)
break;
}
return m_def;
return mon;
}
/*
@@ -566,13 +565,13 @@ static const struct file_operations available_monitors_ops = {
*/
static void disable_all_monitors(void)
{
struct rv_monitor_def *mdef;
struct rv_monitor *mon;
int enabled = 0;
mutex_lock(&rv_interface_lock);
list_for_each_entry(mdef, &rv_monitors_list, list)
enabled += __rv_disable_monitor(mdef, false);
list_for_each_entry(mon, &rv_monitors_list, list)
enabled += __rv_disable_monitor(mon, false);
if (enabled) {
/*
@@ -598,7 +597,7 @@ static ssize_t enabled_monitors_write(struct file *filp, const char __user *user
size_t count, loff_t *ppos)
{
char buff[MAX_RV_MONITOR_NAME_SIZE + 2];
struct rv_monitor_def *mdef;
struct rv_monitor *mon;
int retval = -EINVAL;
bool enable = true;
char *ptr, *tmp;
@@ -633,17 +632,17 @@ static ssize_t enabled_monitors_write(struct file *filp, const char __user *user
if (tmp)
ptr = tmp+1;
list_for_each_entry(mdef, &rv_monitors_list, list) {
if (strcmp(ptr, mdef->monitor->name) != 0)
list_for_each_entry(mon, &rv_monitors_list, list) {
if (strcmp(ptr, mon->name) != 0)
continue;
/*
* Monitor found!
*/
if (enable)
retval = rv_enable_monitor(mdef);
retval = rv_enable_monitor(mon);
else
retval = rv_disable_monitor(mdef);
retval = rv_disable_monitor(mon);
if (!retval)
retval = count;
@@ -702,11 +701,11 @@ static void turn_monitoring_off(void)
static void reset_all_monitors(void)
{
struct rv_monitor_def *mdef;
struct rv_monitor *mon;
list_for_each_entry(mdef, &rv_monitors_list, list) {
if (mdef->monitor->enabled && mdef->monitor->reset)
mdef->monitor->reset();
list_for_each_entry(mon, &rv_monitors_list, list) {
if (mon->enabled && mon->reset)
mon->reset();
}
}
@@ -768,10 +767,9 @@ static const struct file_operations monitoring_on_fops = {
.read = monitoring_on_read_data,
};
static void destroy_monitor_dir(struct rv_monitor_def *mdef)
static void destroy_monitor_dir(struct rv_monitor *mon)
{
reactor_cleanup_monitor(mdef);
rv_remove(mdef->root_d);
rv_remove(mon->root_d);
}
/**
@@ -783,7 +781,7 @@ static void destroy_monitor_dir(struct rv_monitor_def *mdef)
*/
int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent)
{
struct rv_monitor_def *r, *p = NULL;
struct rv_monitor *r;
int retval = 0;
if (strlen(monitor->name) >= MAX_RV_MONITOR_NAME_SIZE) {
@@ -795,49 +793,31 @@ int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent)
mutex_lock(&rv_interface_lock);
list_for_each_entry(r, &rv_monitors_list, list) {
if (strcmp(monitor->name, r->monitor->name) == 0) {
if (strcmp(monitor->name, r->name) == 0) {
pr_info("Monitor %s is already registered\n", monitor->name);
retval = -EEXIST;
goto out_unlock;
}
}
if (parent) {
list_for_each_entry(r, &rv_monitors_list, list) {
if (strcmp(parent->name, r->monitor->name) == 0) {
p = r;
break;
}
}
}
if (p && rv_is_nested_monitor(p)) {
if (parent && rv_is_nested_monitor(parent)) {
pr_info("Parent monitor %s is already nested, cannot nest further\n",
parent->name);
retval = -EINVAL;
goto out_unlock;
}
r = kzalloc(sizeof(struct rv_monitor_def), GFP_KERNEL);
if (!r) {
retval = -ENOMEM;
goto out_unlock;
}
monitor->parent = parent;
r->monitor = monitor;
r->parent = parent;
retval = create_monitor_dir(r, p);
if (retval) {
kfree(r);
goto out_unlock;
}
retval = create_monitor_dir(monitor, parent);
if (retval)
return retval;
/* keep children close to the parent for easier visualisation */
if (p)
list_add(&r->list, &p->list);
if (parent)
list_add(&monitor->list, &parent->list);
else
list_add_tail(&r->list, &rv_monitors_list);
list_add_tail(&monitor->list, &rv_monitors_list);
out_unlock:
mutex_unlock(&rv_interface_lock);
@@ -852,17 +832,11 @@ out_unlock:
*/
int rv_unregister_monitor(struct rv_monitor *monitor)
{
struct rv_monitor_def *ptr, *next;
mutex_lock(&rv_interface_lock);
list_for_each_entry_safe(ptr, next, &rv_monitors_list, list) {
if (strcmp(monitor->name, ptr->monitor->name) == 0) {
rv_disable_monitor(ptr);
list_del(&ptr->list);
destroy_monitor_dir(ptr);
}
}
rv_disable_monitor(monitor);
list_del(&monitor->list);
destroy_monitor_dir(monitor);
mutex_unlock(&rv_interface_lock);
return 0;
+6 -33
View File
@@ -23,48 +23,21 @@ struct rv_interface {
extern struct mutex rv_interface_lock;
extern struct list_head rv_monitors_list;
#ifdef CONFIG_RV_REACTORS
struct rv_reactor_def {
struct list_head list;
struct rv_reactor *reactor;
/* protected by the monitor interface lock */
int counter;
};
#endif
struct rv_monitor_def {
struct list_head list;
struct rv_monitor *monitor;
struct rv_monitor *parent;
struct dentry *root_d;
#ifdef CONFIG_RV_REACTORS
struct rv_reactor_def *rdef;
bool reacting;
#endif
bool task_monitor;
};
struct dentry *get_monitors_root(void);
int rv_disable_monitor(struct rv_monitor_def *mdef);
int rv_enable_monitor(struct rv_monitor_def *mdef);
bool rv_is_container_monitor(struct rv_monitor_def *mdef);
bool rv_is_nested_monitor(struct rv_monitor_def *mdef);
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_def *mdef);
void reactor_cleanup_monitor(struct rv_monitor_def *mdef);
int reactor_populate_monitor(struct rv_monitor *mon);
int init_rv_reactors(struct dentry *root_dir);
#else
static inline int reactor_populate_monitor(struct rv_monitor_def *mdef)
static inline int reactor_populate_monitor(struct rv_monitor *mon)
{
return 0;
}
static inline void reactor_cleanup_monitor(struct rv_monitor_def *mdef)
{
return;
}
static inline int init_rv_reactors(struct dentry *root_dir)
{
return 0;
+43 -95
View File
@@ -70,12 +70,12 @@
*/
static LIST_HEAD(rv_reactors_list);
static struct rv_reactor_def *get_reactor_rdef_by_name(char *name)
static struct rv_reactor *get_reactor_rdef_by_name(char *name)
{
struct rv_reactor_def *r;
struct rv_reactor *r;
list_for_each_entry(r, &rv_reactors_list, list) {
if (strcmp(name, r->reactor->name) == 0)
if (strcmp(name, r->name) == 0)
return r;
}
return NULL;
@@ -86,9 +86,9 @@ static struct rv_reactor_def *get_reactor_rdef_by_name(char *name)
*/
static int reactors_show(struct seq_file *m, void *p)
{
struct rv_reactor_def *rea_def = p;
struct rv_reactor *reactor = container_of(p, struct rv_reactor, list);
seq_printf(m, "%s\n", rea_def->reactor->name);
seq_printf(m, "%s\n", reactor->name);
return 0;
}
@@ -138,13 +138,13 @@ static const struct file_operations available_reactors_ops = {
*/
static int monitor_reactor_show(struct seq_file *m, void *p)
{
struct rv_monitor_def *mdef = m->private;
struct rv_reactor_def *rdef = p;
struct rv_monitor *mon = m->private;
struct rv_reactor *reactor = container_of(p, struct rv_reactor, list);
if (mdef->rdef == rdef)
seq_printf(m, "[%s]\n", rdef->reactor->name);
if (mon->reactor == reactor)
seq_printf(m, "[%s]\n", reactor->name);
else
seq_printf(m, "%s\n", rdef->reactor->name);
seq_printf(m, "%s\n", reactor->name);
return 0;
}
@@ -158,43 +158,37 @@ static const struct seq_operations monitor_reactors_seq_ops = {
.show = monitor_reactor_show
};
static void monitor_swap_reactors_single(struct rv_monitor_def *mdef,
struct rv_reactor_def *rdef,
bool reacting, bool nested)
static void monitor_swap_reactors_single(struct rv_monitor *mon,
struct rv_reactor *reactor,
bool nested)
{
bool monitor_enabled;
/* nothing to do */
if (mdef->rdef == rdef)
if (mon->reactor == reactor)
return;
monitor_enabled = mdef->monitor->enabled;
monitor_enabled = mon->enabled;
if (monitor_enabled)
rv_disable_monitor(mdef);
rv_disable_monitor(mon);
/* swap reactor's usage */
mdef->rdef->counter--;
rdef->counter++;
mdef->rdef = rdef;
mdef->reacting = reacting;
mdef->monitor->react = rdef->reactor->react;
mon->reactor = reactor;
mon->react = reactor->react;
/* enable only once if iterating through a container */
if (monitor_enabled && !nested)
rv_enable_monitor(mdef);
rv_enable_monitor(mon);
}
static void monitor_swap_reactors(struct rv_monitor_def *mdef,
struct rv_reactor_def *rdef, bool reacting)
static void monitor_swap_reactors(struct rv_monitor *mon, struct rv_reactor *reactor)
{
struct rv_monitor_def *p = mdef;
struct rv_monitor *p = mon;
if (rv_is_container_monitor(mdef))
if (rv_is_container_monitor(mon))
list_for_each_entry_continue(p, &rv_monitors_list, list) {
if (p->parent != mdef->monitor)
if (p->parent != mon)
break;
monitor_swap_reactors_single(p, rdef, reacting, true);
monitor_swap_reactors_single(p, reactor, true);
}
/*
* This call enables and disables the monitor if they were active.
@@ -202,7 +196,7 @@ static void monitor_swap_reactors(struct rv_monitor_def *mdef,
* All nested monitors are enabled also if they were off, we may refine
* this logic in the future.
*/
monitor_swap_reactors_single(mdef, rdef, reacting, false);
monitor_swap_reactors_single(mon, reactor, false);
}
static ssize_t
@@ -210,11 +204,10 @@ 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_def *mdef;
struct rv_reactor_def *rdef;
struct rv_monitor *mon;
struct rv_reactor *reactor;
struct seq_file *seq_f;
int retval = -EINVAL;
bool enable;
char *ptr;
int len;
@@ -237,22 +230,17 @@ monitor_reactors_write(struct file *file, const char __user *user_buf,
* See monitor_reactors_open()
*/
seq_f = file->private_data;
mdef = seq_f->private;
mon = seq_f->private;
mutex_lock(&rv_interface_lock);
retval = -EINVAL;
list_for_each_entry(rdef, &rv_reactors_list, list) {
if (strcmp(ptr, rdef->reactor->name) != 0)
list_for_each_entry(reactor, &rv_reactors_list, list) {
if (strcmp(ptr, reactor->name) != 0)
continue;
if (rdef == get_reactor_rdef_by_name("nop"))
enable = false;
else
enable = true;
monitor_swap_reactors(mdef, rdef, enable);
monitor_swap_reactors(mon, reactor);
retval = count;
break;
@@ -268,7 +256,7 @@ monitor_reactors_write(struct file *file, const char __user *user_buf,
*/
static int monitor_reactors_open(struct inode *inode, struct file *file)
{
struct rv_monitor_def *mdef = inode->i_private;
struct rv_monitor *mon = inode->i_private;
struct seq_file *seq_f;
int ret;
@@ -284,7 +272,7 @@ static int monitor_reactors_open(struct inode *inode, struct file *file)
/*
* Copy the create file "private" data to the seq_file private data.
*/
seq_f->private = mdef;
seq_f->private = mon;
return 0;
};
@@ -299,23 +287,16 @@ static const struct file_operations monitor_reactors_ops = {
static int __rv_register_reactor(struct rv_reactor *reactor)
{
struct rv_reactor_def *r;
struct rv_reactor *r;
list_for_each_entry(r, &rv_reactors_list, list) {
if (strcmp(reactor->name, r->reactor->name) == 0) {
if (strcmp(reactor->name, r->name) == 0) {
pr_info("Reactor %s is already registered\n", reactor->name);
return -EINVAL;
}
}
r = kzalloc(sizeof(struct rv_reactor_def), GFP_KERNEL);
if (!r)
return -ENOMEM;
r->reactor = reactor;
r->counter = 0;
list_add_tail(&r->list, &rv_reactors_list);
list_add_tail(&reactor->list, &rv_reactors_list);
return 0;
}
@@ -350,30 +331,10 @@ int rv_register_reactor(struct rv_reactor *reactor)
*/
int rv_unregister_reactor(struct rv_reactor *reactor)
{
struct rv_reactor_def *ptr, *next;
int ret = 0;
mutex_lock(&rv_interface_lock);
list_for_each_entry_safe(ptr, next, &rv_reactors_list, list) {
if (strcmp(reactor->name, ptr->reactor->name) == 0) {
if (!ptr->counter) {
list_del(&ptr->list);
} else {
printk(KERN_WARNING
"rv: the rv_reactor %s is in use by %d monitor(s)\n",
ptr->reactor->name, ptr->counter);
printk(KERN_WARNING "rv: the rv_reactor %s cannot be removed\n",
ptr->reactor->name);
ret = -EBUSY;
break;
}
}
}
list_del(&reactor->list);
mutex_unlock(&rv_interface_lock);
return ret;
return 0;
}
/*
@@ -454,43 +415,30 @@ static const struct file_operations reacting_on_fops = {
/**
* reactor_populate_monitor - creates per monitor reactors file
* @mdef: monitor's definition.
* @mon: The monitor.
*
* Returns 0 if successful, error otherwise.
*/
int reactor_populate_monitor(struct rv_monitor_def *mdef)
int reactor_populate_monitor(struct rv_monitor *mon)
{
struct dentry *tmp;
tmp = rv_create_file("reactors", RV_MODE_WRITE, mdef->root_d, mdef, &monitor_reactors_ops);
tmp = rv_create_file("reactors", RV_MODE_WRITE, mon->root_d, mon, &monitor_reactors_ops);
if (!tmp)
return -ENOMEM;
/*
* Configure as the rv_nop reactor.
*/
mdef->rdef = get_reactor_rdef_by_name("nop");
mdef->rdef->counter++;
mdef->reacting = false;
mon->reactor = get_reactor_rdef_by_name("nop");
return 0;
}
/**
* reactor_cleanup_monitor - cleanup a monitor reference
* @mdef: monitor's definition.
*/
void reactor_cleanup_monitor(struct rv_monitor_def *mdef)
{
lockdep_assert_held(&rv_interface_lock);
mdef->rdef->counter--;
WARN_ON_ONCE(mdef->rdef->counter < 0);
}
/*
* Nop reactor register
*/
static void rv_nop_reaction(char *msg)
__printf(1, 2) static void rv_nop_reaction(const char *msg, ...)
{
}
+121 -45
View File
@@ -16,24 +16,24 @@ DECLARE_EVENT_CLASS(event_da_monitor,
TP_ARGS(state, event, next_state, final_state),
TP_STRUCT__entry(
__array( char, state, MAX_DA_NAME_LEN )
__array( char, event, MAX_DA_NAME_LEN )
__array( char, next_state, MAX_DA_NAME_LEN )
__field( bool, final_state )
__string( state, state )
__string( event, event )
__string( next_state, next_state )
__field( bool, final_state )
),
TP_fast_assign(
memcpy(__entry->state, state, MAX_DA_NAME_LEN);
memcpy(__entry->event, event, MAX_DA_NAME_LEN);
memcpy(__entry->next_state, next_state, MAX_DA_NAME_LEN);
__entry->final_state = final_state;
__assign_str(state);
__assign_str(event);
__assign_str(next_state);
__entry->final_state = final_state;
),
TP_printk("%s x %s -> %s %s",
__entry->state,
__entry->event,
__entry->next_state,
__entry->final_state ? "(final)" : "")
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,
@@ -43,26 +43,26 @@ DECLARE_EVENT_CLASS(error_da_monitor,
TP_ARGS(state, event),
TP_STRUCT__entry(
__array( char, state, MAX_DA_NAME_LEN )
__array( char, event, MAX_DA_NAME_LEN )
__string( state, state )
__string( event, event )
),
TP_fast_assign(
memcpy(__entry->state, state, MAX_DA_NAME_LEN);
memcpy(__entry->event, event, MAX_DA_NAME_LEN);
__assign_str(state);
__assign_str(event);
),
TP_printk("event %s not expected in the state %s",
__entry->event,
__entry->state)
__get_str(event),
__get_str(state))
);
#include <monitors/wip/wip_trace.h>
#include <monitors/tss/tss_trace.h>
#include <monitors/sco/sco_trace.h>
#include <monitors/scpd/scpd_trace.h>
#include <monitors/snep/snep_trace.h>
#include <monitors/sncid/sncid_trace.h>
#include <monitors/sts/sts_trace.h>
#include <monitors/opid/opid_trace.h>
// Add new monitors based on CONFIG_DA_MON_EVENTS_IMPLICIT here
#endif /* CONFIG_DA_MON_EVENTS_IMPLICIT */
@@ -75,27 +75,27 @@ DECLARE_EVENT_CLASS(event_da_monitor_id,
TP_ARGS(id, state, event, next_state, final_state),
TP_STRUCT__entry(
__field( int, id )
__array( char, state, MAX_DA_NAME_LEN )
__array( char, event, MAX_DA_NAME_LEN )
__array( char, next_state, MAX_DA_NAME_LEN )
__field( bool, final_state )
__field( int, id )
__string( state, state )
__string( event, event )
__string( next_state, next_state )
__field( bool, final_state )
),
TP_fast_assign(
memcpy(__entry->state, state, MAX_DA_NAME_LEN);
memcpy(__entry->event, event, MAX_DA_NAME_LEN);
memcpy(__entry->next_state, next_state, MAX_DA_NAME_LEN);
__entry->id = id;
__entry->final_state = final_state;
__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",
TP_printk("%d: %s x %s -> %s%s",
__entry->id,
__entry->state,
__entry->event,
__entry->next_state,
__entry->final_state ? "(final)" : "")
__get_str(state),
__get_str(event),
__get_str(next_state),
__entry->final_state ? " (final)" : "")
);
DECLARE_EVENT_CLASS(error_da_monitor_id,
@@ -105,32 +105,108 @@ DECLARE_EVENT_CLASS(error_da_monitor_id,
TP_ARGS(id, state, event),
TP_STRUCT__entry(
__field( int, id )
__array( char, state, MAX_DA_NAME_LEN )
__array( char, event, MAX_DA_NAME_LEN )
__field( int, id )
__string( state, state )
__string( event, event )
),
TP_fast_assign(
memcpy(__entry->state, state, MAX_DA_NAME_LEN);
memcpy(__entry->event, event, MAX_DA_NAME_LEN);
__entry->id = id;
__assign_str(state);
__assign_str(event);
__entry->id = id;
),
TP_printk("%d: event %s not expected in the state %s",
__entry->id,
__entry->event,
__entry->state)
__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
#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 ust be outside protection */
/* 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>