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:
+1
-4
@@ -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
@@ -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) \
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rv.h>
|
||||
#include <rv/instrumentation.h>
|
||||
#include <rv/da_monitor.h>
|
||||
|
||||
#define MODULE_NAME "nrp"
|
||||
|
||||
#include <trace/events/irq.h>
|
||||
#include <trace/events/sched.h>
|
||||
#include <rv_trace.h>
|
||||
#include <monitors/sched/sched.h>
|
||||
|
||||
#include "nrp.h"
|
||||
|
||||
static struct rv_monitor rv_nrp;
|
||||
DECLARE_DA_MON_PER_TASK(nrp, unsigned char);
|
||||
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
#include <asm/trace/irq_vectors.h>
|
||||
|
||||
static void handle_vector_irq_entry(void *data, int vector)
|
||||
{
|
||||
da_handle_event_nrp(current, irq_entry_nrp);
|
||||
}
|
||||
|
||||
static void attach_vector_irq(void)
|
||||
{
|
||||
rv_attach_trace_probe("nrp", local_timer_entry, handle_vector_irq_entry);
|
||||
if (IS_ENABLED(CONFIG_IRQ_WORK))
|
||||
rv_attach_trace_probe("nrp", irq_work_entry, handle_vector_irq_entry);
|
||||
if (IS_ENABLED(CONFIG_SMP)) {
|
||||
rv_attach_trace_probe("nrp", reschedule_entry, handle_vector_irq_entry);
|
||||
rv_attach_trace_probe("nrp", call_function_entry, handle_vector_irq_entry);
|
||||
rv_attach_trace_probe("nrp", call_function_single_entry, handle_vector_irq_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void detach_vector_irq(void)
|
||||
{
|
||||
rv_detach_trace_probe("nrp", local_timer_entry, handle_vector_irq_entry);
|
||||
if (IS_ENABLED(CONFIG_IRQ_WORK))
|
||||
rv_detach_trace_probe("nrp", irq_work_entry, handle_vector_irq_entry);
|
||||
if (IS_ENABLED(CONFIG_SMP)) {
|
||||
rv_detach_trace_probe("nrp", reschedule_entry, handle_vector_irq_entry);
|
||||
rv_detach_trace_probe("nrp", call_function_entry, handle_vector_irq_entry);
|
||||
rv_detach_trace_probe("nrp", call_function_single_entry, handle_vector_irq_entry);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
/* We assume irq_entry tracepoints are sufficient on other architectures */
|
||||
static void attach_vector_irq(void) { }
|
||||
static void detach_vector_irq(void) { }
|
||||
#endif
|
||||
|
||||
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
|
||||
{
|
||||
da_handle_event_nrp(current, irq_entry_nrp);
|
||||
}
|
||||
|
||||
static void handle_sched_need_resched(void *data, struct task_struct *tsk,
|
||||
int cpu, int tif)
|
||||
{
|
||||
/*
|
||||
* Although need_resched leads to both the rescheduling and preempt_irq
|
||||
* states, it is safer to start the monitor always in preempt_irq,
|
||||
* which may not mirror the system state but makes the monitor simpler,
|
||||
*/
|
||||
if (tif == TIF_NEED_RESCHED)
|
||||
da_handle_start_event_nrp(tsk, sched_need_resched_nrp);
|
||||
}
|
||||
|
||||
static void handle_schedule_entry(void *data, bool preempt)
|
||||
{
|
||||
if (preempt)
|
||||
da_handle_event_nrp(current, schedule_entry_preempt_nrp);
|
||||
else
|
||||
da_handle_event_nrp(current, schedule_entry_nrp);
|
||||
}
|
||||
|
||||
static int enable_nrp(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = da_monitor_init_nrp();
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
rv_attach_trace_probe("nrp", irq_handler_entry, handle_irq_entry);
|
||||
rv_attach_trace_probe("nrp", sched_set_need_resched_tp, handle_sched_need_resched);
|
||||
rv_attach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry);
|
||||
attach_vector_irq();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disable_nrp(void)
|
||||
{
|
||||
rv_nrp.enabled = 0;
|
||||
|
||||
rv_detach_trace_probe("nrp", irq_handler_entry, handle_irq_entry);
|
||||
rv_detach_trace_probe("nrp", sched_set_need_resched_tp, handle_sched_need_resched);
|
||||
rv_detach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry);
|
||||
detach_vector_irq();
|
||||
|
||||
da_monitor_destroy_nrp();
|
||||
}
|
||||
|
||||
static struct rv_monitor rv_nrp = {
|
||||
.name = "nrp",
|
||||
.description = "need resched preempts.",
|
||||
.enable = enable_nrp,
|
||||
.disable = disable_nrp,
|
||||
.reset = da_monitor_reset_all_nrp,
|
||||
.enabled = 0,
|
||||
};
|
||||
|
||||
static int __init register_nrp(void)
|
||||
{
|
||||
return rv_register_monitor(&rv_nrp, &rv_sched);
|
||||
}
|
||||
|
||||
static void __exit unregister_nrp(void)
|
||||
{
|
||||
rv_unregister_monitor(&rv_nrp);
|
||||
}
|
||||
|
||||
module_init(register_nrp);
|
||||
module_exit(unregister_nrp);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
|
||||
MODULE_DESCRIPTION("nrp: need resched preempts.");
|
||||
@@ -0,0 +1,75 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Automatically generated C representation of nrp automaton
|
||||
* For further information about this format, see kernel documentation:
|
||||
* Documentation/trace/rv/deterministic_automata.rst
|
||||
*/
|
||||
|
||||
enum states_nrp {
|
||||
preempt_irq_nrp = 0,
|
||||
any_thread_running_nrp,
|
||||
nested_preempt_nrp,
|
||||
rescheduling_nrp,
|
||||
state_max_nrp
|
||||
};
|
||||
|
||||
#define INVALID_STATE state_max_nrp
|
||||
|
||||
enum events_nrp {
|
||||
irq_entry_nrp = 0,
|
||||
sched_need_resched_nrp,
|
||||
schedule_entry_nrp,
|
||||
schedule_entry_preempt_nrp,
|
||||
event_max_nrp
|
||||
};
|
||||
|
||||
struct automaton_nrp {
|
||||
char *state_names[state_max_nrp];
|
||||
char *event_names[event_max_nrp];
|
||||
unsigned char function[state_max_nrp][event_max_nrp];
|
||||
unsigned char initial_state;
|
||||
bool final_states[state_max_nrp];
|
||||
};
|
||||
|
||||
static const struct automaton_nrp automaton_nrp = {
|
||||
.state_names = {
|
||||
"preempt_irq",
|
||||
"any_thread_running",
|
||||
"nested_preempt",
|
||||
"rescheduling"
|
||||
},
|
||||
.event_names = {
|
||||
"irq_entry",
|
||||
"sched_need_resched",
|
||||
"schedule_entry",
|
||||
"schedule_entry_preempt"
|
||||
},
|
||||
.function = {
|
||||
{
|
||||
preempt_irq_nrp,
|
||||
preempt_irq_nrp,
|
||||
nested_preempt_nrp,
|
||||
nested_preempt_nrp
|
||||
},
|
||||
{
|
||||
any_thread_running_nrp,
|
||||
rescheduling_nrp,
|
||||
any_thread_running_nrp,
|
||||
INVALID_STATE
|
||||
},
|
||||
{
|
||||
nested_preempt_nrp,
|
||||
preempt_irq_nrp,
|
||||
any_thread_running_nrp,
|
||||
any_thread_running_nrp
|
||||
},
|
||||
{
|
||||
preempt_irq_nrp,
|
||||
rescheduling_nrp,
|
||||
any_thread_running_nrp,
|
||||
any_thread_running_nrp
|
||||
},
|
||||
},
|
||||
.initial_state = preempt_irq_nrp,
|
||||
.final_states = { 0, 1, 0, 0 },
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
* Snippet to be included in rv_trace.h
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_RV_MON_NRP
|
||||
DEFINE_EVENT(event_da_monitor_id, event_nrp,
|
||||
TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
|
||||
TP_ARGS(id, state, event, next_state, final_state));
|
||||
|
||||
DEFINE_EVENT(error_da_monitor_id, error_nrp,
|
||||
TP_PROTO(int id, char *state, char *event),
|
||||
TP_ARGS(id, state, event));
|
||||
#endif /* CONFIG_RV_MON_NRP */
|
||||
@@ -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
|
||||
@@ -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.");
|
||||
@@ -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
-4
@@ -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 */
|
||||
@@ -0,0 +1,11 @@
|
||||
config RV_MON_RTAPP
|
||||
depends on RV
|
||||
depends on RV_PER_TASK_MONITORS >= 2
|
||||
bool "rtapp monitor"
|
||||
help
|
||||
Collection of monitors to check for common problems with real-time
|
||||
application that may cause unexpected latency.
|
||||
|
||||
If you are developing a real-time system and not entirely sure whether
|
||||
the applications are designed correctly for real-time, you want to say
|
||||
Y here.
|
||||
@@ -0,0 +1,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");
|
||||
@@ -0,0 +1,3 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
extern struct rv_monitor rv_rtapp;
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
config RV_MON_SLEEP
|
||||
depends on RV
|
||||
select RV_LTL_MONITOR
|
||||
depends on HAVE_SYSCALL_TRACEPOINTS
|
||||
depends on RV_MON_RTAPP
|
||||
select TRACE_IRQFLAGS
|
||||
default y
|
||||
select LTL_MON_EVENTS_ID
|
||||
bool "sleep monitor"
|
||||
help
|
||||
Monitor that real-time tasks do not sleep in a manner that may
|
||||
cause undesirable latency.
|
||||
|
||||
If you are developing a real-time system and not entirely sure whether
|
||||
the applications are designed correctly for real-time, you want to say
|
||||
Y here.
|
||||
|
||||
Enabling this monitor may have performance impact (due to select
|
||||
TRACE_IRQFLAGS). Therefore, you probably should say N for
|
||||
production kernel.
|
||||
@@ -0,0 +1,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");
|
||||
@@ -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 */
|
||||
@@ -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.");
|
||||
@@ -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 },
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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:
|
||||
@@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rv.h>
|
||||
#include <rv/instrumentation.h>
|
||||
#include <rv/da_monitor.h>
|
||||
|
||||
#define MODULE_NAME "sssw"
|
||||
|
||||
#include <trace/events/sched.h>
|
||||
#include <trace/events/signal.h>
|
||||
#include <rv_trace.h>
|
||||
#include <monitors/sched/sched.h>
|
||||
|
||||
#include "sssw.h"
|
||||
|
||||
static struct rv_monitor rv_sssw;
|
||||
DECLARE_DA_MON_PER_TASK(sssw, unsigned char);
|
||||
|
||||
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
|
||||
{
|
||||
if (state == TASK_RUNNING)
|
||||
da_handle_start_event_sssw(tsk, sched_set_state_runnable_sssw);
|
||||
else
|
||||
da_handle_event_sssw(tsk, sched_set_state_sleepable_sssw);
|
||||
}
|
||||
|
||||
static void handle_sched_switch(void *data, bool preempt,
|
||||
struct task_struct *prev,
|
||||
struct task_struct *next,
|
||||
unsigned int prev_state)
|
||||
{
|
||||
if (preempt)
|
||||
da_handle_event_sssw(prev, sched_switch_preempt_sssw);
|
||||
else if (prev_state == TASK_RUNNING)
|
||||
da_handle_event_sssw(prev, sched_switch_yield_sssw);
|
||||
else if (prev_state == TASK_RTLOCK_WAIT)
|
||||
/* special case of sleeping task with racy conditions */
|
||||
da_handle_event_sssw(prev, sched_switch_blocking_sssw);
|
||||
else
|
||||
da_handle_event_sssw(prev, sched_switch_suspend_sssw);
|
||||
da_handle_event_sssw(next, sched_switch_in_sssw);
|
||||
}
|
||||
|
||||
static void handle_sched_wakeup(void *data, struct task_struct *p)
|
||||
{
|
||||
/*
|
||||
* Wakeup can also lead to signal_wakeup although the system is
|
||||
* actually runnable. The monitor can safely start with this event.
|
||||
*/
|
||||
da_handle_start_event_sssw(p, sched_wakeup_sssw);
|
||||
}
|
||||
|
||||
static void handle_signal_deliver(void *data, int sig,
|
||||
struct kernel_siginfo *info,
|
||||
struct k_sigaction *ka)
|
||||
{
|
||||
da_handle_event_sssw(current, signal_deliver_sssw);
|
||||
}
|
||||
|
||||
static int enable_sssw(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = da_monitor_init_sssw();
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
rv_attach_trace_probe("sssw", sched_set_state_tp, handle_sched_set_state);
|
||||
rv_attach_trace_probe("sssw", sched_switch, handle_sched_switch);
|
||||
rv_attach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup);
|
||||
rv_attach_trace_probe("sssw", signal_deliver, handle_signal_deliver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disable_sssw(void)
|
||||
{
|
||||
rv_sssw.enabled = 0;
|
||||
|
||||
rv_detach_trace_probe("sssw", sched_set_state_tp, handle_sched_set_state);
|
||||
rv_detach_trace_probe("sssw", sched_switch, handle_sched_switch);
|
||||
rv_detach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup);
|
||||
rv_detach_trace_probe("sssw", signal_deliver, handle_signal_deliver);
|
||||
|
||||
da_monitor_destroy_sssw();
|
||||
}
|
||||
|
||||
static struct rv_monitor rv_sssw = {
|
||||
.name = "sssw",
|
||||
.description = "set state sleep and wakeup.",
|
||||
.enable = enable_sssw,
|
||||
.disable = disable_sssw,
|
||||
.reset = da_monitor_reset_all_sssw,
|
||||
.enabled = 0,
|
||||
};
|
||||
|
||||
static int __init register_sssw(void)
|
||||
{
|
||||
return rv_register_monitor(&rv_sssw, &rv_sched);
|
||||
}
|
||||
|
||||
static void __exit unregister_sssw(void)
|
||||
{
|
||||
rv_unregister_monitor(&rv_sssw);
|
||||
}
|
||||
|
||||
module_init(register_sssw);
|
||||
module_exit(unregister_sssw);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
|
||||
MODULE_DESCRIPTION("sssw: set state sleep and wakeup.");
|
||||
@@ -0,0 +1,105 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Automatically generated C representation of sssw automaton
|
||||
* For further information about this format, see kernel documentation:
|
||||
* Documentation/trace/rv/deterministic_automata.rst
|
||||
*/
|
||||
|
||||
enum states_sssw {
|
||||
runnable_sssw = 0,
|
||||
signal_wakeup_sssw,
|
||||
sleepable_sssw,
|
||||
sleeping_sssw,
|
||||
state_max_sssw
|
||||
};
|
||||
|
||||
#define INVALID_STATE state_max_sssw
|
||||
|
||||
enum events_sssw {
|
||||
sched_set_state_runnable_sssw = 0,
|
||||
sched_set_state_sleepable_sssw,
|
||||
sched_switch_blocking_sssw,
|
||||
sched_switch_in_sssw,
|
||||
sched_switch_preempt_sssw,
|
||||
sched_switch_suspend_sssw,
|
||||
sched_switch_yield_sssw,
|
||||
sched_wakeup_sssw,
|
||||
signal_deliver_sssw,
|
||||
event_max_sssw
|
||||
};
|
||||
|
||||
struct automaton_sssw {
|
||||
char *state_names[state_max_sssw];
|
||||
char *event_names[event_max_sssw];
|
||||
unsigned char function[state_max_sssw][event_max_sssw];
|
||||
unsigned char initial_state;
|
||||
bool final_states[state_max_sssw];
|
||||
};
|
||||
|
||||
static const struct automaton_sssw automaton_sssw = {
|
||||
.state_names = {
|
||||
"runnable",
|
||||
"signal_wakeup",
|
||||
"sleepable",
|
||||
"sleeping"
|
||||
},
|
||||
.event_names = {
|
||||
"sched_set_state_runnable",
|
||||
"sched_set_state_sleepable",
|
||||
"sched_switch_blocking",
|
||||
"sched_switch_in",
|
||||
"sched_switch_preempt",
|
||||
"sched_switch_suspend",
|
||||
"sched_switch_yield",
|
||||
"sched_wakeup",
|
||||
"signal_deliver"
|
||||
},
|
||||
.function = {
|
||||
{
|
||||
runnable_sssw,
|
||||
sleepable_sssw,
|
||||
sleeping_sssw,
|
||||
runnable_sssw,
|
||||
runnable_sssw,
|
||||
INVALID_STATE,
|
||||
runnable_sssw,
|
||||
runnable_sssw,
|
||||
runnable_sssw
|
||||
},
|
||||
{
|
||||
INVALID_STATE,
|
||||
sleepable_sssw,
|
||||
INVALID_STATE,
|
||||
signal_wakeup_sssw,
|
||||
signal_wakeup_sssw,
|
||||
INVALID_STATE,
|
||||
signal_wakeup_sssw,
|
||||
signal_wakeup_sssw,
|
||||
runnable_sssw
|
||||
},
|
||||
{
|
||||
runnable_sssw,
|
||||
sleepable_sssw,
|
||||
sleeping_sssw,
|
||||
sleepable_sssw,
|
||||
sleepable_sssw,
|
||||
sleeping_sssw,
|
||||
signal_wakeup_sssw,
|
||||
runnable_sssw,
|
||||
sleepable_sssw
|
||||
},
|
||||
{
|
||||
INVALID_STATE,
|
||||
INVALID_STATE,
|
||||
INVALID_STATE,
|
||||
INVALID_STATE,
|
||||
INVALID_STATE,
|
||||
INVALID_STATE,
|
||||
INVALID_STATE,
|
||||
runnable_sssw,
|
||||
INVALID_STATE
|
||||
},
|
||||
},
|
||||
.initial_state = runnable_sssw,
|
||||
.final_states = { 1, 0, 0, 0 },
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
/*
|
||||
* Snippet to be included in rv_trace.h
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_RV_MON_SSSW
|
||||
DEFINE_EVENT(event_da_monitor_id, event_sssw,
|
||||
TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
|
||||
TP_ARGS(id, state, event, next_state, final_state));
|
||||
|
||||
DEFINE_EVENT(error_da_monitor_id, error_sssw,
|
||||
TP_PROTO(int id, char *state, char *event),
|
||||
TP_ARGS(id, state, event));
|
||||
#endif /* CONFIG_RV_MON_SSSW */
|
||||
@@ -0,0 +1,19 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
config RV_MON_STS
|
||||
depends on RV
|
||||
depends on TRACE_IRQFLAGS
|
||||
depends on RV_MON_SCHED
|
||||
default y
|
||||
select DA_MON_EVENTS_IMPLICIT
|
||||
bool "sts monitor"
|
||||
help
|
||||
Monitor to ensure relationships between scheduler and task switches
|
||||
* the scheduler is called and returns with interrupts disabled
|
||||
* each call to the scheduler has up to one switch
|
||||
* switches only happen inside the scheduler
|
||||
* each call to the scheduler disables interrupts to switch
|
||||
This monitor is part of the sched monitors collection.
|
||||
|
||||
For further information, see:
|
||||
Documentation/trace/rv/monitor_sched.rst
|
||||
@@ -0,0 +1,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.");
|
||||
@@ -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 */
|
||||
@@ -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.");
|
||||
@@ -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 },
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user