* [PATCH 4/8] verification/rvgen: Adapt dot2k and templates after refactoring da_monitor.h
From: Gabriele Monaco @ 2025-11-26 10:42 UTC (permalink / raw)
To: linux-kernel, Steven Rostedt, Nam Cao, Gabriele Monaco,
linux-trace-kernel
Cc: Thomas Weißschuh
In-Reply-To: <20251126104241.291258-1-gmonaco@redhat.com>
Previous changes refactored the da_monitor header file to avoid using
macros. This implies a few changes in how to import and use da_monitor
helpers:
DECLARE_DA_MON_<TYPE>(name, type) is substituted by
#define RV_MON_TYPE RV_MON_<TYPE>
Update the rvgen templates to reflect the changes.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
tools/verification/rvgen/rvgen/dot2k.py | 6 +++--
.../rvgen/rvgen/templates/dot2k/main.c | 25 +++++++------------
2 files changed, 13 insertions(+), 18 deletions(-)
diff --git a/tools/verification/rvgen/rvgen/dot2k.py b/tools/verification/rvgen/rvgen/dot2k.py
index ed0a3c901106..d618a842fc52 100644
--- a/tools/verification/rvgen/rvgen/dot2k.py
+++ b/tools/verification/rvgen/rvgen/dot2k.py
@@ -38,9 +38,9 @@ class dot2k(Monitor, Dot2c):
handle = "handle_start_run_event"
if self.monitor_type == "per_task":
buff.append("\tstruct task_struct *p = /* XXX: how do I get p? */;");
- buff.append("\tda_%s_%s(p, %s%s);" % (handle, self.name, event, self.enum_suffix));
+ buff.append("\tda_%s(p, %s%s);" % (handle, event, self.enum_suffix));
else:
- buff.append("\tda_%s_%s(%s%s);" % (handle, self.name, event, self.enum_suffix));
+ buff.append("\tda_%s(%s%s);" % (handle, event, self.enum_suffix));
buff.append("}")
buff.append("")
return '\n'.join(buff)
@@ -66,6 +66,8 @@ class dot2k(Monitor, Dot2c):
buff.append(" * Documentation/trace/rv/deterministic_automata.rst")
buff.append(" */")
buff.append("")
+ buff.append("#define MONITOR_NAME %s" % (self.name))
+ buff.append("")
return buff
diff --git a/tools/verification/rvgen/rvgen/templates/dot2k/main.c b/tools/verification/rvgen/rvgen/templates/dot2k/main.c
index e0fd1134bd85..a14e4f0883db 100644
--- a/tools/verification/rvgen/rvgen/templates/dot2k/main.c
+++ b/tools/verification/rvgen/rvgen/templates/dot2k/main.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "%%MODEL_NAME%%"
@@ -20,15 +19,9 @@
* This is the self-generated part of the monitor. Generally, there is no need
* to touch this section.
*/
+#define RV_MON_TYPE RV_MON_%%MONITOR_TYPE%%
#include "%%MODEL_NAME%%.h"
-
-/*
- * Declare the deterministic automata monitor.
- *
- * The rv monitor reference is needed for the monitor declaration.
- */
-static struct rv_monitor rv_%%MODEL_NAME%%;
-DECLARE_DA_MON_%%MONITOR_TYPE%%(%%MODEL_NAME%%, %%MIN_TYPE%%);
+#include <rv/da_monitor.h>
/*
* This is the instrumentation part of the monitor.
@@ -42,7 +35,7 @@ static int enable_%%MODEL_NAME%%(void)
{
int retval;
- retval = da_monitor_init_%%MODEL_NAME%%();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -53,33 +46,33 @@ static int enable_%%MODEL_NAME%%(void)
static void disable_%%MODEL_NAME%%(void)
{
- rv_%%MODEL_NAME%%.enabled = 0;
+ rv_this.enabled = 0;
%%TRACEPOINT_DETACH%%
- da_monitor_destroy_%%MODEL_NAME%%();
+ da_monitor_destroy();
}
/*
* This is the monitor register section.
*/
-static struct rv_monitor rv_%%MODEL_NAME%% = {
+static struct rv_monitor rv_this = {
.name = "%%MODEL_NAME%%",
.description = "%%DESCRIPTION%%",
.enable = enable_%%MODEL_NAME%%,
.disable = disable_%%MODEL_NAME%%,
- .reset = da_monitor_reset_all_%%MODEL_NAME%%,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_%%MODEL_NAME%%(void)
{
- return rv_register_monitor(&rv_%%MODEL_NAME%%, %%PARENT%%);
+ return rv_register_monitor(&rv_this, %%PARENT%%);
}
static void __exit unregister_%%MODEL_NAME%%(void)
{
- rv_unregister_monitor(&rv_%%MODEL_NAME%%);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_%%MODEL_NAME%%);
--
2.51.1
^ permalink raw reply related
* [PATCH 3/8] Documentation/rv: Adapt documentation after da_monitor refactoring
From: Gabriele Monaco @ 2025-11-26 10:42 UTC (permalink / raw)
To: linux-kernel, Steven Rostedt, Nam Cao, Gabriele Monaco,
Jonathan Corbet, linux-trace-kernel, linux-doc
Cc: Thomas Weißschuh
In-Reply-To: <20251126104241.291258-1-gmonaco@redhat.com>
Previous changes refactored the da_monitor header file to avoid using
macros. This implies a few changes in how to import and use da_monitor
helpers:
DECLARE_DA_MON_<TYPE>(name, type) is substituted by
#define RV_MON_TYPE RV_MON_<TYPE>
da_handle_event_<name>() is substituted by
da_handle_event()
Update the documentation to reflect the changes.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
Documentation/trace/rv/monitor_synthesis.rst | 44 ++++++++++----------
1 file changed, 21 insertions(+), 23 deletions(-)
diff --git a/Documentation/trace/rv/monitor_synthesis.rst b/Documentation/trace/rv/monitor_synthesis.rst
index 3a7d7b2f6cb6..cc5f97977a29 100644
--- a/Documentation/trace/rv/monitor_synthesis.rst
+++ b/Documentation/trace/rv/monitor_synthesis.rst
@@ -100,54 +100,52 @@ rv/da_monitor.h
This initial implementation presents three different types of monitor instances:
-- ``#define DECLARE_DA_MON_GLOBAL(name, type)``
-- ``#define DECLARE_DA_MON_PER_CPU(name, type)``
-- ``#define DECLARE_DA_MON_PER_TASK(name, type)``
+- ``#define RV_MON_TYPE RV_MON_GLOBAL``
+- ``#define RV_MON_TYPE RV_MON_PER_CPU``
+- ``#define RV_MON_TYPE RV_MON_PER_TASK``
-The first declares the functions for a global deterministic automata monitor,
-the second for monitors with per-cpu instances, and the third with per-task
-instances.
+The first sets up functions declaration for a global deterministic automata
+monitor, the second for monitors with per-cpu instances, and the third with
+per-task instances.
-In all cases, the 'name' argument is a string that identifies the monitor, and
-the 'type' argument is the data type used by rvgen on the representation of
-the model in C.
+In all cases, the C file must include the $(MODEL_NAME).h file (generated by
+`rvgen`), for example, to define the per-cpu 'wip' monitor, the `wip.c` source
+file must include::
-For example, the wip model with two states and three events can be
-stored in an 'unsigned char' type. Considering that the preemption control
-is a per-cpu behavior, the monitor declaration in the 'wip.c' file is::
-
- DECLARE_DA_MON_PER_CPU(wip, unsigned char);
+ #define RV_MON_TYPE RV_MON_PER_CPU
+ #include "wip.h"
+ #include <rv/da_monitor.h>
The monitor is executed by sending events to be processed via the functions
presented below::
- da_handle_event_$(MONITOR_NAME)($(event from event enum));
- da_handle_start_event_$(MONITOR_NAME)($(event from event enum));
- da_handle_start_run_event_$(MONITOR_NAME)($(event from event enum));
+ da_handle_event($(event from event enum));
+ da_handle_start_event($(event from event enum));
+ da_handle_start_run_event($(event from event enum));
-The function ``da_handle_event_$(MONITOR_NAME)()`` is the regular case where
+The function ``da_handle_event()`` is the regular case where
the event will be processed if the monitor is processing events.
When a monitor is enabled, it is placed in the initial state of the automata.
However, the monitor does not know if the system is in the *initial state*.
-The ``da_handle_start_event_$(MONITOR_NAME)()`` function is used to notify the
+The ``da_handle_start_event()`` function is used to notify the
monitor that the system is returning to the initial state, so the monitor can
start monitoring the next event.
-The ``da_handle_start_run_event_$(MONITOR_NAME)()`` function is used to notify
+The ``da_handle_start_run_event()`` function is used to notify
the monitor that the system is known to be in the initial state, so the
monitor can start monitoring and monitor the current event.
Using the wip model as example, the events "preempt_disable" and
"sched_waking" should be sent to monitor, respectively, via [2]::
- da_handle_event_wip(preempt_disable_wip);
- da_handle_event_wip(sched_waking_wip);
+ da_handle_event(preempt_disable_wip);
+ da_handle_event(sched_waking_wip);
While the event "preempt_enabled" will use::
- da_handle_start_event_wip(preempt_enable_wip);
+ da_handle_start_event(preempt_enable_wip);
To notify the monitor that the system will be returning to the initial state,
so the system and the monitor should be in sync.
--
2.51.1
^ permalink raw reply related
* [PATCH 2/8] rv: Cleanup da_monitor after refactor
From: Gabriele Monaco @ 2025-11-26 10:42 UTC (permalink / raw)
To: linux-kernel, Steven Rostedt, Nam Cao, Gabriele Monaco,
linux-trace-kernel
Cc: Thomas Weißschuh
In-Reply-To: <20251126104241.291258-1-gmonaco@redhat.com>
Previous changes refactored the da_monitor header file to avoid using
macros, however empty macros (e.g. DECLARE_DA_FUNCTION) were left to
ease review with diff tools.
Most macros also get the argument type which doesn't really have a
purpose since states have their own enum and the storage in struct
da_monitor is fixed to unsigned int.
Remove empty and no longer required macros and substitute the type
parameter with the appropriate enum.
Additionally break long line and adjust the format overall.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
include/rv/automata.h | 24 +++------
include/rv/da_monitor.h | 107 ++++++++++++++++------------------------
2 files changed, 50 insertions(+), 81 deletions(-)
diff --git a/include/rv/automata.h b/include/rv/automata.h
index 5b5d2e94c034..4a4eb40cf09a 100644
--- a/include/rv/automata.h
+++ b/include/rv/automata.h
@@ -6,29 +6,19 @@
* models in C generated by the dot2k tool.
*/
+#ifndef _RV_AUTOMATA_H
+#define _RV_AUTOMATA_H
+
#ifndef MONITOR_NAME
#error "MONITOR_NAME macro is not defined. Did you include $(MODEL_NAME).h generated by rvgen?"
#endif
-#ifndef type
-#define type unsigned char
-#endif
-
#define RV_AUTOMATON_NAME CONCATENATE(automaton_, MONITOR_NAME)
#define EVENT_MAX CONCATENATE(event_max_, MONITOR_NAME)
#define STATE_MAX CONCATENATE(state_max_, MONITOR_NAME)
#define events CONCATENATE(events_, MONITOR_NAME)
#define states CONCATENATE(states_, MONITOR_NAME)
-/*
- * DECLARE_AUTOMATA_HELPERS - define a set of helper functions for automata
- *
- * Define a set of helper functions for automata. The 'name' argument is used
- * as suffix for the functions and data. These functions will handle automaton
- * with data type 'type'.
- */
-#define DECLARE_AUTOMATA_HELPERS(name, type)
-
/*
* model_get_state_name - return the (string) name of the given state
*/
@@ -54,7 +44,7 @@ static char *model_get_event_name(enum events event)
/*
* model_get_initial_state - return the automaton's initial state
*/
-static inline type model_get_initial_state(void)
+static inline enum states model_get_initial_state(void)
{
return RV_AUTOMATON_NAME.initial_state;
}
@@ -65,8 +55,8 @@ static inline type model_get_initial_state(void)
* Given the current state (curr_state) and the event (event), returns
* the next state, or INVALID_STATE in case of error.
*/
-static inline type model_get_next_state(enum states curr_state,
- enum events event)
+static inline enum states model_get_next_state(enum states curr_state,
+ enum events event)
{
if ((curr_state < 0) || (curr_state >= STATE_MAX))
return INVALID_STATE;
@@ -87,3 +77,5 @@ static inline bool model_is_final_state(enum states state)
return RV_AUTOMATON_NAME.final_states[state];
}
+
+#endif
diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h
index a4555dafa8d0..7b28ef9f73bd 100644
--- a/include/rv/da_monitor.h
+++ b/include/rv/da_monitor.h
@@ -11,6 +11,9 @@
* Documentation/trace/rv/da_monitor_synthesis.rst
*/
+#ifndef _RV_DA_MONITOR_H
+#define _RV_DA_MONITOR_H
+
#include <rv/automata.h>
#include <linux/rv.h>
#include <linux/stringify.h>
@@ -19,12 +22,7 @@
static struct rv_monitor rv_this;
-/*
- * Generic helpers for all types of deterministic automata monitors.
- */
-#define DECLARE_DA_MON_GENERIC_HELPERS(name, type)
-
-static void react(type curr_state, type event)
+static void react(enum states curr_state, enum events event)
{
rv_react(&rv_this,
"rv: monitor %s does not allow event %s on state %s\n",
@@ -83,7 +81,6 @@ static inline bool da_monitor_enabled(void)
*/
static inline bool da_monitor_handling_event(struct da_monitor *da_mon)
{
-
if (!da_monitor_enabled())
return 0;
@@ -94,6 +91,7 @@ static inline bool da_monitor_handling_event(struct da_monitor *da_mon)
return 1;
}
+#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
/*
* Event handler for implicit monitors. Implicit monitor is the one which the
* handler does not need to specify which da_monitor to manipulate. Examples
@@ -103,10 +101,8 @@ static inline bool da_monitor_handling_event(struct da_monitor *da_mon)
* warn and reset the monitor if it runs out of retries. The monitor should be
* able to handle various orders.
*/
-#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
-static inline bool
-da_event(struct da_monitor *da_mon, enum events event)
+static inline bool da_event(struct da_monitor *da_mon, enum events event)
{
enum states curr_state, next_state;
@@ -115,15 +111,17 @@ da_event(struct da_monitor *da_mon, enum events event)
next_state = model_get_next_state(curr_state, event);
if (next_state == INVALID_STATE) {
react(curr_state, event);
- CONCATENATE(trace_error_, MONITOR_NAME)(model_get_state_name(curr_state),
- model_get_event_name(event));
+ CONCATENATE(trace_error_, MONITOR_NAME)(
+ model_get_state_name(curr_state),
+ model_get_event_name(event));
return false;
}
if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
- CONCATENATE(trace_event_, MONITOR_NAME)(model_get_state_name(curr_state),
- model_get_event_name(event),
- model_get_state_name(next_state),
- model_is_final_state(next_state));
+ CONCATENATE(trace_event_, MONITOR_NAME)(
+ model_get_state_name(curr_state),
+ model_get_event_name(event),
+ model_get_state_name(next_state),
+ model_is_final_state(next_state));
return true;
}
}
@@ -135,6 +133,7 @@ da_event(struct da_monitor *da_mon, enum events event)
return false;
}
+#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
* Event handler for per_task monitors.
*
@@ -142,10 +141,9 @@ da_event(struct da_monitor *da_mon, enum events event)
* warn and reset the monitor if it runs out of retries. The monitor should be
* able to handle various orders.
*/
-#elif RV_MON_TYPE == RV_MON_PER_TASK
static inline bool da_event(struct da_monitor *da_mon, struct task_struct *tsk,
- enum events event)
+ enum events event)
{
enum states curr_state, next_state;
@@ -155,16 +153,16 @@ static inline bool da_event(struct da_monitor *da_mon, struct task_struct *tsk,
if (next_state == INVALID_STATE) {
react(curr_state, event);
CONCATENATE(trace_error_, MONITOR_NAME)(tsk->pid,
- model_get_state_name(curr_state),
- model_get_event_name(event));
+ model_get_state_name(curr_state),
+ model_get_event_name(event));
return false;
}
if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
CONCATENATE(trace_event_, MONITOR_NAME)(tsk->pid,
- model_get_state_name(curr_state),
- model_get_event_name(event),
- model_get_state_name(next_state),
- model_is_final_state(next_state));
+ model_get_state_name(curr_state),
+ model_get_event_name(event),
+ model_get_state_name(next_state),
+ model_is_final_state(next_state));
return true;
}
}
@@ -175,12 +173,12 @@ static inline bool da_event(struct da_monitor *da_mon, struct task_struct *tsk,
model_get_event_name(event), __stringify(MONITOR_NAME));
return false;
}
-#endif
+#endif /* RV_MON_TYPE */
+#if RV_MON_TYPE == RV_MON_GLOBAL
/*
* Functions to define, init and get a global monitor.
*/
-#if RV_MON_TYPE == RV_MON_GLOBAL
/*
* global monitor (a single variable)
@@ -215,15 +213,12 @@ static inline int da_monitor_init(void)
/*
* da_monitor_destroy - destroy the monitor
*/
-static inline void da_monitor_destroy(void)
-{
- return;
-}
+static inline void da_monitor_destroy(void) { }
+#elif RV_MON_TYPE == RV_MON_PER_CPU
/*
* Functions to define, init and get a per-cpu monitor.
*/
-#elif RV_MON_TYPE == RV_MON_PER_CPU
/*
* per-cpu monitor variables
@@ -245,6 +240,7 @@ static void da_monitor_reset_all(void)
{
struct da_monitor *da_mon;
int cpu;
+
for_each_cpu(cpu, cpu_online_mask) {
da_mon = per_cpu_ptr(&da_mon_this, cpu);
da_monitor_reset(da_mon);
@@ -263,15 +259,12 @@ static inline int da_monitor_init(void)
/*
* da_monitor_destroy - destroy the monitor
*/
-static inline void da_monitor_destroy(void)
-{
- return;
-}
+static inline void da_monitor_destroy(void) { }
+#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
* Functions to define, init and get a per-task monitor.
*/
-#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
* The per-task monitor is stored a vector in the task struct. This variable
@@ -331,18 +324,17 @@ static inline void da_monitor_destroy(void)
}
rv_put_task_monitor_slot(task_mon_slot);
task_mon_slot = RV_PER_TASK_MONITOR_INIT;
- return;
}
-#endif
+#endif /* RV_MON_TYPE */
+#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
/*
* Handle event for implicit monitor: da_get_monitor() will figure out
* the monitor.
*/
-#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
static inline void __da_handle_event(struct da_monitor *da_mon,
- enum events event)
+ enum events event)
{
bool retval;
@@ -418,14 +410,13 @@ static inline bool da_handle_start_run_event(enum events event)
return 1;
}
+#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
* Handle event for per task.
*/
-#elif RV_MON_TYPE == RV_MON_PER_TASK
-static inline void
-__da_handle_event(struct da_monitor *da_mon, struct task_struct *tsk,
- enum events event)
+static inline void __da_handle_event(struct da_monitor *da_mon,
+ struct task_struct *tsk, enum events event)
{
bool retval;
@@ -437,8 +428,7 @@ __da_handle_event(struct da_monitor *da_mon, struct task_struct *tsk,
/*
* da_handle_event - handle an event
*/
-static inline void
-da_handle_event(struct task_struct *tsk, enum events event)
+static inline void da_handle_event(struct task_struct *tsk, enum events event)
{
struct da_monitor *da_mon = da_get_monitor(tsk);
bool retval;
@@ -460,8 +450,8 @@ da_handle_event(struct task_struct *tsk, enum events event)
* If the monitor already started, handle the event.
* If the monitor did not start yet, start the monitor but skip the event.
*/
-static inline bool
-da_handle_start_event(struct task_struct *tsk, enum events event)
+static inline bool da_handle_start_event(struct task_struct *tsk,
+ enum events event)
{
struct da_monitor *da_mon;
@@ -486,8 +476,8 @@ da_handle_start_event(struct task_struct *tsk, enum events event)
* This function is used to notify the monitor that the system is in the
* initial state, so the monitor can start monitoring and handling event.
*/
-static inline bool
-da_handle_start_run_event(struct task_struct *tsk, enum events event)
+static inline bool da_handle_start_run_event(struct task_struct *tsk,
+ enum events event)
{
struct da_monitor *da_mon;
@@ -503,19 +493,6 @@ da_handle_start_run_event(struct task_struct *tsk, enum events event)
return 1;
}
-#endif
+#endif /* RV_MON_TYPE */
-/*
- * Entry point for the global monitor.
- */
-#define DECLARE_DA_MON_GLOBAL(name, type)
-
-/*
- * Entry point for the per-cpu monitor.
- */
-#define DECLARE_DA_MON_PER_CPU(name, type)
-
-/*
- * Entry point for the per-task monitor.
- */
-#define DECLARE_DA_MON_PER_TASK(name, type)
+#endif
--
2.51.1
^ permalink raw reply related
* [PATCH 1/8] rv: Refactor da_monitor to minimise macros
From: Gabriele Monaco @ 2025-11-26 10:42 UTC (permalink / raw)
To: linux-kernel, Steven Rostedt, Nam Cao, Gabriele Monaco,
Masami Hiramatsu, linux-trace-kernel
Cc: Thomas Weißschuh
In-Reply-To: <20251126104241.291258-1-gmonaco@redhat.com>
The da_monitor helper functions are generated from macros of the type:
DECLARE_DA_FUNCTION(name, type) \
static void da_func_x_##name(type arg) {} \
static void da_func_y_##name(type arg) {} \
This is good to minimise code duplication but the long macros made of
skipped end of lines is rather hard to parse. Since functions are
static, the advantage of naming them differently for each monitor is
minimal.
Refactor the da_monitor.h file to minimise macros, instead of declaring
functions from macros, we simply declare them with the same name for all
monitors (e.g. da_func_x) and for any remaining reference to the monitor
name (e.g. tracepoints, enums, global variables) we use the CONCATENATE
macro.
In this way the file is much easier to maintain while keeping the same
generality.
Functions depending on the monitor types are now conditionally compiled
according to the value of RV_MON_TYPE, which must be defined in the
monitor source.
The monitor type can be specified as in the original implementation,
although it's best to keep the default implementation (unsigned char) as
not all parts of code support larger data types, and likely there's no
need.
We keep the empty macro definitions to ease review of this change with
diff tools, but cleanup is required.
Also adapt existing monitors to keep the build working.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
include/linux/rv.h | 4 +
include/rv/automata.h | 132 ++--
include/rv/da_monitor.h | 898 ++++++++++++-------------
kernel/trace/rv/monitors/nrp/nrp.c | 30 +-
kernel/trace/rv/monitors/nrp/nrp.h | 2 +
kernel/trace/rv/monitors/opid/opid.c | 40 +-
kernel/trace/rv/monitors/opid/opid.h | 2 +
kernel/trace/rv/monitors/sco/sco.c | 26 +-
kernel/trace/rv/monitors/sco/sco.h | 2 +
kernel/trace/rv/monitors/scpd/scpd.c | 28 +-
kernel/trace/rv/monitors/scpd/scpd.h | 2 +
kernel/trace/rv/monitors/snep/snep.c | 28 +-
kernel/trace/rv/monitors/snep/snep.h | 2 +
kernel/trace/rv/monitors/snroc/snroc.c | 26 +-
kernel/trace/rv/monitors/snroc/snroc.h | 2 +
kernel/trace/rv/monitors/sssw/sssw.c | 38 +-
kernel/trace/rv/monitors/sssw/sssw.h | 2 +
kernel/trace/rv/monitors/sts/sts.c | 34 +-
kernel/trace/rv/monitors/sts/sts.h | 2 +
kernel/trace/rv/monitors/wip/wip.c | 26 +-
kernel/trace/rv/monitors/wip/wip.h | 2 +
kernel/trace/rv/monitors/wwnr/wwnr.c | 28 +-
kernel/trace/rv/monitors/wwnr/wwnr.h | 2 +
23 files changed, 682 insertions(+), 676 deletions(-)
diff --git a/include/linux/rv.h b/include/linux/rv.h
index 92fd467547e7..58774eb3aecf 100644
--- a/include/linux/rv.h
+++ b/include/linux/rv.h
@@ -10,6 +10,10 @@
#define MAX_DA_NAME_LEN 32
#define MAX_DA_RETRY_RACING_EVENTS 3
+#define RV_MON_GLOBAL 0
+#define RV_MON_PER_CPU 1
+#define RV_MON_PER_TASK 2
+
#ifdef CONFIG_RV
#include <linux/array_size.h>
#include <linux/bitops.h>
diff --git a/include/rv/automata.h b/include/rv/automata.h
index eb9e636809a0..5b5d2e94c034 100644
--- a/include/rv/automata.h
+++ b/include/rv/automata.h
@@ -6,6 +6,20 @@
* models in C generated by the dot2k tool.
*/
+#ifndef MONITOR_NAME
+#error "MONITOR_NAME macro is not defined. Did you include $(MODEL_NAME).h generated by rvgen?"
+#endif
+
+#ifndef type
+#define type unsigned char
+#endif
+
+#define RV_AUTOMATON_NAME CONCATENATE(automaton_, MONITOR_NAME)
+#define EVENT_MAX CONCATENATE(event_max_, MONITOR_NAME)
+#define STATE_MAX CONCATENATE(state_max_, MONITOR_NAME)
+#define events CONCATENATE(events_, MONITOR_NAME)
+#define states CONCATENATE(states_, MONITOR_NAME)
+
/*
* DECLARE_AUTOMATA_HELPERS - define a set of helper functions for automata
*
@@ -13,63 +27,63 @@
* as suffix for the functions and data. These functions will handle automaton
* with data type 'type'.
*/
-#define DECLARE_AUTOMATA_HELPERS(name, type) \
- \
-/* \
- * model_get_state_name_##name - return the (string) name of the given state \
- */ \
-static char *model_get_state_name_##name(enum states_##name state) \
-{ \
- if ((state < 0) || (state >= state_max_##name)) \
- return "INVALID"; \
- \
- return automaton_##name.state_names[state]; \
-} \
- \
-/* \
- * model_get_event_name_##name - return the (string) name of the given event \
- */ \
-static char *model_get_event_name_##name(enum events_##name event) \
-{ \
- if ((event < 0) || (event >= event_max_##name)) \
- return "INVALID"; \
- \
- return automaton_##name.event_names[event]; \
-} \
- \
-/* \
- * model_get_initial_state_##name - return the automaton's initial state \
- */ \
-static inline type model_get_initial_state_##name(void) \
-{ \
- return automaton_##name.initial_state; \
-} \
- \
-/* \
- * model_get_next_state_##name - process an automaton event occurrence \
- * \
- * Given the current state (curr_state) and the event (event), returns \
- * the next state, or INVALID_STATE in case of error. \
- */ \
-static inline type model_get_next_state_##name(enum states_##name curr_state, \
- enum events_##name event) \
-{ \
- if ((curr_state < 0) || (curr_state >= state_max_##name)) \
- return INVALID_STATE; \
- \
- if ((event < 0) || (event >= event_max_##name)) \
- return INVALID_STATE; \
- \
- return automaton_##name.function[curr_state][event]; \
-} \
- \
-/* \
- * model_is_final_state_##name - check if the given state is a final state \
- */ \
-static inline bool model_is_final_state_##name(enum states_##name state) \
-{ \
- if ((state < 0) || (state >= state_max_##name)) \
- return 0; \
- \
- return automaton_##name.final_states[state]; \
+#define DECLARE_AUTOMATA_HELPERS(name, type)
+
+/*
+ * model_get_state_name - return the (string) name of the given state
+ */
+static char *model_get_state_name(enum states state)
+{
+ if ((state < 0) || (state >= STATE_MAX))
+ return "INVALID";
+
+ return RV_AUTOMATON_NAME.state_names[state];
+}
+
+/*
+ * model_get_event_name - return the (string) name of the given event
+ */
+static char *model_get_event_name(enum events event)
+{
+ if ((event < 0) || (event >= EVENT_MAX))
+ return "INVALID";
+
+ return RV_AUTOMATON_NAME.event_names[event];
+}
+
+/*
+ * model_get_initial_state - return the automaton's initial state
+ */
+static inline type model_get_initial_state(void)
+{
+ return RV_AUTOMATON_NAME.initial_state;
+}
+
+/*
+ * model_get_next_state - process an automaton event occurrence
+ *
+ * Given the current state (curr_state) and the event (event), returns
+ * the next state, or INVALID_STATE in case of error.
+ */
+static inline type model_get_next_state(enum states curr_state,
+ enum events event)
+{
+ if ((curr_state < 0) || (curr_state >= STATE_MAX))
+ return INVALID_STATE;
+
+ if ((event < 0) || (event >= EVENT_MAX))
+ return INVALID_STATE;
+
+ return RV_AUTOMATON_NAME.function[curr_state][event];
+}
+
+/*
+ * model_is_final_state - check if the given state is a final state
+ */
+static inline bool model_is_final_state(enum states state)
+{
+ if ((state < 0) || (state >= STATE_MAX))
+ return 0;
+
+ return RV_AUTOMATON_NAME.final_states[state];
}
diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h
index 0cef64366538..a4555dafa8d0 100644
--- a/include/rv/da_monitor.h
+++ b/include/rv/da_monitor.h
@@ -13,82 +13,85 @@
#include <rv/automata.h>
#include <linux/rv.h>
+#include <linux/stringify.h>
#include <linux/bug.h>
#include <linux/sched.h>
+static struct rv_monitor rv_this;
+
/*
* Generic helpers for all types of deterministic automata monitors.
*/
-#define DECLARE_DA_MON_GENERIC_HELPERS(name, type) \
- \
-static void react_##name(type curr_state, type event) \
-{ \
- rv_react(&rv_##name, \
- "rv: monitor %s does not allow event %s on state %s\n", \
- #name, \
- model_get_event_name_##name(event), \
- model_get_state_name_##name(curr_state)); \
-} \
- \
-/* \
- * da_monitor_reset_##name - reset a monitor and setting it to init state \
- */ \
-static inline void da_monitor_reset_##name(struct da_monitor *da_mon) \
-{ \
- da_mon->monitoring = 0; \
- da_mon->curr_state = model_get_initial_state_##name(); \
-} \
- \
-/* \
- * da_monitor_start_##name - start monitoring \
- * \
- * The monitor will ignore all events until monitoring is set to true. This \
- * function needs to be called to tell the monitor to start monitoring. \
- */ \
-static inline void da_monitor_start_##name(struct da_monitor *da_mon) \
-{ \
- da_mon->curr_state = model_get_initial_state_##name(); \
- da_mon->monitoring = 1; \
-} \
- \
-/* \
- * da_monitoring_##name - returns true if the monitor is processing events \
- */ \
-static inline bool da_monitoring_##name(struct da_monitor *da_mon) \
-{ \
- return da_mon->monitoring; \
-} \
- \
-/* \
- * da_monitor_enabled_##name - checks if the monitor is enabled \
- */ \
-static inline bool da_monitor_enabled_##name(void) \
-{ \
- /* global switch */ \
- if (unlikely(!rv_monitoring_on())) \
- return 0; \
- \
- /* monitor enabled */ \
- if (unlikely(!rv_##name.enabled)) \
- return 0; \
- \
- return 1; \
-} \
- \
-/* \
- * da_monitor_handling_event_##name - checks if the monitor is ready to handle events \
- */ \
-static inline bool da_monitor_handling_event_##name(struct da_monitor *da_mon) \
-{ \
- \
- if (!da_monitor_enabled_##name()) \
- return 0; \
- \
- /* monitor is actually monitoring */ \
- if (unlikely(!da_monitoring_##name(da_mon))) \
- return 0; \
- \
- return 1; \
+#define DECLARE_DA_MON_GENERIC_HELPERS(name, type)
+
+static void react(type curr_state, type event)
+{
+ rv_react(&rv_this,
+ "rv: monitor %s does not allow event %s on state %s\n",
+ __stringify(MONITOR_NAME),
+ model_get_event_name(event),
+ model_get_state_name(curr_state));
+}
+
+/*
+ * da_monitor_reset - reset a monitor and setting it to init state
+ */
+static inline void da_monitor_reset(struct da_monitor *da_mon)
+{
+ da_mon->monitoring = 0;
+ da_mon->curr_state = model_get_initial_state();
+}
+
+/*
+ * da_monitor_start - start monitoring
+ *
+ * The monitor will ignore all events until monitoring is set to true. This
+ * function needs to be called to tell the monitor to start monitoring.
+ */
+static inline void da_monitor_start(struct da_monitor *da_mon)
+{
+ da_mon->curr_state = model_get_initial_state();
+ da_mon->monitoring = 1;
+}
+
+/*
+ * da_monitoring - returns true if the monitor is processing events
+ */
+static inline bool da_monitoring(struct da_monitor *da_mon)
+{
+ return da_mon->monitoring;
+}
+
+/*
+ * da_monitor_enabled - checks if the monitor is enabled
+ */
+static inline bool da_monitor_enabled(void)
+{
+ /* global switch */
+ if (unlikely(!rv_monitoring_on()))
+ return 0;
+
+ /* monitor enabled */
+ if (unlikely(!rv_this.enabled))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * da_monitor_handling_event - checks if the monitor is ready to handle events
+ */
+static inline bool da_monitor_handling_event(struct da_monitor *da_mon)
+{
+
+ if (!da_monitor_enabled())
+ return 0;
+
+ /* monitor is actually monitoring */
+ if (unlikely(!da_monitoring(da_mon)))
+ return 0;
+
+ return 1;
}
/*
@@ -100,37 +103,37 @@ static inline bool da_monitor_handling_event_##name(struct da_monitor *da_mon)
* warn and reset the monitor if it runs out of retries. The monitor should be
* able to handle various orders.
*/
-#define DECLARE_DA_MON_MODEL_HANDLER_IMPLICIT(name, type) \
- \
-static inline bool \
-da_event_##name(struct da_monitor *da_mon, enum events_##name event) \
-{ \
- enum states_##name curr_state, next_state; \
- \
- curr_state = READ_ONCE(da_mon->curr_state); \
- for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { \
- next_state = model_get_next_state_##name(curr_state, event); \
- if (next_state == INVALID_STATE) { \
- react_##name(curr_state, event); \
- trace_error_##name(model_get_state_name_##name(curr_state), \
- model_get_event_name_##name(event)); \
- return false; \
- } \
- if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { \
- trace_event_##name(model_get_state_name_##name(curr_state), \
- model_get_event_name_##name(event), \
- model_get_state_name_##name(next_state), \
- model_is_final_state_##name(next_state)); \
- return true; \
- } \
- } \
- \
- trace_rv_retries_error(#name, model_get_event_name_##name(event)); \
- pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS) \
- " retries reached for event %s, resetting monitor %s", \
- model_get_event_name_##name(event), #name); \
- return false; \
-} \
+#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
+
+static inline bool
+da_event(struct da_monitor *da_mon, enum events event)
+{
+ enum states curr_state, next_state;
+
+ curr_state = READ_ONCE(da_mon->curr_state);
+ for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) {
+ next_state = model_get_next_state(curr_state, event);
+ if (next_state == INVALID_STATE) {
+ react(curr_state, event);
+ CONCATENATE(trace_error_, MONITOR_NAME)(model_get_state_name(curr_state),
+ model_get_event_name(event));
+ return false;
+ }
+ if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
+ CONCATENATE(trace_event_, MONITOR_NAME)(model_get_state_name(curr_state),
+ model_get_event_name(event),
+ model_get_state_name(next_state),
+ model_is_final_state(next_state));
+ return true;
+ }
+ }
+
+ trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(event));
+ pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS)
+ " retries reached for event %s, resetting monitor %s",
+ model_get_event_name(event), __stringify(MONITOR_NAME));
+ return false;
+}
/*
* Event handler for per_task monitors.
@@ -139,395 +142,380 @@ da_event_##name(struct da_monitor *da_mon, enum events_##name event) \
* warn and reset the monitor if it runs out of retries. The monitor should be
* able to handle various orders.
*/
-#define DECLARE_DA_MON_MODEL_HANDLER_PER_TASK(name, type) \
- \
-static inline bool da_event_##name(struct da_monitor *da_mon, struct task_struct *tsk, \
- enum events_##name event) \
-{ \
- enum states_##name curr_state, next_state; \
- \
- curr_state = READ_ONCE(da_mon->curr_state); \
- for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { \
- next_state = model_get_next_state_##name(curr_state, event); \
- if (next_state == INVALID_STATE) { \
- react_##name(curr_state, event); \
- trace_error_##name(tsk->pid, \
- model_get_state_name_##name(curr_state), \
- model_get_event_name_##name(event)); \
- return false; \
- } \
- if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { \
- trace_event_##name(tsk->pid, \
- model_get_state_name_##name(curr_state), \
- model_get_event_name_##name(event), \
- model_get_state_name_##name(next_state), \
- model_is_final_state_##name(next_state)); \
- return true; \
- } \
- } \
- \
- trace_rv_retries_error(#name, model_get_event_name_##name(event)); \
- pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS) \
- " retries reached for event %s, resetting monitor %s", \
- model_get_event_name_##name(event), #name); \
- return false; \
+#elif RV_MON_TYPE == RV_MON_PER_TASK
+
+static inline bool da_event(struct da_monitor *da_mon, struct task_struct *tsk,
+ enum events event)
+{
+ enum states curr_state, next_state;
+
+ curr_state = READ_ONCE(da_mon->curr_state);
+ for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) {
+ next_state = model_get_next_state(curr_state, event);
+ if (next_state == INVALID_STATE) {
+ react(curr_state, event);
+ CONCATENATE(trace_error_, MONITOR_NAME)(tsk->pid,
+ model_get_state_name(curr_state),
+ model_get_event_name(event));
+ return false;
+ }
+ if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
+ CONCATENATE(trace_event_, MONITOR_NAME)(tsk->pid,
+ model_get_state_name(curr_state),
+ model_get_event_name(event),
+ model_get_state_name(next_state),
+ model_is_final_state(next_state));
+ return true;
+ }
+ }
+
+ trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(event));
+ pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS)
+ " retries reached for event %s, resetting monitor %s",
+ model_get_event_name(event), __stringify(MONITOR_NAME));
+ return false;
}
+#endif
/*
* Functions to define, init and get a global monitor.
*/
-#define DECLARE_DA_MON_INIT_GLOBAL(name, type) \
- \
-/* \
- * global monitor (a single variable) \
- */ \
-static struct da_monitor da_mon_##name; \
- \
-/* \
- * da_get_monitor_##name - return the global monitor address \
- */ \
-static struct da_monitor *da_get_monitor_##name(void) \
-{ \
- return &da_mon_##name; \
-} \
- \
-/* \
- * da_monitor_reset_all_##name - reset the single monitor \
- */ \
-static void da_monitor_reset_all_##name(void) \
-{ \
- da_monitor_reset_##name(da_get_monitor_##name()); \
-} \
- \
-/* \
- * da_monitor_init_##name - initialize a monitor \
- */ \
-static inline int da_monitor_init_##name(void) \
-{ \
- da_monitor_reset_all_##name(); \
- return 0; \
-} \
- \
-/* \
- * da_monitor_destroy_##name - destroy the monitor \
- */ \
-static inline void da_monitor_destroy_##name(void) \
-{ \
- return; \
+#if RV_MON_TYPE == RV_MON_GLOBAL
+
+/*
+ * global monitor (a single variable)
+ */
+static struct da_monitor da_mon_this;
+
+/*
+ * da_get_monitor - return the global monitor address
+ */
+static struct da_monitor *da_get_monitor(void)
+{
+ return &da_mon_this;
+}
+
+/*
+ * da_monitor_reset_all - reset the single monitor
+ */
+static void da_monitor_reset_all(void)
+{
+ da_monitor_reset(da_get_monitor());
+}
+
+/*
+ * da_monitor_init - initialize a monitor
+ */
+static inline int da_monitor_init(void)
+{
+ da_monitor_reset_all();
+ return 0;
+}
+
+/*
+ * da_monitor_destroy - destroy the monitor
+ */
+static inline void da_monitor_destroy(void)
+{
+ return;
}
/*
* Functions to define, init and get a per-cpu monitor.
*/
-#define DECLARE_DA_MON_INIT_PER_CPU(name, type) \
- \
-/* \
- * per-cpu monitor variables \
- */ \
-static DEFINE_PER_CPU(struct da_monitor, da_mon_##name); \
- \
-/* \
- * da_get_monitor_##name - return current CPU monitor address \
- */ \
-static struct da_monitor *da_get_monitor_##name(void) \
-{ \
- return this_cpu_ptr(&da_mon_##name); \
-} \
- \
-/* \
- * da_monitor_reset_all_##name - reset all CPUs' monitor \
- */ \
-static void da_monitor_reset_all_##name(void) \
-{ \
- struct da_monitor *da_mon; \
- int cpu; \
- for_each_cpu(cpu, cpu_online_mask) { \
- da_mon = per_cpu_ptr(&da_mon_##name, cpu); \
- da_monitor_reset_##name(da_mon); \
- } \
-} \
- \
-/* \
- * da_monitor_init_##name - initialize all CPUs' monitor \
- */ \
-static inline int da_monitor_init_##name(void) \
-{ \
- da_monitor_reset_all_##name(); \
- return 0; \
-} \
- \
-/* \
- * da_monitor_destroy_##name - destroy the monitor \
- */ \
-static inline void da_monitor_destroy_##name(void) \
-{ \
- return; \
+#elif RV_MON_TYPE == RV_MON_PER_CPU
+
+/*
+ * per-cpu monitor variables
+ */
+static DEFINE_PER_CPU(struct da_monitor, da_mon_this);
+
+/*
+ * da_get_monitor - return current CPU monitor address
+ */
+static struct da_monitor *da_get_monitor(void)
+{
+ return this_cpu_ptr(&da_mon_this);
+}
+
+/*
+ * da_monitor_reset_all - reset all CPUs' monitor
+ */
+static void da_monitor_reset_all(void)
+{
+ struct da_monitor *da_mon;
+ int cpu;
+ for_each_cpu(cpu, cpu_online_mask) {
+ da_mon = per_cpu_ptr(&da_mon_this, cpu);
+ da_monitor_reset(da_mon);
+ }
+}
+
+/*
+ * da_monitor_init - initialize all CPUs' monitor
+ */
+static inline int da_monitor_init(void)
+{
+ da_monitor_reset_all();
+ return 0;
+}
+
+/*
+ * da_monitor_destroy - destroy the monitor
+ */
+static inline void da_monitor_destroy(void)
+{
+ return;
}
/*
* Functions to define, init and get a per-task monitor.
*/
-#define DECLARE_DA_MON_INIT_PER_TASK(name, type) \
- \
-/* \
- * The per-task monitor is stored a vector in the task struct. This variable \
- * stores the position on the vector reserved for this monitor. \
- */ \
-static int task_mon_slot_##name = RV_PER_TASK_MONITOR_INIT; \
- \
-/* \
- * da_get_monitor_##name - return the monitor in the allocated slot for tsk \
- */ \
-static inline struct da_monitor *da_get_monitor_##name(struct task_struct *tsk) \
-{ \
- return &tsk->rv[task_mon_slot_##name].da_mon; \
-} \
- \
-static void da_monitor_reset_all_##name(void) \
-{ \
- struct task_struct *g, *p; \
- int cpu; \
- \
- read_lock(&tasklist_lock); \
- for_each_process_thread(g, p) \
- da_monitor_reset_##name(da_get_monitor_##name(p)); \
- for_each_present_cpu(cpu) \
- da_monitor_reset_##name(da_get_monitor_##name(idle_task(cpu))); \
- read_unlock(&tasklist_lock); \
-} \
- \
-/* \
- * da_monitor_init_##name - initialize the per-task monitor \
- * \
- * Try to allocate a slot in the task's vector of monitors. If there \
- * is an available slot, use it and reset all task's monitor. \
- */ \
-static int da_monitor_init_##name(void) \
-{ \
- int slot; \
- \
- slot = rv_get_task_monitor_slot(); \
- if (slot < 0 || slot >= RV_PER_TASK_MONITOR_INIT) \
- return slot; \
- \
- task_mon_slot_##name = slot; \
- \
- da_monitor_reset_all_##name(); \
- return 0; \
-} \
- \
-/* \
- * da_monitor_destroy_##name - return the allocated slot \
- */ \
-static inline void da_monitor_destroy_##name(void) \
-{ \
- if (task_mon_slot_##name == RV_PER_TASK_MONITOR_INIT) { \
- WARN_ONCE(1, "Disabling a disabled monitor: " #name); \
- return; \
- } \
- rv_put_task_monitor_slot(task_mon_slot_##name); \
- task_mon_slot_##name = RV_PER_TASK_MONITOR_INIT; \
- return; \
+#elif RV_MON_TYPE == RV_MON_PER_TASK
+
+/*
+ * The per-task monitor is stored a vector in the task struct. This variable
+ * stores the position on the vector reserved for this monitor.
+ */
+static int task_mon_slot = RV_PER_TASK_MONITOR_INIT;
+
+/*
+ * da_get_monitor - return the monitor in the allocated slot for tsk
+ */
+static inline struct da_monitor *da_get_monitor(struct task_struct *tsk)
+{
+ return &tsk->rv[task_mon_slot].da_mon;
+}
+
+static void da_monitor_reset_all(void)
+{
+ struct task_struct *g, *p;
+ int cpu;
+
+ read_lock(&tasklist_lock);
+ for_each_process_thread(g, p)
+ da_monitor_reset(da_get_monitor(p));
+ for_each_present_cpu(cpu)
+ da_monitor_reset(da_get_monitor(idle_task(cpu)));
+ read_unlock(&tasklist_lock);
}
/*
- * Handle event for implicit monitor: da_get_monitor_##name() will figure out
+ * da_monitor_init - initialize the per-task monitor
+ *
+ * Try to allocate a slot in the task's vector of monitors. If there
+ * is an available slot, use it and reset all task's monitor.
+ */
+static int da_monitor_init(void)
+{
+ int slot;
+
+ slot = rv_get_task_monitor_slot();
+ if (slot < 0 || slot >= RV_PER_TASK_MONITOR_INIT)
+ return slot;
+
+ task_mon_slot = slot;
+
+ da_monitor_reset_all();
+ return 0;
+}
+
+/*
+ * da_monitor_destroy - return the allocated slot
+ */
+static inline void da_monitor_destroy(void)
+{
+ if (task_mon_slot == RV_PER_TASK_MONITOR_INIT) {
+ WARN_ONCE(1, "Disabling a disabled monitor: " __stringify(MONITOR_NAME));
+ return;
+ }
+ rv_put_task_monitor_slot(task_mon_slot);
+ task_mon_slot = RV_PER_TASK_MONITOR_INIT;
+ return;
+}
+#endif
+
+/*
+ * Handle event for implicit monitor: da_get_monitor() will figure out
* the monitor.
*/
-#define DECLARE_DA_MON_MONITOR_HANDLER_IMPLICIT(name, type) \
- \
-static inline void __da_handle_event_##name(struct da_monitor *da_mon, \
- enum events_##name event) \
-{ \
- bool retval; \
- \
- retval = da_event_##name(da_mon, event); \
- if (!retval) \
- da_monitor_reset_##name(da_mon); \
-} \
- \
-/* \
- * da_handle_event_##name - handle an event \
- */ \
-static inline void da_handle_event_##name(enum events_##name event) \
-{ \
- struct da_monitor *da_mon = da_get_monitor_##name(); \
- bool retval; \
- \
- retval = da_monitor_handling_event_##name(da_mon); \
- if (!retval) \
- return; \
- \
- __da_handle_event_##name(da_mon, event); \
-} \
- \
-/* \
- * da_handle_start_event_##name - start monitoring or handle event \
- * \
- * This function is used to notify the monitor that the system is returning \
- * to the initial state, so the monitor can start monitoring in the next event. \
- * Thus: \
- * \
- * If the monitor already started, handle the event. \
- * If the monitor did not start yet, start the monitor but skip the event. \
- */ \
-static inline bool da_handle_start_event_##name(enum events_##name event) \
-{ \
- struct da_monitor *da_mon; \
- \
- if (!da_monitor_enabled_##name()) \
- return 0; \
- \
- da_mon = da_get_monitor_##name(); \
- \
- if (unlikely(!da_monitoring_##name(da_mon))) { \
- da_monitor_start_##name(da_mon); \
- return 0; \
- } \
- \
- __da_handle_event_##name(da_mon, event); \
- \
- return 1; \
-} \
- \
-/* \
- * da_handle_start_run_event_##name - start monitoring and handle event \
- * \
- * This function is used to notify the monitor that the system is in the \
- * initial state, so the monitor can start monitoring and handling event. \
- */ \
-static inline bool da_handle_start_run_event_##name(enum events_##name event) \
-{ \
- struct da_monitor *da_mon; \
- \
- if (!da_monitor_enabled_##name()) \
- return 0; \
- \
- da_mon = da_get_monitor_##name(); \
- \
- if (unlikely(!da_monitoring_##name(da_mon))) \
- da_monitor_start_##name(da_mon); \
- \
- __da_handle_event_##name(da_mon, event); \
- \
- return 1; \
+#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
+
+static inline void __da_handle_event(struct da_monitor *da_mon,
+ enum events event)
+{
+ bool retval;
+
+ retval = da_event(da_mon, event);
+ if (!retval)
+ da_monitor_reset(da_mon);
+}
+
+/*
+ * da_handle_event - handle an event
+ */
+static inline void da_handle_event(enum events event)
+{
+ struct da_monitor *da_mon = da_get_monitor();
+ bool retval;
+
+ retval = da_monitor_handling_event(da_mon);
+ if (!retval)
+ return;
+
+ __da_handle_event(da_mon, event);
+}
+
+/*
+ * da_handle_start_event - start monitoring or handle event
+ *
+ * This function is used to notify the monitor that the system is returning
+ * to the initial state, so the monitor can start monitoring in the next event.
+ * Thus:
+ *
+ * If the monitor already started, handle the event.
+ * If the monitor did not start yet, start the monitor but skip the event.
+ */
+static inline bool da_handle_start_event(enum events event)
+{
+ struct da_monitor *da_mon;
+
+ if (!da_monitor_enabled())
+ return 0;
+
+ da_mon = da_get_monitor();
+
+ if (unlikely(!da_monitoring(da_mon))) {
+ da_monitor_start(da_mon);
+ return 0;
+ }
+
+ __da_handle_event(da_mon, event);
+
+ return 1;
+}
+
+/*
+ * da_handle_start_run_event - start monitoring and handle event
+ *
+ * This function is used to notify the monitor that the system is in the
+ * initial state, so the monitor can start monitoring and handling event.
+ */
+static inline bool da_handle_start_run_event(enum events event)
+{
+ struct da_monitor *da_mon;
+
+ if (!da_monitor_enabled())
+ return 0;
+
+ da_mon = da_get_monitor();
+
+ if (unlikely(!da_monitoring(da_mon)))
+ da_monitor_start(da_mon);
+
+ __da_handle_event(da_mon, event);
+
+ return 1;
}
/*
* Handle event for per task.
*/
-#define DECLARE_DA_MON_MONITOR_HANDLER_PER_TASK(name, type) \
- \
-static inline void \
-__da_handle_event_##name(struct da_monitor *da_mon, struct task_struct *tsk, \
- enum events_##name event) \
-{ \
- bool retval; \
- \
- retval = da_event_##name(da_mon, tsk, event); \
- if (!retval) \
- da_monitor_reset_##name(da_mon); \
-} \
- \
-/* \
- * da_handle_event_##name - handle an event \
- */ \
-static inline void \
-da_handle_event_##name(struct task_struct *tsk, enum events_##name event) \
-{ \
- struct da_monitor *da_mon = da_get_monitor_##name(tsk); \
- bool retval; \
- \
- retval = da_monitor_handling_event_##name(da_mon); \
- if (!retval) \
- return; \
- \
- __da_handle_event_##name(da_mon, tsk, event); \
-} \
- \
-/* \
- * da_handle_start_event_##name - start monitoring or handle event \
- * \
- * This function is used to notify the monitor that the system is returning \
- * to the initial state, so the monitor can start monitoring in the next event. \
- * Thus: \
- * \
- * If the monitor already started, handle the event. \
- * If the monitor did not start yet, start the monitor but skip the event. \
- */ \
-static inline bool \
-da_handle_start_event_##name(struct task_struct *tsk, enum events_##name event) \
-{ \
- struct da_monitor *da_mon; \
- \
- if (!da_monitor_enabled_##name()) \
- return 0; \
- \
- da_mon = da_get_monitor_##name(tsk); \
- \
- if (unlikely(!da_monitoring_##name(da_mon))) { \
- da_monitor_start_##name(da_mon); \
- return 0; \
- } \
- \
- __da_handle_event_##name(da_mon, tsk, event); \
- \
- return 1; \
-} \
- \
-/* \
- * da_handle_start_run_event_##name - start monitoring and handle event \
- * \
- * This function is used to notify the monitor that the system is in the \
- * initial state, so the monitor can start monitoring and handling event. \
- */ \
-static inline bool \
-da_handle_start_run_event_##name(struct task_struct *tsk, enum events_##name event) \
-{ \
- struct da_monitor *da_mon; \
- \
- if (!da_monitor_enabled_##name()) \
- return 0; \
- \
- da_mon = da_get_monitor_##name(tsk); \
- \
- if (unlikely(!da_monitoring_##name(da_mon))) \
- da_monitor_start_##name(da_mon); \
- \
- __da_handle_event_##name(da_mon, tsk, event); \
- \
- return 1; \
+#elif RV_MON_TYPE == RV_MON_PER_TASK
+
+static inline void
+__da_handle_event(struct da_monitor *da_mon, struct task_struct *tsk,
+ enum events event)
+{
+ bool retval;
+
+ retval = da_event(da_mon, tsk, event);
+ if (!retval)
+ da_monitor_reset(da_mon);
+}
+
+/*
+ * da_handle_event - handle an event
+ */
+static inline void
+da_handle_event(struct task_struct *tsk, enum events event)
+{
+ struct da_monitor *da_mon = da_get_monitor(tsk);
+ bool retval;
+
+ retval = da_monitor_handling_event(da_mon);
+ if (!retval)
+ return;
+
+ __da_handle_event(da_mon, tsk, event);
+}
+
+/*
+ * da_handle_start_event - start monitoring or handle event
+ *
+ * This function is used to notify the monitor that the system is returning
+ * to the initial state, so the monitor can start monitoring in the next event.
+ * Thus:
+ *
+ * If the monitor already started, handle the event.
+ * If the monitor did not start yet, start the monitor but skip the event.
+ */
+static inline bool
+da_handle_start_event(struct task_struct *tsk, enum events event)
+{
+ struct da_monitor *da_mon;
+
+ if (!da_monitor_enabled())
+ return 0;
+
+ da_mon = da_get_monitor(tsk);
+
+ if (unlikely(!da_monitoring(da_mon))) {
+ da_monitor_start(da_mon);
+ return 0;
+ }
+
+ __da_handle_event(da_mon, tsk, event);
+
+ return 1;
+}
+
+/*
+ * da_handle_start_run_event - start monitoring and handle event
+ *
+ * This function is used to notify the monitor that the system is in the
+ * initial state, so the monitor can start monitoring and handling event.
+ */
+static inline bool
+da_handle_start_run_event(struct task_struct *tsk, enum events event)
+{
+ struct da_monitor *da_mon;
+
+ if (!da_monitor_enabled())
+ return 0;
+
+ da_mon = da_get_monitor(tsk);
+
+ if (unlikely(!da_monitoring(da_mon)))
+ da_monitor_start(da_mon);
+
+ __da_handle_event(da_mon, tsk, event);
+
+ return 1;
}
+#endif
/*
* Entry point for the global monitor.
*/
-#define DECLARE_DA_MON_GLOBAL(name, type) \
- \
-DECLARE_AUTOMATA_HELPERS(name, type) \
-DECLARE_DA_MON_GENERIC_HELPERS(name, type) \
-DECLARE_DA_MON_MODEL_HANDLER_IMPLICIT(name, type) \
-DECLARE_DA_MON_INIT_GLOBAL(name, type) \
-DECLARE_DA_MON_MONITOR_HANDLER_IMPLICIT(name, type)
+#define DECLARE_DA_MON_GLOBAL(name, type)
/*
* Entry point for the per-cpu monitor.
*/
-#define DECLARE_DA_MON_PER_CPU(name, type) \
- \
-DECLARE_AUTOMATA_HELPERS(name, type) \
-DECLARE_DA_MON_GENERIC_HELPERS(name, type) \
-DECLARE_DA_MON_MODEL_HANDLER_IMPLICIT(name, type) \
-DECLARE_DA_MON_INIT_PER_CPU(name, type) \
-DECLARE_DA_MON_MONITOR_HANDLER_IMPLICIT(name, type)
+#define DECLARE_DA_MON_PER_CPU(name, type)
/*
* Entry point for the per-task monitor.
*/
-#define DECLARE_DA_MON_PER_TASK(name, type) \
- \
-DECLARE_AUTOMATA_HELPERS(name, type) \
-DECLARE_DA_MON_GENERIC_HELPERS(name, type) \
-DECLARE_DA_MON_MODEL_HANDLER_PER_TASK(name, type) \
-DECLARE_DA_MON_INIT_PER_TASK(name, type) \
-DECLARE_DA_MON_MONITOR_HANDLER_PER_TASK(name, type)
+#define DECLARE_DA_MON_PER_TASK(name, type)
diff --git a/kernel/trace/rv/monitors/nrp/nrp.c b/kernel/trace/rv/monitors/nrp/nrp.c
index 5a83b7171432..4b5646a70094 100644
--- a/kernel/trace/rv/monitors/nrp/nrp.c
+++ b/kernel/trace/rv/monitors/nrp/nrp.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "nrp"
@@ -15,17 +14,16 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_TASK
#include "nrp.h"
-
-static struct rv_monitor rv_nrp;
-DECLARE_DA_MON_PER_TASK(nrp, unsigned char);
+#include <rv/da_monitor.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
- da_handle_event_nrp(current, irq_entry_nrp);
+ da_handle_event(current, irq_entry_nrp);
}
static void attach_vector_irq(void)
@@ -60,7 +58,7 @@ static void detach_vector_irq(void) { }
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
- da_handle_event_nrp(current, irq_entry_nrp);
+ da_handle_event(current, irq_entry_nrp);
}
static void handle_sched_need_resched(void *data, struct task_struct *tsk,
@@ -72,22 +70,22 @@ static void handle_sched_need_resched(void *data, struct task_struct *tsk,
* 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);
+ da_handle_start_event(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);
+ da_handle_event(current, schedule_entry_preempt_nrp);
else
- da_handle_event_nrp(current, schedule_entry_nrp);
+ da_handle_event(current, schedule_entry_nrp);
}
static int enable_nrp(void)
{
int retval;
- retval = da_monitor_init_nrp();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -101,33 +99,33 @@ static int enable_nrp(void)
static void disable_nrp(void)
{
- rv_nrp.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("nrp", irq_handler_entry, handle_irq_entry);
rv_detach_trace_probe("nrp", sched_set_need_resched_tp, handle_sched_need_resched);
rv_detach_trace_probe("nrp", sched_entry_tp, handle_schedule_entry);
detach_vector_irq();
- da_monitor_destroy_nrp();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_nrp = {
+static struct rv_monitor rv_this = {
.name = "nrp",
.description = "need resched preempts.",
.enable = enable_nrp,
.disable = disable_nrp,
- .reset = da_monitor_reset_all_nrp,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_nrp(void)
{
- return rv_register_monitor(&rv_nrp, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_nrp(void)
{
- rv_unregister_monitor(&rv_nrp);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_nrp);
diff --git a/kernel/trace/rv/monitors/nrp/nrp.h b/kernel/trace/rv/monitors/nrp/nrp.h
index c9f12207cbf6..c2ec83da2124 100644
--- a/kernel/trace/rv/monitors/nrp/nrp.h
+++ b/kernel/trace/rv/monitors/nrp/nrp.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME nrp
+
enum states_nrp {
preempt_irq_nrp = 0,
any_thread_running_nrp,
diff --git a/kernel/trace/rv/monitors/opid/opid.c b/kernel/trace/rv/monitors/opid/opid.c
index 50d64e7fb8c4..25a40e90fa40 100644
--- a/kernel/trace/rv/monitors/opid/opid.c
+++ b/kernel/trace/rv/monitors/opid/opid.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "opid"
@@ -16,17 +15,16 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_CPU
#include "opid.h"
-
-static struct rv_monitor rv_opid;
-DECLARE_DA_MON_PER_CPU(opid, unsigned char);
+#include <rv/da_monitor.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
- da_handle_event_opid(irq_entry_opid);
+ da_handle_event(irq_entry_opid);
}
static void attach_vector_irq(void)
@@ -61,52 +59,52 @@ static void detach_vector_irq(void) { }
static void handle_irq_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_opid(irq_disable_opid);
+ da_handle_event(irq_disable_opid);
}
static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_opid(irq_enable_opid);
+ da_handle_event(irq_enable_opid);
}
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
- da_handle_event_opid(irq_entry_opid);
+ da_handle_event(irq_entry_opid);
}
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_opid(preempt_disable_opid);
+ da_handle_event(preempt_disable_opid);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_opid(preempt_enable_opid);
+ da_handle_event(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);
+ da_handle_event(sched_need_resched_opid);
else
- da_handle_start_event_opid(sched_need_resched_opid);
+ da_handle_start_event(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);
+ da_handle_event(sched_waking_opid);
else
- da_handle_start_event_opid(sched_waking_opid);
+ da_handle_start_event(sched_waking_opid);
}
static int enable_opid(void)
{
int retval;
- retval = da_monitor_init_opid();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -124,7 +122,7 @@ static int enable_opid(void)
static void disable_opid(void)
{
- rv_opid.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("opid", irq_disable, handle_irq_disable);
rv_detach_trace_probe("opid", irq_enable, handle_irq_enable);
@@ -135,29 +133,29 @@ static void disable_opid(void)
rv_detach_trace_probe("opid", sched_waking, handle_sched_waking);
detach_vector_irq();
- da_monitor_destroy_opid();
+ da_monitor_destroy();
}
/*
* This is the monitor register section.
*/
-static struct rv_monitor rv_opid = {
+static struct rv_monitor rv_this = {
.name = "opid",
.description = "operations with preemption and irq disabled.",
.enable = enable_opid,
.disable = disable_opid,
- .reset = da_monitor_reset_all_opid,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_opid(void)
{
- return rv_register_monitor(&rv_opid, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_opid(void)
{
- rv_unregister_monitor(&rv_opid);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_opid);
diff --git a/kernel/trace/rv/monitors/opid/opid.h b/kernel/trace/rv/monitors/opid/opid.h
index b4b8c2ff7f64..5014f1b85ecf 100644
--- a/kernel/trace/rv/monitors/opid/opid.h
+++ b/kernel/trace/rv/monitors/opid/opid.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME opid
+
enum states_opid {
disabled_opid = 0,
enabled_opid,
diff --git a/kernel/trace/rv/monitors/sco/sco.c b/kernel/trace/rv/monitors/sco/sco.c
index 04c36405e2e3..5a3bd5e16e62 100644
--- a/kernel/trace/rv/monitors/sco/sco.c
+++ b/kernel/trace/rv/monitors/sco/sco.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "sco"
@@ -14,31 +13,30 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_CPU
#include "sco.h"
-
-static struct rv_monitor rv_sco;
-DECLARE_DA_MON_PER_CPU(sco, unsigned char);
+#include <rv/da_monitor.h>
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
- da_handle_start_event_sco(sched_set_state_sco);
+ da_handle_start_event(sched_set_state_sco);
}
static void handle_schedule_entry(void *data, bool preempt)
{
- da_handle_event_sco(schedule_entry_sco);
+ da_handle_event(schedule_entry_sco);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
- da_handle_start_event_sco(schedule_exit_sco);
+ da_handle_start_event(schedule_exit_sco);
}
static int enable_sco(void)
{
int retval;
- retval = da_monitor_init_sco();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -51,32 +49,32 @@ static int enable_sco(void)
static void disable_sco(void)
{
- rv_sco.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("sco", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("sco", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("sco", sched_exit_tp, handle_schedule_exit);
- da_monitor_destroy_sco();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_sco = {
+static struct rv_monitor rv_this = {
.name = "sco",
.description = "scheduling context operations.",
.enable = enable_sco,
.disable = disable_sco,
- .reset = da_monitor_reset_all_sco,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_sco(void)
{
- return rv_register_monitor(&rv_sco, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_sco(void)
{
- rv_unregister_monitor(&rv_sco);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_sco);
diff --git a/kernel/trace/rv/monitors/sco/sco.h b/kernel/trace/rv/monitors/sco/sco.h
index 7a4c1f2d5ca1..06b1c420ce54 100644
--- a/kernel/trace/rv/monitors/sco/sco.h
+++ b/kernel/trace/rv/monitors/sco/sco.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME sco
+
enum states_sco {
thread_context_sco = 0,
scheduling_context_sco,
diff --git a/kernel/trace/rv/monitors/scpd/scpd.c b/kernel/trace/rv/monitors/scpd/scpd.c
index 1e351ba52fee..83b48627dc9f 100644
--- a/kernel/trace/rv/monitors/scpd/scpd.c
+++ b/kernel/trace/rv/monitors/scpd/scpd.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "scpd"
@@ -15,36 +14,35 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_CPU
#include "scpd.h"
-
-static struct rv_monitor rv_scpd;
-DECLARE_DA_MON_PER_CPU(scpd, unsigned char);
+#include <rv/da_monitor.h>
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_scpd(preempt_disable_scpd);
+ da_handle_event(preempt_disable_scpd);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_start_event_scpd(preempt_enable_scpd);
+ da_handle_start_event(preempt_enable_scpd);
}
static void handle_schedule_entry(void *data, bool preempt)
{
- da_handle_event_scpd(schedule_entry_scpd);
+ da_handle_event(schedule_entry_scpd);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
- da_handle_event_scpd(schedule_exit_scpd);
+ da_handle_event(schedule_exit_scpd);
}
static int enable_scpd(void)
{
int retval;
- retval = da_monitor_init_scpd();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -58,33 +56,33 @@ static int enable_scpd(void)
static void disable_scpd(void)
{
- rv_scpd.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("scpd", preempt_disable, handle_preempt_disable);
rv_detach_trace_probe("scpd", preempt_enable, handle_preempt_enable);
rv_detach_trace_probe("scpd", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("scpd", sched_exit_tp, handle_schedule_exit);
- da_monitor_destroy_scpd();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_scpd = {
+static struct rv_monitor rv_this = {
.name = "scpd",
.description = "schedule called with preemption disabled.",
.enable = enable_scpd,
.disable = disable_scpd,
- .reset = da_monitor_reset_all_scpd,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_scpd(void)
{
- return rv_register_monitor(&rv_scpd, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_scpd(void)
{
- rv_unregister_monitor(&rv_scpd);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_scpd);
diff --git a/kernel/trace/rv/monitors/scpd/scpd.h b/kernel/trace/rv/monitors/scpd/scpd.h
index 295f735a5811..4a725a68085a 100644
--- a/kernel/trace/rv/monitors/scpd/scpd.h
+++ b/kernel/trace/rv/monitors/scpd/scpd.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME scpd
+
enum states_scpd {
cant_sched_scpd = 0,
can_sched_scpd,
diff --git a/kernel/trace/rv/monitors/snep/snep.c b/kernel/trace/rv/monitors/snep/snep.c
index 558950f524a5..b80b73795dec 100644
--- a/kernel/trace/rv/monitors/snep/snep.c
+++ b/kernel/trace/rv/monitors/snep/snep.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "snep"
@@ -15,36 +14,35 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_CPU
#include "snep.h"
-
-static struct rv_monitor rv_snep;
-DECLARE_DA_MON_PER_CPU(snep, unsigned char);
+#include <rv/da_monitor.h>
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_start_event_snep(preempt_disable_snep);
+ da_handle_start_event(preempt_disable_snep);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_start_event_snep(preempt_enable_snep);
+ da_handle_start_event(preempt_enable_snep);
}
static void handle_schedule_entry(void *data, bool preempt)
{
- da_handle_event_snep(schedule_entry_snep);
+ da_handle_event(schedule_entry_snep);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
- da_handle_start_event_snep(schedule_exit_snep);
+ da_handle_start_event(schedule_exit_snep);
}
static int enable_snep(void)
{
int retval;
- retval = da_monitor_init_snep();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -58,33 +56,33 @@ static int enable_snep(void)
static void disable_snep(void)
{
- rv_snep.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("snep", preempt_disable, handle_preempt_disable);
rv_detach_trace_probe("snep", preempt_enable, handle_preempt_enable);
rv_detach_trace_probe("snep", sched_entry_tp, handle_schedule_entry);
rv_detach_trace_probe("snep", sched_exit_tp, handle_schedule_exit);
- da_monitor_destroy_snep();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_snep = {
+static struct rv_monitor rv_this = {
.name = "snep",
.description = "schedule does not enable preempt.",
.enable = enable_snep,
.disable = disable_snep,
- .reset = da_monitor_reset_all_snep,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_snep(void)
{
- return rv_register_monitor(&rv_snep, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_snep(void)
{
- rv_unregister_monitor(&rv_snep);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_snep);
diff --git a/kernel/trace/rv/monitors/snep/snep.h b/kernel/trace/rv/monitors/snep/snep.h
index 4cd9abb77b7b..753080dc5fa1 100644
--- a/kernel/trace/rv/monitors/snep/snep.h
+++ b/kernel/trace/rv/monitors/snep/snep.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME snep
+
enum states_snep {
non_scheduling_context_snep = 0,
scheduling_contex_snep,
diff --git a/kernel/trace/rv/monitors/snroc/snroc.c b/kernel/trace/rv/monitors/snroc/snroc.c
index 540e686e699f..f168b1a4b12c 100644
--- a/kernel/trace/rv/monitors/snroc/snroc.c
+++ b/kernel/trace/rv/monitors/snroc/snroc.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "snroc"
@@ -14,14 +13,13 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_TASK
#include "snroc.h"
-
-static struct rv_monitor rv_snroc;
-DECLARE_DA_MON_PER_TASK(snroc, unsigned char);
+#include <rv/da_monitor.h>
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
- da_handle_event_snroc(tsk, sched_set_state_snroc);
+ da_handle_event(tsk, sched_set_state_snroc);
}
static void handle_sched_switch(void *data, bool preempt,
@@ -29,15 +27,15 @@ static void handle_sched_switch(void *data, bool preempt,
struct task_struct *next,
unsigned int prev_state)
{
- da_handle_start_event_snroc(prev, sched_switch_out_snroc);
- da_handle_event_snroc(next, sched_switch_in_snroc);
+ da_handle_start_event(prev, sched_switch_out_snroc);
+ da_handle_event(next, sched_switch_in_snroc);
}
static int enable_snroc(void)
{
int retval;
- retval = da_monitor_init_snroc();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -49,31 +47,31 @@ static int enable_snroc(void)
static void disable_snroc(void)
{
- rv_snroc.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("snroc", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("snroc", sched_switch, handle_sched_switch);
- da_monitor_destroy_snroc();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_snroc = {
+static struct rv_monitor rv_this = {
.name = "snroc",
.description = "set non runnable on its own context.",
.enable = enable_snroc,
.disable = disable_snroc,
- .reset = da_monitor_reset_all_snroc,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_snroc(void)
{
- return rv_register_monitor(&rv_snroc, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_snroc(void)
{
- rv_unregister_monitor(&rv_snroc);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_snroc);
diff --git a/kernel/trace/rv/monitors/snroc/snroc.h b/kernel/trace/rv/monitors/snroc/snroc.h
index c3650a2b1b10..ada5ee08bdab 100644
--- a/kernel/trace/rv/monitors/snroc/snroc.h
+++ b/kernel/trace/rv/monitors/snroc/snroc.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME snroc
+
enum states_snroc {
other_context_snroc = 0,
own_context_snroc,
diff --git a/kernel/trace/rv/monitors/sssw/sssw.c b/kernel/trace/rv/monitors/sssw/sssw.c
index 84b8d890d9d4..a91321c890cd 100644
--- a/kernel/trace/rv/monitors/sssw/sssw.c
+++ b/kernel/trace/rv/monitors/sssw/sssw.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "sssw"
@@ -15,17 +14,16 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_TASK
#include "sssw.h"
-
-static struct rv_monitor rv_sssw;
-DECLARE_DA_MON_PER_TASK(sssw, unsigned char);
+#include <rv/da_monitor.h>
static void handle_sched_set_state(void *data, struct task_struct *tsk, int state)
{
if (state == TASK_RUNNING)
- da_handle_start_event_sssw(tsk, sched_set_state_runnable_sssw);
+ da_handle_start_event(tsk, sched_set_state_runnable_sssw);
else
- da_handle_event_sssw(tsk, sched_set_state_sleepable_sssw);
+ da_handle_event(tsk, sched_set_state_sleepable_sssw);
}
static void handle_sched_switch(void *data, bool preempt,
@@ -34,15 +32,15 @@ static void handle_sched_switch(void *data, bool preempt,
unsigned int prev_state)
{
if (preempt)
- da_handle_event_sssw(prev, sched_switch_preempt_sssw);
+ da_handle_event(prev, sched_switch_preempt_sssw);
else if (prev_state == TASK_RUNNING)
- da_handle_event_sssw(prev, sched_switch_yield_sssw);
+ da_handle_event(prev, sched_switch_yield_sssw);
else if (prev_state == TASK_RTLOCK_WAIT)
/* special case of sleeping task with racy conditions */
- da_handle_event_sssw(prev, sched_switch_blocking_sssw);
+ da_handle_event(prev, sched_switch_blocking_sssw);
else
- da_handle_event_sssw(prev, sched_switch_suspend_sssw);
- da_handle_event_sssw(next, sched_switch_in_sssw);
+ da_handle_event(prev, sched_switch_suspend_sssw);
+ da_handle_event(next, sched_switch_in_sssw);
}
static void handle_sched_wakeup(void *data, struct task_struct *p)
@@ -51,21 +49,21 @@ 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);
+ da_handle_start_event(p, sched_wakeup_sssw);
}
static void handle_signal_deliver(void *data, int sig,
struct kernel_siginfo *info,
struct k_sigaction *ka)
{
- da_handle_event_sssw(current, signal_deliver_sssw);
+ da_handle_event(current, signal_deliver_sssw);
}
static int enable_sssw(void)
{
int retval;
- retval = da_monitor_init_sssw();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -79,33 +77,33 @@ static int enable_sssw(void)
static void disable_sssw(void)
{
- rv_sssw.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("sssw", sched_set_state_tp, handle_sched_set_state);
rv_detach_trace_probe("sssw", sched_switch, handle_sched_switch);
rv_detach_trace_probe("sssw", sched_wakeup, handle_sched_wakeup);
rv_detach_trace_probe("sssw", signal_deliver, handle_signal_deliver);
- da_monitor_destroy_sssw();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_sssw = {
+static struct rv_monitor rv_this = {
.name = "sssw",
.description = "set state sleep and wakeup.",
.enable = enable_sssw,
.disable = disable_sssw,
- .reset = da_monitor_reset_all_sssw,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_sssw(void)
{
- return rv_register_monitor(&rv_sssw, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_sssw(void)
{
- rv_unregister_monitor(&rv_sssw);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_sssw);
diff --git a/kernel/trace/rv/monitors/sssw/sssw.h b/kernel/trace/rv/monitors/sssw/sssw.h
index 243d54050c94..8409eaadc7e0 100644
--- a/kernel/trace/rv/monitors/sssw/sssw.h
+++ b/kernel/trace/rv/monitors/sssw/sssw.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME sssw
+
enum states_sssw {
runnable_sssw = 0,
signal_wakeup_sssw,
diff --git a/kernel/trace/rv/monitors/sts/sts.c b/kernel/trace/rv/monitors/sts/sts.c
index c4a9cd67c1d2..ce031cbf202a 100644
--- a/kernel/trace/rv/monitors/sts/sts.c
+++ b/kernel/trace/rv/monitors/sts/sts.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "sts"
@@ -16,17 +15,16 @@
#include <rv_trace.h>
#include <monitors/sched/sched.h>
+#define RV_MON_TYPE RV_MON_PER_CPU
#include "sts.h"
-
-static struct rv_monitor rv_sts;
-DECLARE_DA_MON_PER_CPU(sts, unsigned char);
+#include <rv/da_monitor.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>
static void handle_vector_irq_entry(void *data, int vector)
{
- da_handle_event_sts(irq_entry_sts);
+ da_handle_event(irq_entry_sts);
}
static void attach_vector_irq(void)
@@ -61,17 +59,17 @@ static void detach_vector_irq(void) { }
static void handle_irq_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_sts(irq_disable_sts);
+ da_handle_event(irq_disable_sts);
}
static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_sts(irq_enable_sts);
+ da_handle_event(irq_enable_sts);
}
static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
- da_handle_event_sts(irq_entry_sts);
+ da_handle_event(irq_entry_sts);
}
static void handle_sched_switch(void *data, bool preempt,
@@ -79,24 +77,24 @@ static void handle_sched_switch(void *data, bool preempt,
struct task_struct *next,
unsigned int prev_state)
{
- da_handle_event_sts(sched_switch_sts);
+ da_handle_event(sched_switch_sts);
}
static void handle_schedule_entry(void *data, bool preempt)
{
- da_handle_event_sts(schedule_entry_sts);
+ da_handle_event(schedule_entry_sts);
}
static void handle_schedule_exit(void *data, bool is_switch)
{
- da_handle_start_event_sts(schedule_exit_sts);
+ da_handle_start_event(schedule_exit_sts);
}
static int enable_sts(void)
{
int retval;
- retval = da_monitor_init_sts();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -113,7 +111,7 @@ static int enable_sts(void)
static void disable_sts(void)
{
- rv_sts.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("sts", irq_disable, handle_irq_disable);
rv_detach_trace_probe("sts", irq_enable, handle_irq_enable);
@@ -123,29 +121,29 @@ static void disable_sts(void)
rv_detach_trace_probe("sts", sched_exit_tp, handle_schedule_exit);
detach_vector_irq();
- da_monitor_destroy_sts();
+ da_monitor_destroy();
}
/*
* This is the monitor register section.
*/
-static struct rv_monitor rv_sts = {
+static struct rv_monitor rv_this = {
.name = "sts",
.description = "schedule implies task switch.",
.enable = enable_sts,
.disable = disable_sts,
- .reset = da_monitor_reset_all_sts,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_sts(void)
{
- return rv_register_monitor(&rv_sts, &rv_sched);
+ return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_sts(void)
{
- rv_unregister_monitor(&rv_sts);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_sts);
diff --git a/kernel/trace/rv/monitors/sts/sts.h b/kernel/trace/rv/monitors/sts/sts.h
index 3368b6599a00..3779d7f99404 100644
--- a/kernel/trace/rv/monitors/sts/sts.h
+++ b/kernel/trace/rv/monitors/sts/sts.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME sts
+
enum states_sts {
can_sched_sts = 0,
cant_sched_sts,
diff --git a/kernel/trace/rv/monitors/wip/wip.c b/kernel/trace/rv/monitors/wip/wip.c
index 4b4e99615a11..22d77ec42463 100644
--- a/kernel/trace/rv/monitors/wip/wip.c
+++ b/kernel/trace/rv/monitors/wip/wip.c
@@ -6,7 +6,6 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "wip"
@@ -14,31 +13,30 @@
#include <trace/events/sched.h>
#include <trace/events/preemptirq.h>
+#define RV_MON_TYPE RV_MON_PER_CPU
#include "wip.h"
-
-static struct rv_monitor rv_wip;
-DECLARE_DA_MON_PER_CPU(wip, unsigned char);
+#include <rv/da_monitor.h>
static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_event_wip(preempt_disable_wip);
+ da_handle_event(preempt_disable_wip);
}
static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
- da_handle_start_event_wip(preempt_enable_wip);
+ da_handle_start_event(preempt_enable_wip);
}
static void handle_sched_waking(void *data, struct task_struct *task)
{
- da_handle_event_wip(sched_waking_wip);
+ da_handle_event(sched_waking_wip);
}
static int enable_wip(void)
{
int retval;
- retval = da_monitor_init_wip();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -51,32 +49,32 @@ static int enable_wip(void)
static void disable_wip(void)
{
- rv_wip.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("wip", preempt_disable, handle_preempt_disable);
rv_detach_trace_probe("wip", preempt_enable, handle_preempt_enable);
rv_detach_trace_probe("wip", sched_waking, handle_sched_waking);
- da_monitor_destroy_wip();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_wip = {
+static struct rv_monitor rv_this = {
.name = "wip",
.description = "wakeup in preemptive per-cpu testing monitor.",
.enable = enable_wip,
.disable = disable_wip,
- .reset = da_monitor_reset_all_wip,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_wip(void)
{
- return rv_register_monitor(&rv_wip, NULL);
+ return rv_register_monitor(&rv_this, NULL);
}
static void __exit unregister_wip(void)
{
- rv_unregister_monitor(&rv_wip);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_wip);
diff --git a/kernel/trace/rv/monitors/wip/wip.h b/kernel/trace/rv/monitors/wip/wip.h
index c7193748bf36..cfdc52975354 100644
--- a/kernel/trace/rv/monitors/wip/wip.h
+++ b/kernel/trace/rv/monitors/wip/wip.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME wip
+
enum states_wip {
preemptive_wip = 0,
non_preemptive_wip,
diff --git a/kernel/trace/rv/monitors/wwnr/wwnr.c b/kernel/trace/rv/monitors/wwnr/wwnr.c
index 4145bea2729e..579e7e217ee0 100644
--- a/kernel/trace/rv/monitors/wwnr/wwnr.c
+++ b/kernel/trace/rv/monitors/wwnr/wwnr.c
@@ -6,40 +6,38 @@
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
-#include <rv/da_monitor.h>
#define MODULE_NAME "wwnr"
#include <rv_trace.h>
#include <trace/events/sched.h>
+#define RV_MON_TYPE RV_MON_PER_TASK
#include "wwnr.h"
-
-static struct rv_monitor rv_wwnr;
-DECLARE_DA_MON_PER_TASK(wwnr, unsigned char);
+#include <rv/da_monitor.h>
static void handle_switch(void *data, bool preempt, struct task_struct *p,
struct task_struct *n, unsigned int prev_state)
{
/* start monitoring only after the first suspension */
if (prev_state == TASK_INTERRUPTIBLE)
- da_handle_start_event_wwnr(p, switch_out_wwnr);
+ da_handle_start_event(p, switch_out_wwnr);
else
- da_handle_event_wwnr(p, switch_out_wwnr);
+ da_handle_event(p, switch_out_wwnr);
- da_handle_event_wwnr(n, switch_in_wwnr);
+ da_handle_event(n, switch_in_wwnr);
}
static void handle_wakeup(void *data, struct task_struct *p)
{
- da_handle_event_wwnr(p, wakeup_wwnr);
+ da_handle_event(p, wakeup_wwnr);
}
static int enable_wwnr(void)
{
int retval;
- retval = da_monitor_init_wwnr();
+ retval = da_monitor_init();
if (retval)
return retval;
@@ -51,31 +49,31 @@ static int enable_wwnr(void)
static void disable_wwnr(void)
{
- rv_wwnr.enabled = 0;
+ rv_this.enabled = 0;
rv_detach_trace_probe("wwnr", sched_switch, handle_switch);
rv_detach_trace_probe("wwnr", sched_wakeup, handle_wakeup);
- da_monitor_destroy_wwnr();
+ da_monitor_destroy();
}
-static struct rv_monitor rv_wwnr = {
+static struct rv_monitor rv_this = {
.name = "wwnr",
.description = "wakeup while not running per-task testing model.",
.enable = enable_wwnr,
.disable = disable_wwnr,
- .reset = da_monitor_reset_all_wwnr,
+ .reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_wwnr(void)
{
- return rv_register_monitor(&rv_wwnr, NULL);
+ return rv_register_monitor(&rv_this, NULL);
}
static void __exit unregister_wwnr(void)
{
- rv_unregister_monitor(&rv_wwnr);
+ rv_unregister_monitor(&rv_this);
}
module_init(register_wwnr);
diff --git a/kernel/trace/rv/monitors/wwnr/wwnr.h b/kernel/trace/rv/monitors/wwnr/wwnr.h
index 0a59d23edf61..85d12e42a955 100644
--- a/kernel/trace/rv/monitors/wwnr/wwnr.h
+++ b/kernel/trace/rv/monitors/wwnr/wwnr.h
@@ -5,6 +5,8 @@
* Documentation/trace/rv/deterministic_automata.rst
*/
+#define MONITOR_NAME wwnr
+
enum states_wwnr {
not_running_wwnr = 0,
running_wwnr,
--
2.51.1
^ permalink raw reply related
* [PATCH 0/8] rv: Refactor and cleanup
From: Gabriele Monaco @ 2025-11-26 10:42 UTC (permalink / raw)
To: linux-kernel, Steven Rostedt, Nam Cao
Cc: Gabriele Monaco, Thomas Weißschuh, linux-trace-kernel
This series contains reviewed preparation patches included in [1].
These are namely: refactor to minimise macros, including cleanup and
documentation, python script annotations and cleanup, removal of
superfluous assignments and declarations in monitors code.
The series is based on linux-next and the first two patches had minor
conflicts, mainly resulting from 4f739ed19d22 ("rv: Pass va_list to
reactors").
[1] - https://lore.kernel.org/linux-trace-kernel/20250919140954.104920-1-gmonaco@redhat.com
To: Steven Rostedt <rostedt@goodmis.org>
To: Nam Cao <namcao@linutronix.de>
Cc: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Cc: linux-trace-kernel@vger.kernel.org
Gabriele Monaco (8):
rv: Refactor da_monitor to minimise macros
rv: Cleanup da_monitor after refactor
Documentation/rv: Adapt documentation after da_monitor refactoring
verification/rvgen: Adapt dot2k and templates after refactoring
da_monitor.h
verification/rvgen: Annotate DA functions with types
verification/dot2c: Remove __buff_to_string() and cleanup
verification/dot2c: Remove superfluous enum assignment and add last
comma
verification/rvgen: Remove unused variable declaration from containers
Documentation/trace/rv/monitor_synthesis.rst | 44 +-
include/linux/rv.h | 4 +
include/rv/automata.h | 132 +--
include/rv/da_monitor.h | 885 +++++++++---------
kernel/trace/rv/monitors/nrp/nrp.c | 30 +-
kernel/trace/rv/monitors/nrp/nrp.h | 22 +-
kernel/trace/rv/monitors/opid/opid.c | 40 +-
kernel/trace/rv/monitors/opid/opid.h | 24 +-
kernel/trace/rv/monitors/rtapp/rtapp.c | 2 -
kernel/trace/rv/monitors/sched/sched.c | 2 -
kernel/trace/rv/monitors/sco/sco.c | 26 +-
kernel/trace/rv/monitors/sco/sco.h | 14 +-
kernel/trace/rv/monitors/scpd/scpd.c | 28 +-
kernel/trace/rv/monitors/scpd/scpd.h | 14 +-
kernel/trace/rv/monitors/snep/snep.c | 28 +-
kernel/trace/rv/monitors/snep/snep.h | 18 +-
kernel/trace/rv/monitors/snroc/snroc.c | 26 +-
kernel/trace/rv/monitors/snroc/snroc.h | 14 +-
kernel/trace/rv/monitors/sssw/sssw.c | 38 +-
kernel/trace/rv/monitors/sssw/sssw.h | 22 +-
kernel/trace/rv/monitors/sts/sts.c | 34 +-
kernel/trace/rv/monitors/sts/sts.h | 28 +-
kernel/trace/rv/monitors/wip/wip.c | 26 +-
kernel/trace/rv/monitors/wip/wip.h | 14 +-
kernel/trace/rv/monitors/wwnr/wwnr.c | 28 +-
kernel/trace/rv/monitors/wwnr/wwnr.h | 14 +-
tools/verification/rvgen/rvgen/automata.py | 20 +-
tools/verification/rvgen/rvgen/dot2c.py | 111 +--
tools/verification/rvgen/rvgen/dot2k.py | 26 +-
.../rvgen/rvgen/templates/container/main.c | 2 -
.../rvgen/rvgen/templates/dot2k/main.c | 25 +-
31 files changed, 834 insertions(+), 907 deletions(-)
base-commit: 663d0d1af3faefe673cabf4b6b077149a87ad71f
--
2.51.1
^ permalink raw reply
* Re: [PATCH linux-next] fgraph: Fix and tighten PID filtering support
From: wang.yaxin @ 2025-11-26 9:50 UTC (permalink / raw)
To: rostedt
Cc: mhiramat, mark.rutland, mathieu.desnoyers, linux-kernel,
linux-trace-kernel, hu.shengming, hang.run, yang.yang29
In-Reply-To: <20251125130504.6f1b442f@gandalf.local.home>
On Wed, 26 Nov 2025 02:04:00
Steve wrote:
> On Thu, 13 Nov 2025 13:56:27 +0800 (CST)
> <wang.yaxin@zte.com.cn> wrote:
>
> > From: Shengming Hu <hu.shengming@zte.com.cn>
> >
> > Function graph tracing did not honor set_ftrace_pid() rules properly.
> >
> > The root cause is that for fgraph_ops, the underlying ftrace_ops->private
> > was left uninitialized. As a result, ftrace_pids_enabled(op) always
> > returned false, effectively disabling PID filtering in the function graph
> > tracer.
> >
> > PID filtering seemed to "work" only because graph_entry() performed an
> > extra coarse-grained check via ftrace_trace_task(). Specifically,
> > ftrace_ignore_pid is updated by ftrace_filter_pid_sched_switch_probe
> > during sched_switch events. Under the original logic, when the intent
> > is to trace only PID A, a context switch from task B to A sets
> > ftrace_ignore_pid to A’s PID. However, there remains a window
> > where B’s functions are still captured by the function-graph tracer.
> > The following trace demonstrates this leakage
> > (B = haveged-213, A = test.sh-385):
>
> Thanks for the patch.
>
>
> > Fix this by:
>
> The below should really be three different patches.
>
Hi Steve
Sorry, there were some formatting issues when I sent the series of v2 patches initially,
so I have resent them. Please refer to the latest sending for accuracy.
It has been divided into 3 different patches in v2:
https://lore.kernel.org/linux-trace-kernel/20251126172445319I7DWJm-KEEuCmqtLupteE@zte.com.cn/T/#t
> > 1. Properly initializing gops->ops->private so that
> > ftrace_pids_enabled() works as expected.
> > 2. Removing the imprecise fallback check in graph_entry().
> > 3. Updating register_ftrace_graph() to set gops->entryfunc =
> > fgraph_pid_func whenever PID filtering is active, so the correct
> > per-task filtering is enforced at entry time.
> >
> > With this change, function graph tracing will respect the configured
> > PID filter list consistently, and the redundant coarse check is no
> > longer needed.
> >
> > Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
> > ---
> > kernel/trace/fgraph.c | 9 +++++++--
> > kernel/trace/trace.h | 9 ---------
> > kernel/trace/trace_functions_graph.c | 3 ---
> > 3 files changed, 7 insertions(+), 14 deletions(-)
> >
> > diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
> > index 484ad7a18..00df3d4ac 100644
> > --- a/kernel/trace/fgraph.c
> > +++ b/kernel/trace/fgraph.c
> > @@ -1019,6 +1019,7 @@ void fgraph_init_ops(struct ftrace_ops *dst_ops,
> > mutex_init(&dst_ops->local_hash.regex_lock);
> > INIT_LIST_HEAD(&dst_ops->subop_list);
> > dst_ops->flags |= FTRACE_OPS_FL_INITIALIZED;
> > + dst_ops->private = src_ops->private;
> > }
> > #endif
> > }
> > @@ -1375,6 +1376,12 @@ int register_ftrace_graph(struct fgraph_ops *gops)
> > gops->idx = i;
> >
> > ftrace_graph_active++;
>
> Please keep a space here.
>
Space has been added in v2:
https://lore.kernel.org/linux-trace-kernel/20251126172445319I7DWJm-KEEuCmqtLupteE@zte.com.cn/T/#t
> > + /* Always save the function, and reset at unregistering */
> > + gops->saved_func = gops->entryfunc;
> > +#ifdef CONFIG_DYNAMIC_FTRACE
> > + if (ftrace_pids_enabled(&gops->ops))
> > + gops->entryfunc = fgraph_pid_func;
> > +#endif
>
> Thanks,
>
> -- Steve
>
Thank you for your valuable review feedback, Steve!
I have addressed all the comments you raised, and the complete v2 patch
series ([PATCH v2 0/3] to [PATCH v2 3/3]):
https://lore.kernel.org/linux-trace-kernel/20251126172445319I7DWJm-KEEuCmqtLupteE@zte.com.cn/T/#t
--
With Best Regards,
Shengming
> >
> > if (ftrace_graph_active == 2)
> > ftrace_graph_disable_direct(true);
> > @@ -1395,8 +1402,6 @@ int register_ftrace_graph(struct fgraph_ops *gops)
> > } else {
> > init_task_vars(gops->idx);
> > }
> > - /* Always save the function, and reset at unregistering */
> > - gops->saved_func = gops->entryfunc;
> >
> > gops->ops.flags |= FTRACE_OPS_FL_GRAPH;
> >
> > diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> > index a3a15cfab..048a53282 100644
> > --- a/kernel/trace/trace.h
> > +++ b/kernel/trace/trace.h
> > @@ -1162,11 +1162,6 @@ struct ftrace_func_command {
> > char *params, int enable);
> > };
> > extern bool ftrace_filter_param __initdata;
> > -static inline int ftrace_trace_task(struct trace_array *tr)
> > -{
> > - return this_cpu_read(tr->array_buffer.data->ftrace_ignore_pid) !=
> > - FTRACE_PID_IGNORE;
> > -}
> > extern int ftrace_is_dead(void);
> > int ftrace_create_function_files(struct trace_array *tr,
> > struct dentry *parent);
> > @@ -1184,10 +1179,6 @@ void ftrace_clear_pids(struct trace_array *tr);
> > int init_function_trace(void);
> > void ftrace_pid_follow_fork(struct trace_array *tr, bool enable);
> > #else
> > -static inline int ftrace_trace_task(struct trace_array *tr)
> > -{
> > - return 1;
> > -}
> > static inline int ftrace_is_dead(void) { return 0; }
> > static inline int
> > ftrace_create_function_files(struct trace_array *tr,
> > diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
> > index fe9607edc..0efe831e4 100644
> > --- a/kernel/trace/trace_functions_graph.c
> > +++ b/kernel/trace/trace_functions_graph.c
> > @@ -232,9 +232,6 @@ static int graph_entry(struct ftrace_graph_ent *trace,
> > return 1;
> > }
> >
> > - if (!ftrace_trace_task(tr))
> > - return 0;
> > -
> > if (ftrace_graph_ignore_func(gops, trace))
> > return 0;
> >
^ permalink raw reply
* [PATCH v2 3/3] fgraph: Remove coarse PID filtering from graph_entry()
From: wang.yaxin @ 2025-11-26 9:35 UTC (permalink / raw)
To: wang.yaxin
Cc: rostedt, mhiramat, mark.rutland, mathieu.desnoyers, linux-kernel,
linux-trace-kernel, hu.shengming, zhang.run, yang.yang29
In-Reply-To: <20251126172445319I7DWJm-KEEuCmqtLupteE@zte.com.cn>
From: Shengming Hu <hu.shengming@zte.com.cn>
With PID filtering working via ftrace_pids_enabled() and fgraph_pid_func,
the coarse-grained ftrace_trace_task() check in graph_entry() is obsolete.
It was only a fallback for uninitialized op->private (now fixed), and its
removal ensures consistent PID filtering with standard function tracing.
Also remove unused ftrace_trace_task() definition from trace.h.
Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
---
kernel/trace/trace.h | 9 ---------
kernel/trace/trace_functions_graph.c | 3 ---
2 files changed, 12 deletions(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 58be6d741..2bc985739 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1159,11 +1159,6 @@ struct ftrace_func_command {
char *params, int enable);
};
extern bool ftrace_filter_param __initdata;
-static inline int ftrace_trace_task(struct trace_array *tr)
-{
- return this_cpu_read(tr->array_buffer.data->ftrace_ignore_pid) !=
- FTRACE_PID_IGNORE;
-}
extern int ftrace_is_dead(void);
int ftrace_create_function_files(struct trace_array *tr,
struct dentry *parent);
@@ -1181,10 +1176,6 @@ void ftrace_clear_pids(struct trace_array *tr);
int init_function_trace(void);
void ftrace_pid_follow_fork(struct trace_array *tr, bool enable);
#else
-static inline int ftrace_trace_task(struct trace_array *tr)
-{
- return 1;
-}
static inline int ftrace_is_dead(void) { return 0; }
static inline int
ftrace_create_function_files(struct trace_array *tr,
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 44d5dc503..cb9aef060 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -233,9 +233,6 @@ static int graph_entry(struct ftrace_graph_ent *trace,
return 1;
}
- if (!ftrace_trace_task(tr))
- return 0;
-
if (ftrace_graph_ignore_func(gops, trace))
return 0;
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v2 1/2] rv: Convert to use lock guard
From: Gabriele Monaco @ 2025-11-26 9:33 UTC (permalink / raw)
To: Nam Cao, Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-trace-kernel,
linux-kernel
In-Reply-To: <878qft5gxk.fsf@yellow.woof>
On Wed, 2025-11-26 at 09:36 +0100, Nam Cao wrote:
> On Tue, 2025-11-25 at 14:57 -0500, Steven Rostedt wrote:
> > On Mon, 17 Nov 2025 09:06:02 +0000
> > Nam Cao <namcao@linutronix.de> wrote:
> >
> > > @@ -644,13 +640,11 @@ static ssize_t enabled_monitors_write(struct file
> > > *filp, const char __user *user
> > > else
> > > retval = rv_disable_monitor(mon);
> > >
> > > - if (!retval)
> > > - retval = count;
> > > -
> > > - break;
> > > + if (retval)
> > > + return retval;
> > > + return count;
> >
> > No biggy, but I wonder if this would look better as:
> >
> > return retval ? : count;
>
> Unless you really prefer it this way, I would rather not. The first time
> I saw this syntax, it confused the hell out of me. Took me some time
> scratching my head until I figured out that it is a GNU extension.
>
> I prefer to stay with the C standard unless there is major benefit not
> to.
To be fair, I find it a bit obscure as well, although it's frequently used
within the kernel.
Let's not change it then.
Thanks,
Gabriele
^ permalink raw reply
* [PATCH v2 2/3] fgraph: Check ftrace_pids_enabled on registration for early filtering
From: wang.yaxin @ 2025-11-26 9:33 UTC (permalink / raw)
To: wang.yaxin
Cc: rostedt, mhiramat, mark.rutland, mathieu.desnoyers, linux-kernel,
linux-trace-kernel, hu.shengming, zhang.run, yang.yang29
In-Reply-To: <20251126172445319I7DWJm-KEEuCmqtLupteE@zte.com.cn>
From: Shengming Hu <hu.shengming@zte.com.cn>
When registering ftrace_graph, check if ftrace_pids_enabled is active.
If enabled, assign entryfunc to fgraph_pid_func to ensure filtering
is performed before executing the saved original entry function.
Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
---
kernel/trace/fgraph.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index f6f25d81a..65ac0f04a 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -1369,6 +1369,13 @@ int register_ftrace_graph(struct fgraph_ops *gops)
ftrace_graph_active++;
+ /* Always save the function, and reset at unregistering */
+ gops->saved_func = gops->entryfunc;
+#ifdef CONFIG_DYNAMIC_FTRACE
+ if (ftrace_pids_enabled(&gops->ops))
+ gops->entryfunc = fgraph_pid_func;
+#endif
+
if (ftrace_graph_active == 2)
ftrace_graph_disable_direct(true);
@@ -1388,8 +1395,6 @@ int register_ftrace_graph(struct fgraph_ops *gops)
} else {
init_task_vars(gops->idx);
}
- /* Always save the function, and reset at unregistering */
- gops->saved_func = gops->entryfunc;
gops->ops.flags |= FTRACE_OPS_FL_GRAPH;
--
2.25.1
^ permalink raw reply related
* [PATCH v2 1/3] fgraph: Initialize ftrace_ops->private for function graph ops
From: wang.yaxin @ 2025-11-26 9:29 UTC (permalink / raw)
To: wang.yaxin
Cc: rostedt, mhiramat, mark.rutland, mathieu.desnoyers, linux-kernel,
linux-trace-kernel, hu.shengming, zhang.run, yang.yang29
In-Reply-To: <20251126172445319I7DWJm-KEEuCmqtLupteE@zte.com.cn>
From: Shengming Hu <hu.shengming@zte.com.cn>
The ftrace_pids_enabled(op) check relies on op->private being properly
initialized, but fgraph_ops's underlying ftrace_ops->private was left
uninitialized. This caused ftrace_pids_enabled() to always return false,
effectively disabling PID filtering for function graph tracing.
Fix this by copying src_ops->private to dst_ops->private in
fgraph_init_ops(), ensuring PID filter state is correctly propagated.
Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
---
kernel/trace/fgraph.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 7fb9b169d..f6f25d81a 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -1016,6 +1016,7 @@ void fgraph_init_ops(struct ftrace_ops *dst_ops,
mutex_init(&dst_ops->local_hash.regex_lock);
INIT_LIST_HEAD(&dst_ops->subop_list);
dst_ops->flags |= FTRACE_OPS_FL_INITIALIZED;
+ dst_ops->private = src_ops->private;
}
#endif
}
--
2.25.1
^ permalink raw reply related
* [PATCH v2 0/3] fgraph: Fix and tighten PID filtering support
From: wang.yaxin @ 2025-11-26 9:24 UTC (permalink / raw)
To: rostedt, mhiramat, mark.rutland, mathieu.desnoyers
Cc: linux-kernel, linux-trace-kernel, hu.shengming, zhang.run,
yang.yang29, wang.yaxin
From: Shengming Hu <hu.shengming@zte.com.cn>
Function graph tracing did not honor set_ftrace_pid() rules properly.
The root cause is that for fgraph_ops, the underlying ftrace_ops->private
was left uninitialized. As a result, ftrace_pids_enabled(op) always
returned false, effectively disabling PID filtering in the function graph
tracer.
PID filtering seemed to "work" only because graph_entry() performed an
extra coarse-grained check via ftrace_trace_task(). Specifically,
ftrace_ignore_pid is updated by ftrace_filter_pid_sched_switch_probe
during sched_switch events. Under the original logic, when the intent
is to trace only PID A, a context switch from task B to A sets
ftrace_ignore_pid to A’s PID. However, there remains a window
where B’s functions are still captured by the function-graph tracer.
The following trace demonstrates this leakage
(B = haveged-213, A = test.sh-385):
4) test.sh-385 | | set_next_entity() {
4) test.sh-385 | 2.108 us | __dequeue_entity();
4) test.sh-385 | 1.526 us | __update_load_avg_se();
4) test.sh-385 | 1.363 us | __update_load_avg_cfs_rq();
4) test.sh-385 | + 14.144 us | }
4) test.sh-385 | 1.417 us | __set_next_task_fair.part.0();
4) test.sh-385 | + 77.877 us | }
4) test.sh-385 | | __traceiter_sched_switch() {
4) test.sh-385 | 2.097 us | _raw_spin_lock_irqsave();
4) test.sh-385 | 1.483 us | _raw_spin_unlock_irqrestore();
4) test.sh-385 | + 14.014 us | }
------------------------------------------
4) test.sh-385 => haveged-213
------------------------------------------
4) haveged-213 | | switch_mm_irqs_off() {
4) haveged-213 | 2.387 us | choose_new_asid();
4) haveged-213 | + 12.462 us | switch_ldt();
4) haveged-213 | + 45.288 us | }
4) haveged-213 | 1.639 us | x86_task_fpu();
4) haveged-213 | | save_fpregs_to_fpstate() {
4) haveged-213 | 1.888 us | xfd_validate_state();
4) haveged-213 | 6.995 us | }
------------------------------------------
4) haveged-213 => test.sh-385
------------------------------------------
4) test.sh-385 | | finish_task_switch.isra.0() {
4) test.sh-385 | 1.857 us | _raw_spin_unlock();
4) test.sh-385 | 5.618 us | }
4) test.sh-385 | ! 446.226 us | }
4) test.sh-385 | | __pte_offset_map_lock() {
4) test.sh-385 | | ___pte_offset_map() {
4) test.sh-385 | 1.933 us | __rcu_read_lock();
4) test.sh-385 | 6.271 us | }
4) test.sh-385 | 2.056 us | _raw_spin_lock();
4) test.sh-385 | + 14.281 us | }
The following three logically independent patches fix this defect while
preserving the effectiveness of PID filtering in fgraph:
Patch 1 properly initializes ops->private for fgraph_ops.
Patch 2 switches gops->entryfunc to fgraph_pid_func when PID filtering
is active.
Patch 3 removes the coarse fallback logic, making PID filtering reliable
and consistent with function tracing.
Changes from v1:
https://lore.kernel.org/linux-trace-kernel/20251113135627439rGwzvG7JzdmnN8VMHAfMs@zte.com.cn/
- Split the patch into three independent commits as suggested by Steven
- Add a space after "ftrace_graph_active++" as suggested by Steven
Shengming Hu (3):
fgraph: Initialize ftrace_ops->private for function graph ops
fgraph: Check ftrace_pids_enabled on registration for early filtering
fgraph: fgraph: Remove redundant PID filtering from graph_entry()
kernel/trace/fgraph.c | 10 ++++++++--
kernel/trace/trace.h | 9 ---------
kernel/trace/trace_functions_graph.c | 3 ---
3 files changed, 8 insertions(+), 14 deletions(-)
--
2.25.1
^ permalink raw reply
* [PATCH v2 0/3] fgraph: Fix and tighten PID filtering support
From: wang.yaxin @ 2025-11-26 9:08 UTC (permalink / raw)
To: rostedt, mhiramat, mark.rutland, mathieu.desnoyers
Cc: linux-kernel, linux-trace-kernel, hu.shengming, zhang.run,
yang.yang29, wang.yaxin
From: Shengming Hu <hu.shengming@zte.com.cn>
Function graph tracing did not honor set_ftrace_pid() rules properly.
The root cause is that for fgraph_ops, the underlying ftrace_ops->private
was left uninitialized. As a result, ftrace_pids_enabled(op) always
returned false, effectively disabling PID filtering in the function graph
tracer.
PID filtering seemed to "work" only because graph_entry() performed an
extra coarse-grained check via ftrace_trace_task(). Specifically,
ftrace_ignore_pid is updated by ftrace_filter_pid_sched_switch_probe
during sched_switch events. Under the original logic, when the intent
is to trace only PID A, a context switch from task B to A sets
ftrace_ignore_pid to A’s PID. However, there remains a window
where B’s functions are still captured by the function-graph tracer.
The following trace demonstrates this leakage
(B = haveged-213, A = test.sh-385):
4) test.sh-385 | | set_next_entity() {
4) test.sh-385 | 2.108 us | __dequeue_entity();
4) test.sh-385 | 1.526 us | __update_load_avg_se();
4) test.sh-385 | 1.363 us | __update_load_avg_cfs_rq();
4) test.sh-385 | + 14.144 us | }
4) test.sh-385 | 1.417 us | __set_next_task_fair.part.0();
4) test.sh-385 | + 77.877 us | }
4) test.sh-385 | | __traceiter_sched_switch() {
4) test.sh-385 | 2.097 us | _raw_spin_lock_irqsave();
4) test.sh-385 | 1.483 us | _raw_spin_unlock_irqrestore();
4) test.sh-385 | + 14.014 us | }
------------------------------------------
4) test.sh-385 => haveged-213
------------------------------------------
4) haveged-213 | | switch_mm_irqs_off() {
4) haveged-213 | 2.387 us | choose_new_asid();
4) haveged-213 | + 12.462 us | switch_ldt();
4) haveged-213 | + 45.288 us | }
4) haveged-213 | 1.639 us | x86_task_fpu();
4) haveged-213 | | save_fpregs_to_fpstate() {
4) haveged-213 | 1.888 us | xfd_validate_state();
4) haveged-213 | 6.995 us | }
------------------------------------------
4) haveged-213 => test.sh-385
------------------------------------------
4) test.sh-385 | | finish_task_switch.isra.0() {
4) test.sh-385 | 1.857 us | _raw_spin_unlock();
4) test.sh-385 | 5.618 us | }
4) test.sh-385 | ! 446.226 us | }
4) test.sh-385 | | __pte_offset_map_lock() {
4) test.sh-385 | | ___pte_offset_map() {
4) test.sh-385 | 1.933 us | __rcu_read_lock();
4) test.sh-385 | 6.271 us | }
4) test.sh-385 | 2.056 us | _raw_spin_lock();
4) test.sh-385 | + 14.281 us | }
The following three logically independent patches fix this defect while
preserving the effectiveness of PID filtering in fgraph:
Patch 1 properly initializes ops->private for fgraph_ops.
Patch 2 switches gops->entryfunc to fgraph_pid_func when PID filtering
is active.
Patch 3 removes the coarse fallback logic, making PID filtering reliable
and consistent with function tracing.
Changes from v1:
https://lore.kernel.org/linux-trace-kernel/20251113135627439rGwzvG7JzdmnN8VMHAfMs@zte.com.cn/
- Split the patch into three independent commits as suggested by Steven
- Add a space after "ftrace_graph_active++" as suggested by Steven
Shengming Hu (3):
fgraph: Initialize ftrace_ops->private for function graph ops
fgraph: Check ftrace_pids_enabled on registration for early filtering
fgraph: fgraph: Remove redundant PID filtering from graph_entry()
kernel/trace/fgraph.c | 10 ++++++++--
kernel/trace/trace.h | 9 ---------
kernel/trace/trace_functions_graph.c | 3 ---
3 files changed, 8 insertions(+), 14 deletions(-)
--
2.25.1
^ permalink raw reply
* [PATCH v2 1/3] fgraph: Initialize ftrace_ops->private for function graph ops
From: wang.yaxin @ 2025-11-26 9:12 UTC (permalink / raw)
To: wang.yaxin
Cc: rostedt, mhiramat, mark.rutland, mathieu.desnoyers, linux-kernel,
linux-trace-kernel, hu.shengming, zhang.run, yang.yang29
In-Reply-To: <202511261708240123PvauPcd7Xbqx_EDrKkNn@zte.com.cn>
From: Shengming Hu <hu.shengming@zte.com.cn>
The ftrace_pids_enabled(op) check relies on op->private being properly
initialized, but fgraph_ops's underlying ftrace_ops->private was left
uninitialized. This caused ftrace_pids_enabled() to always return false,
effectively disabling PID filtering for function graph tracing.
Fix this by copying src_ops->private to dst_ops->private in
fgraph_init_ops(), ensuring PID filter state is correctly propagated.
Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
---
kernel/trace/fgraph.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 7fb9b169d..f6f25d81a 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -1016,6 +1016,7 @@ void fgraph_init_ops(struct ftrace_ops *dst_ops,
mutex_init(&dst_ops->local_hash.regex_lock);
INIT_LIST_HEAD(&dst_ops->subop_list);
dst_ops->flags |= FTRACE_OPS_FL_INITIALIZED;
+ dst_ops->private = src_ops->private;
}
#endif
}
--
2.25.1
^ permalink raw reply related
* [PATCH bpf-next] bpf: make kprobe_multi_link_prog_run always_inline
From: Menglong Dong @ 2025-11-26 8:52 UTC (permalink / raw)
To: ast, jolsa
Cc: kpsingh, mattbobrowski, song, ast, daniel, andrii, martin.lau,
eddyz87, yonghong.song, john.fastabend, sdf, haoluo, rostedt,
mhiramat, mathieu.desnoyers, jiang.biao, bpf, linux-kernel,
linux-trace-kernel
Make kprobe_multi_link_prog_run() always inline to obtain better
performance. Before this patch, the bench performance is:
./bench trig-kprobe-multi
Setting up benchmark 'trig-kprobe-multi'...
Benchmark 'trig-kprobe-multi' started.
Iter 0 ( 95.485us): hits 62.462M/s ( 62.462M/prod), [...]
Iter 1 (-80.054us): hits 62.486M/s ( 62.486M/prod), [...]
Iter 2 ( 13.572us): hits 62.287M/s ( 62.287M/prod), [...]
Iter 3 ( 76.961us): hits 62.293M/s ( 62.293M/prod), [...]
Iter 4 (-77.698us): hits 62.394M/s ( 62.394M/prod), [...]
Iter 5 (-13.399us): hits 62.319M/s ( 62.319M/prod), [...]
Iter 6 ( 77.573us): hits 62.250M/s ( 62.250M/prod), [...]
Summary: hits 62.338 ± 0.083M/s ( 62.338M/prod)
And after this patch, the performance is:
Iter 0 (454.148us): hits 66.900M/s ( 66.900M/prod), [...]
Iter 1 (-435.540us): hits 68.925M/s ( 68.925M/prod), [...]
Iter 2 ( 8.223us): hits 68.795M/s ( 68.795M/prod), [...]
Iter 3 (-12.347us): hits 68.880M/s ( 68.880M/prod), [...]
Iter 4 ( 2.291us): hits 68.767M/s ( 68.767M/prod), [...]
Iter 5 ( -1.446us): hits 68.756M/s ( 68.756M/prod), [...]
Iter 6 ( 13.882us): hits 68.657M/s ( 68.657M/prod), [...]
Summary: hits 68.792 ± 0.087M/s ( 68.792M/prod)
As we can see, the performance of kprobe-multi increase from 62M/s to
68M/s.
Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
---
kernel/trace/bpf_trace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index a795f7afbf3d..d57727abaade 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -2529,7 +2529,7 @@ static u64 bpf_kprobe_multi_entry_ip(struct bpf_run_ctx *ctx)
return run_ctx->entry_ip;
}
-static int
+static __always_inline int
kprobe_multi_link_prog_run(struct bpf_kprobe_multi_link *link,
unsigned long entry_ip, struct ftrace_regs *fregs,
bool is_return, void *data)
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v2 1/2] rv: Convert to use lock guard
From: Nam Cao @ 2025-11-26 8:36 UTC (permalink / raw)
To: Gabriele Monaco, Steven Rostedt
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-trace-kernel,
linux-kernel
In-Reply-To: <e4838dd91f14024222eef8e705f2b1ae7945bb80.camel@redhat.com>
On Tue, 2025-11-25 at 14:57 -0500, Steven Rostedt wrote:
> On Mon, 17 Nov 2025 09:06:02 +0000
> Nam Cao <namcao@linutronix.de> wrote:
>
> > @@ -644,13 +640,11 @@ static ssize_t enabled_monitors_write(struct file
> > *filp, const char __user *user
> > else
> > retval = rv_disable_monitor(mon);
> >
> > - if (!retval)
> > - retval = count;
> > -
> > - break;
> > + if (retval)
> > + return retval;
> > + return count;
>
> No biggy, but I wonder if this would look better as:
>
> return retval ? : count;
Unless you really prefer it this way, I would rather not. The first time
I saw this syntax, it confused the hell out of me. Took me some time
scratching my head until I figured out that it is a GNU extension.
I prefer to stay with the C standard unless there is major benefit not
to.
Nam
^ permalink raw reply
* Re: [RFC PATCH 5/8] uprobe/x86: Add support to optimize on top of emulated instructions
From: Jiri Olsa @ 2025-11-26 7:54 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Masami Hiramatsu, Peter Zijlstra, Andrii Nakryiko, bpf,
linux-kernel, linux-trace-kernel, x86, Song Liu, Yonghong Song,
John Fastabend, Steven Rostedt, Ingo Molnar, David Laight
In-Reply-To: <aSSdavSy_unRaEgF@redhat.com>
On Mon, Nov 24, 2025 at 07:01:14PM +0100, Oleg Nesterov wrote:
> Hi Jiri,
>
> I am trying to understand this series, will try to read it more carefully
> later...
>
> (damn why do you always send the patches when I am on PTO? ;)
it's more fun that way ;-) thanks for checking on it
>
> On 11/17, Jiri Olsa wrote:
> >
> > struct arch_uprobe {
> > union {
> > - u8 insn[MAX_UINSN_BYTES];
> > + u8 insn[5*MAX_UINSN_BYTES];
>
> Hmm. OK, this matches the "for (i = 0; i < 5; i++)" loop in
> opt_setup_xol_ops(), but do we really need this change? Please see
> the question at the end.
>
> > +static int opt_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
> > +{
> > + unsigned long offset = insn->length;
> > + struct insn insnX;
> > + int i, ret;
> > +
> > + if (test_bit(ARCH_UPROBE_FLAG_CAN_OPTIMIZE, &auprobe->flags))
> > + return -ENOSYS;
>
> I think this logic needs some cleanups... If ARCH_UPROBE_FLAG_CAN_OPTIMIZE
> is set by the caller, the it doesn't make sense to call xxx_setup_xol_ops(),
> right? But lets forget it for now.
>
> > + ret = opt_setup_xol_insns(auprobe, &auprobe->opt.xol[0], insn);
>
> I think this should go into the main loop, see below
>
> > + for (i = 1; i < 5; i++) {
> > + ret = uprobe_init_insn_offset(auprobe, offset, &insnX, true);
> > + if (ret)
> > + break;
> > + ret = opt_setup_xol_insns(auprobe, &auprobe->opt.xol[i], &insnX);
> > + if (ret)
> > + break;
> > + offset += insnX.length;
> > + auprobe->opt.cnt++;
> > + if (offset >= 5)
> > + goto optimize;
> > + }
> > +
> > + return -ENOSYS;
>
> I don't think -ENOSYS makes sense if opt_setup_xol_insns() succeeds at least once.
> IOW, how about
>
> static int opt_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
> {
> unsigned long offset = 0;
> struct insn insnX;
> int i, ret;
>
> if (test_bit(ARCH_UPROBE_FLAG_CAN_OPTIMIZE, &auprobe->flags))
> return -ENOSYS;
>
> for (i = 0; i < 5; i++) {
> ret = opt_setup_xol_insns(auprobe, &auprobe->opt.xol[i], insn);
> if (ret)
> break;
> offset += insn->length;
> if (offset >= 5)
> break;
>
> insn = &insnX;
> ret = uprobe_init_insn_offset(auprobe, offset, insn, true);
> if (ret)
> break;
> }
>
> if (!offset)
> return -ENOSYS;
>
> if (offset >= 5) {
> auprobe->opt.cnt = i + 1;
> auprobe->xol.ops = &opt_xol_ops;
> set_bit(ARCH_UPROBE_FLAG_CAN_OPTIMIZE, &auprobe->flags);
> set_bit(ARCH_UPROBE_FLAG_OPTIMIZE_EMULATE, &auprobe->flags);
> }
>
> return 0;
> }
>
> ?
>
> This way the caller, arch_uprobe_analyze_insn(), doesn't need to call
> push/mov/sub/_setup_xol_ops(), and the code looks a bit simpler to me.
ah nice, will try that
>
> No?
>
> > + * TODO perhaps we could 'emulate' nop, so there would be no need for
> > + * ARCH_UPROBE_FLAG_OPTIMIZE_EMULATE flag, because we would emulate
> > + * allways.
>
> Agreed... and this connects to "this logic needs some cleanups" above.
> I guess we need nop_setup_xol_ops() extracted from branch_setup_xol_ops()
> but again, lets forget it for now.
ok, it will hopefully make the code simpler, will check on that
>
> -------------------------------------------------------------------------------
> Now the main question. What if we avoid this change
>
> - u8 insn[MAX_UINSN_BYTES];
> + u8 insn[5*MAX_UINSN_BYTES];
>
> mentioned above, and change opt_setup_xol_ops() to just do
>
> - for (i = 0; i < 5; i++)
> + for (i = 0;; i++)
>
> ?
>
> The main loop stops when offset >= 5 anyway.
>
> And. if auprobe->insn[offset:MAX_UINSN_BYTES] doesn't contain a full/valid
> insn at the start, then uprobe_init_insn_offset()->insn_decode() should fail?
>
> Most probably I missed something, but I can't understand this part.
no, I think you're right, I did not realize we fit under MAX_UINSN_BYTES
anyway, call instruction needs only 5 bytes
thanks,
jirka
^ permalink raw reply
* Re: [PATCH v2 1/2] rv: Convert to use lock guard
From: Gabriele Monaco @ 2025-11-26 7:27 UTC (permalink / raw)
To: Steven Rostedt, Nam Cao
Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-trace-kernel,
linux-kernel
In-Reply-To: <20251125145736.48c3ed9d@gandalf.local.home>
On Tue, 2025-11-25 at 14:57 -0500, Steven Rostedt wrote:
> On Mon, 17 Nov 2025 09:06:02 +0000
> Nam Cao <namcao@linutronix.de> wrote:
>
> > @@ -644,13 +640,11 @@ static ssize_t enabled_monitors_write(struct file
> > *filp, const char __user *user
> > else
> > retval = rv_disable_monitor(mon);
> >
> > - if (!retval)
> > - retval = count;
> > -
> > - break;
> > + if (retval)
> > + return retval;
> > + return count;
>
> No biggy, but I wonder if this would look better as:
>
> return retval ? : count;
>
Tried both patches and they look fine to me.
Reviewed-by: Gabriele Monaco <gmonaco@redhat.com>
Nam, feel free to send an updated version if you want to apply Steve's
suggestion or keep the patch like this.
Steve, this is the only remaining change before the merge window, unless you
prefer to keep it for the next round, I'm going to send a small pull requests
with those two patches alone.
Thanks,
Gabriele
> -- Steve
>
> > }
> >
> > - mutex_unlock(&rv_interface_lock);
> > return retval;
> > }
> >
^ permalink raw reply
* Re: [PATCH v2] tracing: Add system trigger file to enable triggers for all the system's events
From: Masami Hiramatsu @ 2025-11-26 6:02 UTC (permalink / raw)
To: Steven Rostedt
Cc: LKML, Linux Trace Kernel, Masami Hiramatsu, Mathieu Desnoyers,
Tom Zanussi
In-Reply-To: <20251125200837.31aae207@gandalf.local.home>
On Tue, 25 Nov 2025 20:08:37 -0500
Steven Rostedt <rostedt@goodmis.org> wrote:
> From: Steven Rostedt <rostedt@goodmis.org>
>
> Currently a trigger can only be added to individual events. Some triggers
> (like stacktrace) can be useful to add as a bulk trigger for a set of
> system events (like interrupt or scheduling).
>
> Add a trigger file to the system directories:
>
> /sys/kernel/tracing/events/*/trigger
>
> And allow stacktrace trigger to be enabled for all those events.
>
> Writing into the system/trigger file acts the same as writing into each of
> the system event's trigger files individually.
>
> This also allows to remove a trigger from all events in a subsystem (even
> if it's not a subsystem trigger!).
>
I have some comments below.
> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
> ---
> Note, this is based on top of:
>
> https://patchwork.kernel.org/project/linux-trace-kernel/cover/20251120205600.570673392@kernel.org/
>
> Changes since v1: https://patch.msgid.link/20251120160003.2fa33d80@gandalf.local.home
>
> - Removed unused set variable len (kernel test robot)
>
> - Assign next to strim(buf) to remove beginning spaces
>
> Documentation/trace/events.rst | 25 ++++
> kernel/trace/trace.c | 11 +-
> kernel/trace/trace.h | 15 ++-
> kernel/trace/trace_events.c | 70 +++++-----
> kernel/trace/trace_events_trigger.c | 199 +++++++++++++++++++++++++++-
> 5 files changed, 278 insertions(+), 42 deletions(-)
>
> diff --git a/Documentation/trace/events.rst b/Documentation/trace/events.rst
> index 18d112963dec..caa4958af43a 100644
> --- a/Documentation/trace/events.rst
> +++ b/Documentation/trace/events.rst
> @@ -416,6 +416,31 @@ way, so beware about making generalizations between the two.
> can also enable triggers that are written into
> /sys/kernel/tracing/events/ftrace/print/trigger
>
> +The system directory also has a trigger file that allows some triggers to be
> +set for all the system's events. This is limited to only a small subset of the
> +triggers and does not allow for the count parameter. But it does allow for
> +filters. Writing into this file is the same as writing into each of the
> +system's event's trigger files individually. Although only a subset of
> +triggers may use this file for enabling, all triggers may use this file for
> +disabling::
> +
> + cd /sys/kernel/tracing
> + cat events/sched/trigger
> + # Available system triggers:
> + # stacktrace
> +
> + echo stacktrace > events/sched/trigger
> + cat events/sched/sched_switch/trigger
> + stacktrace:unlimited
> +
> + echo snapshot > events/sched/sched_waking/trigger
> + cat events/sched/sched_waking/trigger
> + snapshot:unlimited
> + echo '!snapshot' > events/sched/trigger
> + cat events/sched/sched_waking/trigger
> + # Available triggers:
> + # traceon traceoff snapshot stacktrace enable_event disable_event enable_hist disable_hist hist
> +
> 6.1 Expression syntax
> ---------------------
>
> diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
> index 032bdedca5d9..feced9f43156 100644
> --- a/kernel/trace/trace.c
> +++ b/kernel/trace/trace.c
> @@ -592,11 +592,12 @@ void trace_set_ring_buffer_expanded(struct trace_array *tr)
>
> LIST_HEAD(ftrace_trace_arrays);
>
> -int trace_array_get(struct trace_array *this_tr)
> +int __trace_array_get(struct trace_array *this_tr)
> {
> struct trace_array *tr;
>
> - guard(mutex)(&trace_types_lock);
> + lockdep_assert_held(&trace_types_lock);
> +
> list_for_each_entry(tr, &ftrace_trace_arrays, list) {
> if (tr == this_tr) {
> tr->ref++;
> @@ -607,6 +608,12 @@ int trace_array_get(struct trace_array *this_tr)
> return -ENODEV;
> }
>
> +int trace_array_get(struct trace_array *tr)
> +{
> + guard(mutex)(&trace_types_lock);
> + return __trace_array_get(tr);
> +}
> +
> static void __trace_array_put(struct trace_array *this_tr)
> {
> WARN_ON(!this_tr->ref);
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index fd5a6daa6c25..7379763a057d 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -469,10 +469,14 @@ extern struct list_head ftrace_trace_arrays;
> extern struct mutex trace_types_lock;
>
> extern int trace_array_get(struct trace_array *tr);
> +extern int __trace_array_get(struct trace_array *tr);
> extern int tracing_check_open_get_tr(struct trace_array *tr);
> extern struct trace_array *trace_array_find(const char *instance);
> extern struct trace_array *trace_array_find_get(const char *instance);
>
> +extern struct trace_subsystem_dir *trace_get_system_dir(struct inode *inode);
> +void trace_put_system_dir(struct trace_subsystem_dir *dir);
> +
> extern u64 tracing_event_time_stamp(struct trace_buffer *buffer, struct ring_buffer_event *rbe);
> extern int tracing_set_filter_buffering(struct trace_array *tr, bool set);
> extern int tracing_set_clock(struct trace_array *tr, const char *clockstr);
> @@ -1774,6 +1778,7 @@ static inline struct trace_event_file *event_file_file(struct file *filp)
> }
>
> extern const struct file_operations event_trigger_fops;
> +extern const struct file_operations event_system_trigger_fops;
> extern const struct file_operations event_hist_fops;
> extern const struct file_operations event_hist_debug_fops;
> extern const struct file_operations event_inject_fops;
> @@ -2057,10 +2062,16 @@ struct event_command {
> * regardless of whether or not it has a filter associated with
> * it (filters make a trigger require access to the trace record
> * but are not always present).
> + *
> + * @SYSTEM: A flag that says whether or not this command can be used
> + * at the event system level. For example, can it be written into
> + * events/sched/trigger file where it will be enabled for all
> + * sched events?
> */
> enum event_command_flags {
> - EVENT_CMD_FL_POST_TRIGGER = 1,
> - EVENT_CMD_FL_NEEDS_REC = 2,
> + EVENT_CMD_FL_POST_TRIGGER = BIT(1),
> + EVENT_CMD_FL_NEEDS_REC = BIT(2),
> + EVENT_CMD_FL_SYSTEM = BIT(3),
> };
>
> static inline bool event_command_post_trigger(struct event_command *cmd_ops)
> diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
> index 9b07ad9eb284..f00b41f73fc2 100644
> --- a/kernel/trace/trace_events.c
> +++ b/kernel/trace/trace_events.c
> @@ -2168,51 +2168,52 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
>
> static LIST_HEAD(event_subsystems);
>
> -static int subsystem_open(struct inode *inode, struct file *filp)
> +struct trace_subsystem_dir *trace_get_system_dir(struct inode *inode)
> {
> - struct trace_subsystem_dir *dir = NULL, *iter_dir;
> - struct trace_array *tr = NULL, *iter_tr;
> - struct event_subsystem *system = NULL;
> - int ret;
> + struct trace_subsystem_dir *dir;
> + struct trace_array *tr = NULL;
nit: This also no need to be initialized.
>
> - if (tracing_is_disabled())
> - return -ENODEV;
> + guard(mutex)(&event_mutex);
> + guard(mutex)(&trace_types_lock);
>
> /* Make sure the system still exists */
> - mutex_lock(&event_mutex);
> - mutex_lock(&trace_types_lock);
> - list_for_each_entry(iter_tr, &ftrace_trace_arrays, list) {
> - list_for_each_entry(iter_dir, &iter_tr->systems, list) {
> - if (iter_dir == inode->i_private) {
> + list_for_each_entry(tr, &ftrace_trace_arrays, list) {
> + list_for_each_entry(dir, &tr->systems, list) {
> + if (dir == inode->i_private) {
> /* Don't open systems with no events */
> - tr = iter_tr;
> - dir = iter_dir;
> - if (dir->nr_events) {
> - __get_system_dir(dir);
> - system = dir->subsystem;
> - }
> - goto exit_loop;
> + if (!dir->nr_events)
> + return NULL;
> + if (__trace_array_get(tr) < 0)
> + return NULL;
> + __get_system_dir(dir);
> + return dir;
> }
> }
> }
> - exit_loop:
> - mutex_unlock(&trace_types_lock);
> - mutex_unlock(&event_mutex);
> + return NULL;
> +}
>
> - if (!system)
> +void trace_put_system_dir(struct trace_subsystem_dir *dir)
> +{
> + trace_array_put(dir->tr);
> + put_system(dir);
> +}
> +
> +static int subsystem_open(struct inode *inode, struct file *filp)
> +{
> + struct trace_subsystem_dir *dir;
> + int ret;
> +
> + if (tracing_is_disabled())
> return -ENODEV;
>
> - /* Still need to increment the ref count of the system */
> - if (trace_array_get(tr) < 0) {
> - put_system(dir);
> + dir = trace_get_system_dir(inode);
> + if (!dir)
> return -ENODEV;
> - }
>
> ret = tracing_open_generic(inode, filp);
> - if (ret < 0) {
> - trace_array_put(tr);
> - put_system(dir);
> - }
> + if (ret < 0)
> + trace_put_system_dir(dir);
>
> return ret;
> }
> @@ -2761,6 +2762,9 @@ static int system_callback(const char *name, umode_t *mode, void **data,
> else if (strcmp(name, "enable") == 0)
> *fops = &ftrace_system_enable_fops;
>
> + else if (strcmp(name, "trigger") == 0)
> + *fops = &event_system_trigger_fops;
> +
> else
> return 0;
>
> @@ -2784,6 +2788,10 @@ event_subsystem_dir(struct trace_array *tr, const char *name,
> {
> .name = "enable",
> .callback = system_callback,
> + },
> + {
> + .name = "trigger",
> + .callback = system_callback,
> }
> };
>
> diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
> index 1dfe69146a81..9249770679f3 100644
> --- a/kernel/trace/trace_events_trigger.c
> +++ b/kernel/trace/trace_events_trigger.c
> @@ -329,21 +329,28 @@ int trigger_process_regex(struct trace_event_file *file, char *buff)
> return -EINVAL;
> }
>
> +static char *get_user_buf(const char __user *ubuf, size_t cnt)
> +{
> + if (!cnt)
> + return NULL;
> +
> + if (cnt >= PAGE_SIZE)
> + return ERR_PTR(-EINVAL);
> +
> + return memdup_user_nul(ubuf, cnt);
> +}
> +
> static ssize_t event_trigger_regex_write(struct file *file,
> const char __user *ubuf,
> size_t cnt, loff_t *ppos)
> {
> struct trace_event_file *event_file;
> ssize_t ret;
> - char *buf __free(kfree) = NULL;
> + char *buf __free(kfree) = get_user_buf(ubuf, cnt);
>
> - if (!cnt)
> + if (!buf)
> return 0;
>
> - if (cnt >= PAGE_SIZE)
> - return -EINVAL;
> -
> - buf = memdup_user_nul(ubuf, cnt);
> if (IS_ERR(buf))
> return PTR_ERR(buf);
You can simply write:
if (IS_ERR_OR_NULL(buf))
return PTR_ERR_OR_ZERO(buf);
>
> @@ -397,6 +404,184 @@ const struct file_operations event_trigger_fops = {
> .release = event_trigger_release,
> };
>
> +static ssize_t
> +event_system_trigger_read(struct file *filp, char __user *ubuf,
> + size_t count, loff_t *ppos)
> +{
> + char *buf __free(kfree) = kmalloc(SZ_4K, GFP_KERNEL);
> + struct event_command *p;
> + struct seq_buf s;
> + int len;
> +
> + if (!buf)
> + return -ENOMEM;
> +
> + seq_buf_init(&s, buf, SZ_4K);
> +
> + seq_buf_puts(&s, "# Available system triggers:\n");
> + seq_buf_putc(&s, '#');
> +
> + guard(mutex)(&trigger_cmd_mutex);
> + list_for_each_entry_reverse(p, &trigger_commands, list) {
> + if (p->flags & EVENT_CMD_FL_SYSTEM)
> + seq_buf_printf(&s, " %s", p->name);
> + }
> + seq_buf_putc(&s, '\n');
> +
> + len = seq_buf_used(&s);
> +
> + if (*ppos >= len)
> + return 0;
> +
> + len -= *ppos;
> +
> + if (count > len)
> + count = len;
> +
> + if (copy_to_user(ubuf, buf + *ppos, count))
> + return -EFAULT;
> +
> + *ppos += count;
> +
> + return count;
> +}
> +
> +static int process_system_events(struct trace_subsystem_dir *dir,
> + struct event_command *p, char *buff,
> + char *command, char *next)
> +{
> + struct event_subsystem *system = dir->subsystem;
> + struct trace_event_file *file;
> + struct trace_array *tr = dir->tr;
> + bool remove = false;
> + int ret = 0;
> +
> + if (buff[0] == '!')
> + remove = true;
> +
> + lockdep_assert_held(&event_mutex);
> +
> + list_for_each_entry(file, &tr->events, list) {
> +
> + if (strcmp(system->name, file->event_call->class->system) != 0)
> + continue;
> +
> + ret = p->parse(p, file, buff, command, next);
> +
> + /* Removals and existing events do not error */
> + if (ret < 0 && ret != -EEXIST && !remove) {
> + pr_warn("Failed adding trigger %s on %s\n",
> + command, trace_event_name(file->event_call));
> + }
Can I expect that this can recover the previous settings
via event trigger?
e.g.
# echo "stacktrace" > events/sched/sched_wakeup/trigger
# echo "stacktrace" > events/sched/trigger
# echo "!stacktrace" > events/sched/trigger
# cat events/sched/sched_wakeup/trigger
stacktrace:unlimited
?
> + }
> + return 0;
> +}
> +
> +static ssize_t
> +event_system_trigger_write(struct file *filp, const char __user *ubuf,
> + size_t cnt, loff_t *ppos)
> +{
> + struct trace_subsystem_dir *dir = filp->private_data;
> + struct event_command *p;
> + char *command, *next;
> + char *buf __free(kfree) = get_user_buf(ubuf, cnt);
> + bool remove = false;
> + bool found = false;
> + ssize_t ret;
> +
> + if (!buf)
> + return 0;
> +
> + if (IS_ERR(buf))
> + return PTR_ERR(buf);
Ditto.
> +
> + /* system triggers are not allowed to have counters */
> + if (strchr(buf, ':'))
> + return -EINVAL;
':' is not always used for counters (e.g. hist) and it seems odd
to check anything about parse here. Can we do this counter check
after parse a command?
> +
> + /* If opened for read too, dir is in the seq_file descriptor */
> + if (filp->f_mode & FMODE_READ) {
> + struct seq_file *m = filp->private_data;
> + dir = m->private;
> + }
> +
> + /* Skip added space at beginning of buf */
> + next = strim(buf);
> +
> + command = strsep(&next, " \t");
> + if (next) {
> + next = skip_spaces(next);
> + if (!*next)
> + next = NULL;
> + }
strim() removes both leading and trailing whitespace. So this check is
not required.
Thanks,
> + if (command[0] == '!') {
> + remove = true;
> + command++;
> + }
> +
> + guard(mutex)(&event_mutex);
> + guard(mutex)(&trigger_cmd_mutex);
> +
> + list_for_each_entry(p, &trigger_commands, list) {
> + /* Allow to remove any trigger */
> + if (!remove && !(p->flags & EVENT_CMD_FL_SYSTEM))
> + continue;
> + if (strcmp(p->name, command) == 0) {
> + found = true;
> + ret = process_system_events(dir, p, buf, command, next);
> + break;
> + }
> + }
> +
> + if (!found)
> + ret = -ENODEV;
> +
> + if (!ret)
> + *ppos += cnt;
> +
> + if (remove || ret < 0)
> + return ret ? : cnt;
> +
> + return cnt;
> +}
> +
> +static int
> +event_system_trigger_open(struct inode *inode, struct file *file)
> +{
> + struct trace_subsystem_dir *dir;
> + int ret;
> +
> + ret = security_locked_down(LOCKDOWN_TRACEFS);
> + if (ret)
> + return ret;
> +
> + dir = trace_get_system_dir(inode);
> + if (!dir)
> + return -ENODEV;
> +
> + file->private_data = dir;
> +
> + return ret;
> +}
> +
> +static int
> +event_system_trigger_release(struct inode *inode, struct file *file)
> +{
> + struct trace_subsystem_dir *dir = inode->i_private;
> +
> + trace_put_system_dir(dir);
> +
> + return 0;
> +}
> +
> +const struct file_operations event_system_trigger_fops = {
> + .open = event_system_trigger_open,
> + .read = event_system_trigger_read,
> + .write = event_system_trigger_write,
> + .llseek = tracing_lseek,
> + .release = event_system_trigger_release,
> +};
> +
> /*
> * Currently we only register event commands from __init, so mark this
> * __init too.
> @@ -1587,7 +1772,7 @@ stacktrace_trigger_print(struct seq_file *m, struct event_trigger_data *data)
> static struct event_command trigger_stacktrace_cmd = {
> .name = "stacktrace",
> .trigger_type = ETT_STACKTRACE,
> - .flags = EVENT_CMD_FL_POST_TRIGGER,
> + .flags = EVENT_CMD_FL_POST_TRIGGER | EVENT_CMD_FL_SYSTEM,
> .parse = event_trigger_parse,
> .reg = register_trigger,
> .unreg = unregister_trigger,
> --
> 2.51.0
>
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply
* Re: [RFC PATCH net-next] page_pool: Add page_pool_release_stalled tracepoint
From: Leon Hwang @ 2025-11-26 2:16 UTC (permalink / raw)
To: Steven Rostedt
Cc: netdev, hawk, ilias.apalodimas, mhiramat, mathieu.desnoyers,
davem, edumazet, kuba, pabeni, horms, kerneljasonxing, lance.yang,
jiayuan.chen, linux-kernel, linux-trace-kernel, Leon Huang Fu
In-Reply-To: <20251125112304.493ea1ee@gandalf.local.home>
On 26/11/25 00:23, Steven Rostedt wrote:
> On Tue, 25 Nov 2025 16:22:07 +0800
> Leon Hwang <leon.hwang@linux.dev> wrote:
>
>> +TRACE_EVENT(page_pool_release_stalled,
>> +
>> + TP_PROTO(const struct page_pool *pool, int inflight, int sec),
>> +
>> + TP_ARGS(pool, inflight, sec),
>> +
>> + TP_STRUCT__entry(
>> + __field(const struct page_pool *, pool)
>> + __field(int, inflight)
>> + __field(int, sec)
>> + ),
>> +
>> + TP_fast_assign(
>> + __entry->pool = pool;
>> + __entry->inflight = inflight;
>> + __entry->sec = sec;
>> + ),
>> +
>> + TP_printk("page_pool=%p id=%d inflight=%d sec=%d",
>> + __entry->pool, __entry->pool->user.id, __entry->inflight, __entry->sec)
>
> You can't do: __entry->pool->user.id
>
> The TP_fast_assign() is executed when the tracepoint is triggered. The
> TP_printk() is executed when the trace is read. That can happen seconds,
> minutes, hours, days, even months after the pool was assigned.
>
> That __entry->pool can very well be freed a long time ago.
>
> If you need the id, you need to record it in the TP_fast_assign():
>
> __entry->id = pool->user.id
>
> and print that.
>
> -- Steve
>
Hi Steve,
Thanks for the review.
Yes, the id is needed here (similar to what we emit in pr_warn()), so
I'll follow your suggestion and record it explicitly in TP_fast_assign():
__entry->id = pool->user.id;
And then update the print format to:
TP_printk("page_pool=%p id=%d inflight=%d sec=%d",
__entry->pool, __entry->id,
__entry->inflight, __entry->sec)
Thanks,
Leon
^ permalink raw reply
* Re: [PATCH v2] dma-buf: add some tracepoints to debug.
From: Steven Rostedt @ 2025-11-26 1:39 UTC (permalink / raw)
To: Xiang Gao
Cc: sumit.semwal, christian.koenig, mhiramat, linux-media, dri-devel,
linaro-mm-sig, linux-kernel, mathieu.desnoyers, dhowells, kuba,
brauner, akpm, linux-trace-kernel, gaoxiang17
In-Reply-To: <20251125162949.220488-1-gxxa03070307@gmail.com>
On Wed, 26 Nov 2025 00:29:49 +0800
Xiang Gao <gxxa03070307@gmail.com> wrote:
> +++ b/include/trace/events/dma_buf.h
> @@ -0,0 +1,281 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#undef TRACE_SYSTEM
> +#define TRACE_SYSTEM dma_buf
> +
> +#if !defined(_TRACE_DMA_BUF_H) || defined(TRACE_HEADER_MULTI_READ)
> +#define _TRACE_DMA_BUF_H
> +
> +#include <linux/dma-buf.h>
> +#include <linux/tracepoint.h>
> +
> +TRACE_EVENT(dma_buf_export,
> +
> + TP_PROTO(struct dma_buf *dmabuf),
> +
> + TP_ARGS(dmabuf),
> +
> + TP_STRUCT__entry(
> + __string(exp_name, dmabuf->exp_name)
> + __string(name, dmabuf->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
The above isn't doing what you think it's doing. The name is assigned
before this by the above __string(name, dmabuf->name).
You really shouldn't be taking any locks in a tracepoint. A tracepoint is a
callback, that isn't called most of the time. You could be hiding very
hard to find deadlocks by taking a lock in a tracepoint callback.
You need to take the lock around the tracepoint call itself where it is
called in the code. Not in the TRACE_EVENT.
You may need to have something like:
@@ -220,6 +223,8 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
dmabuf->size >> PAGE_SHIFT)
return -EINVAL;
+ if (trace_dma_buf_mmap_internal_enabled()) {
+ guard(spinlock)(&dmabuf->namelock);
+ trace_dma_buf_mmap_internal(dmabuf);
+ }
+
return dmabuf->ops->mmap(dmabuf, vma);
}
The "trace_dma_buf_mmap_internal_enabled()" is a static branch, where it is
either a nop or a jump to the tracing code. It's not a normal conditional
branch. It acts the same as tracepoints themselves do.
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->f_refcnt = file_count(dmabuf->file);
> + ),
> +
> + TP_printk("exp_name=%s name=%s size=%zu ino=%lu f_refcnt=%ld",
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->f_refcnt)
> +);
Below seems to be a lot of very similar TRACE_EVENT()s. A TRACE_EVENT() is
literally defined as:
#define TRACE_EVENT(name, proto, args, tstruct, assign, print) \
DECLARE_EVENT_CLASS(name, \
PARAMS(proto), \
PARAMS(args), \
PARAMS(tstruct), \
PARAMS(assign), \
PARAMS(print)); \
DEFINE_EVENT(name, name, PARAMS(proto), PARAMS(args));
That is, it is both a DECLARE_EVENT_CLASS() and a DEFINE_EVENT(). You can
make one DECLARE_EVENT_CLASS() and use many DEFINE_EVENT()s with it.
Each DECLARE_EVENT_CLASS() takes up around 4 to 5 kilobytes of memory. Each
DEFINE_EVENT() takes around 300 bytes to 1K of memory. The more
DEFINE_EVENT()s you use with a single DECLARE_EVENT_CLASS(), the more
memory you save. Please try to do that.
-- Steve
> +
> +TRACE_EVENT(dma_buf_fd,
> +
> + TP_PROTO(struct dma_buf *dmabuf, int fd),
> +
> + TP_ARGS(dmabuf, fd),
> +
> + TP_STRUCT__entry(
> + __string(exp_name, dmabuf->exp_name)
> + __string(name, dmabuf->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(int, fd)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->fd = fd;
> + __entry->f_refcnt = file_count(dmabuf->file);
> + ),
> +
> + TP_printk("exp_name=%s name=%s size=%zu ino=%lu fd=%d f_refcnt=%ld",
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->fd,
> + __entry->f_refcnt)
> +);
> +
> +TRACE_EVENT(dma_buf_mmap_internal,
> +
> + TP_PROTO(struct dma_buf *dmabuf),
> +
> + TP_ARGS(dmabuf),
> +
> + TP_STRUCT__entry(
> + __string(exp_name, dmabuf->exp_name)
> + __string(name, dmabuf->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->f_refcnt = file_count(dmabuf->file);
> + ),
> +
> + TP_printk("exp_name=%s name=%s size=%zu ino=%lu f_refcnt=%ld",
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->f_refcnt)
> +);
> +
> +TRACE_EVENT(dma_buf_mmap,
> +
> + TP_PROTO(struct dma_buf *dmabuf),
> +
> + TP_ARGS(dmabuf),
> +
> + TP_STRUCT__entry(
> + __string(exp_name, dmabuf->exp_name)
> + __string(name, dmabuf->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->f_refcnt = file_count(dmabuf->file);
> + ),
> +
> + TP_printk("exp_name=%s name=%s size=%zu ino=%lu f_refcnt=%ld",
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->f_refcnt)
> +);
> +
> +TRACE_EVENT(dma_buf_attach,
> +
> + TP_PROTO(struct dma_buf *dmabuf, struct device *dev),
> +
> + TP_ARGS(dmabuf, dev),
> +
> + TP_STRUCT__entry(
> + __string(dname, dev_name(dev))
> + __string(exp_name, dmabuf->exp_name)
> + __string(name, dmabuf->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(dname);
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->f_refcnt = file_count(dmabuf->file);
> + ),
> +
> + TP_printk("dev_name=%s exp_name=%s name=%s size=%zu ino=%lu f_refcnt=%ld",
> + __get_str(dname),
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->f_refcnt)
> +);
> +
> +TRACE_EVENT(dma_buf_detach,
> +
> + TP_PROTO(struct dma_buf *dmabuf),
> +
> + TP_ARGS(dmabuf),
> +
> + TP_STRUCT__entry(
> + __string(exp_name, dmabuf->exp_name)
> + __string(name, dmabuf->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->f_refcnt = file_count(dmabuf->file);
> + ),
> +
> + TP_printk("exp_name=%s name=%s size=%zu ino=%lu f_refcnt=%ld",
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->f_refcnt)
> +);
> +
> +TRACE_EVENT(dma_buf_get,
> +
> + TP_PROTO(int fd, struct file *file),
> +
> + TP_ARGS(fd, file),
> +
> + TP_STRUCT__entry(
> + __string(exp_name, ((struct dma_buf *)file->private_data)->exp_name)
> + __string(name, ((struct dma_buf *)file->private_data)->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(int, fd)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + struct dma_buf *dmabuf = (struct dma_buf *)file->private_data;
> +
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->fd = fd;
> + __entry->f_refcnt = file_count(file);
> + ),
> +
> + TP_printk("exp_name=%s name=%s size=%zu ino=%lu fd=%d f_refcnt=%ld",
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->fd,
> + __entry->f_refcnt)
> +);
> +
> +TRACE_EVENT(dma_buf_put,
> +
> + TP_PROTO(struct dma_buf *dmabuf),
> +
> + TP_ARGS(dmabuf),
> +
> + TP_STRUCT__entry(
> + __string(exp_name, dmabuf->exp_name)
> + __string(name, dmabuf->name)
> + __field(size_t, size)
> + __field(ino_t, ino)
> + __field(long, f_refcnt)
> + ),
> +
> + TP_fast_assign(
> + __assign_str(exp_name);
> + spin_lock(&dmabuf->name_lock);
> + __assign_str(name);
> + spin_unlock(&dmabuf->name_lock);
> + __entry->size = dmabuf->size;
> + __entry->ino = dmabuf->file->f_inode->i_ino;
> + __entry->f_refcnt = file_count(dmabuf->file);
> + ),
> +
> + TP_printk("exp_name=%s name=%s size=%zu ino=%lu f_refcnt=%ld",
> + __get_str(exp_name),
> + __get_str(name),
> + __entry->size,
> + __entry->ino,
> + __entry->f_refcnt)
> +);
> +
> +#endif /* _TRACE_DMA_BUF_H */
> +
> +/* This part must be outside protection */
> +#include <trace/define_trace.h>
^ permalink raw reply
* [PATCH v2] tracing: Add system trigger file to enable triggers for all the system's events
From: Steven Rostedt @ 2025-11-26 1:08 UTC (permalink / raw)
To: LKML, Linux Trace Kernel; +Cc: Masami Hiramatsu, Mathieu Desnoyers, Tom Zanussi
From: Steven Rostedt <rostedt@goodmis.org>
Currently a trigger can only be added to individual events. Some triggers
(like stacktrace) can be useful to add as a bulk trigger for a set of
system events (like interrupt or scheduling).
Add a trigger file to the system directories:
/sys/kernel/tracing/events/*/trigger
And allow stacktrace trigger to be enabled for all those events.
Writing into the system/trigger file acts the same as writing into each of
the system event's trigger files individually.
This also allows to remove a trigger from all events in a subsystem (even
if it's not a subsystem trigger!).
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Note, this is based on top of:
https://patchwork.kernel.org/project/linux-trace-kernel/cover/20251120205600.570673392@kernel.org/
Changes since v1: https://patch.msgid.link/20251120160003.2fa33d80@gandalf.local.home
- Removed unused set variable len (kernel test robot)
- Assign next to strim(buf) to remove beginning spaces
Documentation/trace/events.rst | 25 ++++
kernel/trace/trace.c | 11 +-
kernel/trace/trace.h | 15 ++-
kernel/trace/trace_events.c | 70 +++++-----
kernel/trace/trace_events_trigger.c | 199 +++++++++++++++++++++++++++-
5 files changed, 278 insertions(+), 42 deletions(-)
diff --git a/Documentation/trace/events.rst b/Documentation/trace/events.rst
index 18d112963dec..caa4958af43a 100644
--- a/Documentation/trace/events.rst
+++ b/Documentation/trace/events.rst
@@ -416,6 +416,31 @@ way, so beware about making generalizations between the two.
can also enable triggers that are written into
/sys/kernel/tracing/events/ftrace/print/trigger
+The system directory also has a trigger file that allows some triggers to be
+set for all the system's events. This is limited to only a small subset of the
+triggers and does not allow for the count parameter. But it does allow for
+filters. Writing into this file is the same as writing into each of the
+system's event's trigger files individually. Although only a subset of
+triggers may use this file for enabling, all triggers may use this file for
+disabling::
+
+ cd /sys/kernel/tracing
+ cat events/sched/trigger
+ # Available system triggers:
+ # stacktrace
+
+ echo stacktrace > events/sched/trigger
+ cat events/sched/sched_switch/trigger
+ stacktrace:unlimited
+
+ echo snapshot > events/sched/sched_waking/trigger
+ cat events/sched/sched_waking/trigger
+ snapshot:unlimited
+ echo '!snapshot' > events/sched/trigger
+ cat events/sched/sched_waking/trigger
+ # Available triggers:
+ # traceon traceoff snapshot stacktrace enable_event disable_event enable_hist disable_hist hist
+
6.1 Expression syntax
---------------------
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 032bdedca5d9..feced9f43156 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -592,11 +592,12 @@ void trace_set_ring_buffer_expanded(struct trace_array *tr)
LIST_HEAD(ftrace_trace_arrays);
-int trace_array_get(struct trace_array *this_tr)
+int __trace_array_get(struct trace_array *this_tr)
{
struct trace_array *tr;
- guard(mutex)(&trace_types_lock);
+ lockdep_assert_held(&trace_types_lock);
+
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
if (tr == this_tr) {
tr->ref++;
@@ -607,6 +608,12 @@ int trace_array_get(struct trace_array *this_tr)
return -ENODEV;
}
+int trace_array_get(struct trace_array *tr)
+{
+ guard(mutex)(&trace_types_lock);
+ return __trace_array_get(tr);
+}
+
static void __trace_array_put(struct trace_array *this_tr)
{
WARN_ON(!this_tr->ref);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index fd5a6daa6c25..7379763a057d 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -469,10 +469,14 @@ extern struct list_head ftrace_trace_arrays;
extern struct mutex trace_types_lock;
extern int trace_array_get(struct trace_array *tr);
+extern int __trace_array_get(struct trace_array *tr);
extern int tracing_check_open_get_tr(struct trace_array *tr);
extern struct trace_array *trace_array_find(const char *instance);
extern struct trace_array *trace_array_find_get(const char *instance);
+extern struct trace_subsystem_dir *trace_get_system_dir(struct inode *inode);
+void trace_put_system_dir(struct trace_subsystem_dir *dir);
+
extern u64 tracing_event_time_stamp(struct trace_buffer *buffer, struct ring_buffer_event *rbe);
extern int tracing_set_filter_buffering(struct trace_array *tr, bool set);
extern int tracing_set_clock(struct trace_array *tr, const char *clockstr);
@@ -1774,6 +1778,7 @@ static inline struct trace_event_file *event_file_file(struct file *filp)
}
extern const struct file_operations event_trigger_fops;
+extern const struct file_operations event_system_trigger_fops;
extern const struct file_operations event_hist_fops;
extern const struct file_operations event_hist_debug_fops;
extern const struct file_operations event_inject_fops;
@@ -2057,10 +2062,16 @@ struct event_command {
* regardless of whether or not it has a filter associated with
* it (filters make a trigger require access to the trace record
* but are not always present).
+ *
+ * @SYSTEM: A flag that says whether or not this command can be used
+ * at the event system level. For example, can it be written into
+ * events/sched/trigger file where it will be enabled for all
+ * sched events?
*/
enum event_command_flags {
- EVENT_CMD_FL_POST_TRIGGER = 1,
- EVENT_CMD_FL_NEEDS_REC = 2,
+ EVENT_CMD_FL_POST_TRIGGER = BIT(1),
+ EVENT_CMD_FL_NEEDS_REC = BIT(2),
+ EVENT_CMD_FL_SYSTEM = BIT(3),
};
static inline bool event_command_post_trigger(struct event_command *cmd_ops)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 9b07ad9eb284..f00b41f73fc2 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -2168,51 +2168,52 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
static LIST_HEAD(event_subsystems);
-static int subsystem_open(struct inode *inode, struct file *filp)
+struct trace_subsystem_dir *trace_get_system_dir(struct inode *inode)
{
- struct trace_subsystem_dir *dir = NULL, *iter_dir;
- struct trace_array *tr = NULL, *iter_tr;
- struct event_subsystem *system = NULL;
- int ret;
+ struct trace_subsystem_dir *dir;
+ struct trace_array *tr = NULL;
- if (tracing_is_disabled())
- return -ENODEV;
+ guard(mutex)(&event_mutex);
+ guard(mutex)(&trace_types_lock);
/* Make sure the system still exists */
- mutex_lock(&event_mutex);
- mutex_lock(&trace_types_lock);
- list_for_each_entry(iter_tr, &ftrace_trace_arrays, list) {
- list_for_each_entry(iter_dir, &iter_tr->systems, list) {
- if (iter_dir == inode->i_private) {
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) {
+ list_for_each_entry(dir, &tr->systems, list) {
+ if (dir == inode->i_private) {
/* Don't open systems with no events */
- tr = iter_tr;
- dir = iter_dir;
- if (dir->nr_events) {
- __get_system_dir(dir);
- system = dir->subsystem;
- }
- goto exit_loop;
+ if (!dir->nr_events)
+ return NULL;
+ if (__trace_array_get(tr) < 0)
+ return NULL;
+ __get_system_dir(dir);
+ return dir;
}
}
}
- exit_loop:
- mutex_unlock(&trace_types_lock);
- mutex_unlock(&event_mutex);
+ return NULL;
+}
- if (!system)
+void trace_put_system_dir(struct trace_subsystem_dir *dir)
+{
+ trace_array_put(dir->tr);
+ put_system(dir);
+}
+
+static int subsystem_open(struct inode *inode, struct file *filp)
+{
+ struct trace_subsystem_dir *dir;
+ int ret;
+
+ if (tracing_is_disabled())
return -ENODEV;
- /* Still need to increment the ref count of the system */
- if (trace_array_get(tr) < 0) {
- put_system(dir);
+ dir = trace_get_system_dir(inode);
+ if (!dir)
return -ENODEV;
- }
ret = tracing_open_generic(inode, filp);
- if (ret < 0) {
- trace_array_put(tr);
- put_system(dir);
- }
+ if (ret < 0)
+ trace_put_system_dir(dir);
return ret;
}
@@ -2761,6 +2762,9 @@ static int system_callback(const char *name, umode_t *mode, void **data,
else if (strcmp(name, "enable") == 0)
*fops = &ftrace_system_enable_fops;
+ else if (strcmp(name, "trigger") == 0)
+ *fops = &event_system_trigger_fops;
+
else
return 0;
@@ -2784,6 +2788,10 @@ event_subsystem_dir(struct trace_array *tr, const char *name,
{
.name = "enable",
.callback = system_callback,
+ },
+ {
+ .name = "trigger",
+ .callback = system_callback,
}
};
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 1dfe69146a81..9249770679f3 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -329,21 +329,28 @@ int trigger_process_regex(struct trace_event_file *file, char *buff)
return -EINVAL;
}
+static char *get_user_buf(const char __user *ubuf, size_t cnt)
+{
+ if (!cnt)
+ return NULL;
+
+ if (cnt >= PAGE_SIZE)
+ return ERR_PTR(-EINVAL);
+
+ return memdup_user_nul(ubuf, cnt);
+}
+
static ssize_t event_trigger_regex_write(struct file *file,
const char __user *ubuf,
size_t cnt, loff_t *ppos)
{
struct trace_event_file *event_file;
ssize_t ret;
- char *buf __free(kfree) = NULL;
+ char *buf __free(kfree) = get_user_buf(ubuf, cnt);
- if (!cnt)
+ if (!buf)
return 0;
- if (cnt >= PAGE_SIZE)
- return -EINVAL;
-
- buf = memdup_user_nul(ubuf, cnt);
if (IS_ERR(buf))
return PTR_ERR(buf);
@@ -397,6 +404,184 @@ const struct file_operations event_trigger_fops = {
.release = event_trigger_release,
};
+static ssize_t
+event_system_trigger_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf __free(kfree) = kmalloc(SZ_4K, GFP_KERNEL);
+ struct event_command *p;
+ struct seq_buf s;
+ int len;
+
+ if (!buf)
+ return -ENOMEM;
+
+ seq_buf_init(&s, buf, SZ_4K);
+
+ seq_buf_puts(&s, "# Available system triggers:\n");
+ seq_buf_putc(&s, '#');
+
+ guard(mutex)(&trigger_cmd_mutex);
+ list_for_each_entry_reverse(p, &trigger_commands, list) {
+ if (p->flags & EVENT_CMD_FL_SYSTEM)
+ seq_buf_printf(&s, " %s", p->name);
+ }
+ seq_buf_putc(&s, '\n');
+
+ len = seq_buf_used(&s);
+
+ if (*ppos >= len)
+ return 0;
+
+ len -= *ppos;
+
+ if (count > len)
+ count = len;
+
+ if (copy_to_user(ubuf, buf + *ppos, count))
+ return -EFAULT;
+
+ *ppos += count;
+
+ return count;
+}
+
+static int process_system_events(struct trace_subsystem_dir *dir,
+ struct event_command *p, char *buff,
+ char *command, char *next)
+{
+ struct event_subsystem *system = dir->subsystem;
+ struct trace_event_file *file;
+ struct trace_array *tr = dir->tr;
+ bool remove = false;
+ int ret = 0;
+
+ if (buff[0] == '!')
+ remove = true;
+
+ lockdep_assert_held(&event_mutex);
+
+ list_for_each_entry(file, &tr->events, list) {
+
+ if (strcmp(system->name, file->event_call->class->system) != 0)
+ continue;
+
+ ret = p->parse(p, file, buff, command, next);
+
+ /* Removals and existing events do not error */
+ if (ret < 0 && ret != -EEXIST && !remove) {
+ pr_warn("Failed adding trigger %s on %s\n",
+ command, trace_event_name(file->event_call));
+ }
+ }
+ return 0;
+}
+
+static ssize_t
+event_system_trigger_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct trace_subsystem_dir *dir = filp->private_data;
+ struct event_command *p;
+ char *command, *next;
+ char *buf __free(kfree) = get_user_buf(ubuf, cnt);
+ bool remove = false;
+ bool found = false;
+ ssize_t ret;
+
+ if (!buf)
+ return 0;
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* system triggers are not allowed to have counters */
+ if (strchr(buf, ':'))
+ return -EINVAL;
+
+ /* If opened for read too, dir is in the seq_file descriptor */
+ if (filp->f_mode & FMODE_READ) {
+ struct seq_file *m = filp->private_data;
+ dir = m->private;
+ }
+
+ /* Skip added space at beginning of buf */
+ next = strim(buf);
+
+ command = strsep(&next, " \t");
+ if (next) {
+ next = skip_spaces(next);
+ if (!*next)
+ next = NULL;
+ }
+ if (command[0] == '!') {
+ remove = true;
+ command++;
+ }
+
+ guard(mutex)(&event_mutex);
+ guard(mutex)(&trigger_cmd_mutex);
+
+ list_for_each_entry(p, &trigger_commands, list) {
+ /* Allow to remove any trigger */
+ if (!remove && !(p->flags & EVENT_CMD_FL_SYSTEM))
+ continue;
+ if (strcmp(p->name, command) == 0) {
+ found = true;
+ ret = process_system_events(dir, p, buf, command, next);
+ break;
+ }
+ }
+
+ if (!found)
+ ret = -ENODEV;
+
+ if (!ret)
+ *ppos += cnt;
+
+ if (remove || ret < 0)
+ return ret ? : cnt;
+
+ return cnt;
+}
+
+static int
+event_system_trigger_open(struct inode *inode, struct file *file)
+{
+ struct trace_subsystem_dir *dir;
+ int ret;
+
+ ret = security_locked_down(LOCKDOWN_TRACEFS);
+ if (ret)
+ return ret;
+
+ dir = trace_get_system_dir(inode);
+ if (!dir)
+ return -ENODEV;
+
+ file->private_data = dir;
+
+ return ret;
+}
+
+static int
+event_system_trigger_release(struct inode *inode, struct file *file)
+{
+ struct trace_subsystem_dir *dir = inode->i_private;
+
+ trace_put_system_dir(dir);
+
+ return 0;
+}
+
+const struct file_operations event_system_trigger_fops = {
+ .open = event_system_trigger_open,
+ .read = event_system_trigger_read,
+ .write = event_system_trigger_write,
+ .llseek = tracing_lseek,
+ .release = event_system_trigger_release,
+};
+
/*
* Currently we only register event commands from __init, so mark this
* __init too.
@@ -1587,7 +1772,7 @@ stacktrace_trigger_print(struct seq_file *m, struct event_trigger_data *data)
static struct event_command trigger_stacktrace_cmd = {
.name = "stacktrace",
.trigger_type = ETT_STACKTRACE,
- .flags = EVENT_CMD_FL_POST_TRIGGER,
+ .flags = EVENT_CMD_FL_POST_TRIGGER | EVENT_CMD_FL_SYSTEM,
.parse = event_trigger_parse,
.reg = register_trigger,
.unreg = unregister_trigger,
--
2.51.0
^ permalink raw reply related
* [PATCH v2 3/3] tracing: Use strim() in trigger_process_regex() instead of skip_spaces()
From: Steven Rostedt @ 2025-11-25 21:40 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
Tom Zanussi
In-Reply-To: <20251125214004.396482401@kernel.org>
From: Steven Rostedt <rostedt@goodmis.org>
The function trigger_process_regex() is called by a few functions, where
only one calls strim() on the buffer passed to it. That leaves the other
functions not trimming the end of the buffer passed in and making it a
little inconsistent.
Remove the strim() from event_trigger_regex_write() and have
trigger_process_regex() use strim() instead of skip_spaces(). The buff
variable is not passed in as const, so it can be modified.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
kernel/trace/trace_events_trigger.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 3b97c242b795..96aad82b1628 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -308,7 +308,8 @@ int trigger_process_regex(struct trace_event_file *file, char *buff)
char *command, *next;
struct event_command *p;
- next = buff = skip_spaces(buff);
+ next = buff = strim(buff);
+
command = strsep(&next, ": \t");
if (next) {
next = skip_spaces(next);
@@ -345,8 +346,6 @@ static ssize_t event_trigger_regex_write(struct file *file,
if (IS_ERR(buf))
return PTR_ERR(buf);
- strim(buf);
-
guard(mutex)(&event_mutex);
event_file = event_file_file(file);
--
2.51.0
^ permalink raw reply related
* [PATCH v2 2/3] tracing: Add bulk garbage collection of freeing event_trigger_data
From: Steven Rostedt @ 2025-11-25 21:40 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
Tom Zanussi
In-Reply-To: <20251125214004.396482401@kernel.org>
From: Steven Rostedt <rostedt@goodmis.org>
The event trigger data requires a full tracepoint_synchronize_unregister()
call before freeing. That call can take 100s of milliseconds to complete.
In order to allow for bulk freeing of the trigger data, it can not call
the tracepoint_synchronize_unregister() for every individual trigger data
being free.
Create a kthread that gets created the first time a trigger data is freed,
and have it use the lockless llist to get the list of data to free, run
the tracepoint_synchronize_unregister() then free everything in the list.
By freeing hundreds of event_trigger_data elements together, it only
requires two runs of the synchronization function, and not hundreds of
runs. This speeds up the operation by orders of magnitude (milliseconds
instead of several seconds).
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
Changes since v1: https://patch.msgid.link/20251120205710.151041470@kernel.org
- Moved include of llist.h to trace.h as it is used there (Masami Hiramatsu)
kernel/trace/trace.h | 2 ++
kernel/trace/trace_events_trigger.c | 55 +++++++++++++++++++++++++++--
2 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 5863800b1ab3..911fc75dc6c4 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -22,6 +22,7 @@
#include <linux/ctype.h>
#include <linux/once_lite.h>
#include <linux/ftrace_regs.h>
+#include <linux/llist.h>
#include "pid_list.h"
@@ -1808,6 +1809,7 @@ struct event_trigger_data {
char *name;
struct list_head named_list;
struct event_trigger_data *named_data;
+ struct llist_node llist;
};
/* Avoid typos */
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index e5dcfcbb2cd5..3b97c242b795 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -6,6 +6,7 @@
*/
#include <linux/security.h>
+#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/mutex.h>
@@ -17,15 +18,63 @@
static LIST_HEAD(trigger_commands);
static DEFINE_MUTEX(trigger_cmd_mutex);
+static struct task_struct *trigger_kthread;
+static struct llist_head trigger_data_free_list;
+static DEFINE_MUTEX(trigger_data_kthread_mutex);
+
+/* Bulk garbage collection of event_trigger_data elements */
+static int trigger_kthread_fn(void *ignore)
+{
+ struct event_trigger_data *data, *tmp;
+ struct llist_node *llnodes;
+
+ /* Once this task starts, it lives forever */
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (llist_empty(&trigger_data_free_list))
+ schedule();
+
+ __set_current_state(TASK_RUNNING);
+
+ llnodes = llist_del_all(&trigger_data_free_list);
+
+ /* make sure current triggers exit before free */
+ tracepoint_synchronize_unregister();
+
+ llist_for_each_entry_safe(data, tmp, llnodes, llist)
+ kfree(data);
+ }
+
+ return 0;
+}
+
void trigger_data_free(struct event_trigger_data *data)
{
if (data->cmd_ops->set_filter)
data->cmd_ops->set_filter(NULL, data, NULL);
- /* make sure current triggers exit before free */
- tracepoint_synchronize_unregister();
+ if (unlikely(!trigger_kthread)) {
+ guard(mutex)(&trigger_data_kthread_mutex);
+ /* Check again after taking mutex */
+ if (!trigger_kthread) {
+ struct task_struct *kthread;
+
+ kthread = kthread_create(trigger_kthread_fn, NULL,
+ "trigger_data_free");
+ if (!IS_ERR(kthread))
+ WRITE_ONCE(trigger_kthread, kthread);
+ }
+ }
+
+ if (!trigger_kthread) {
+ /* Do it the slow way */
+ tracepoint_synchronize_unregister();
+ kfree(data);
+ return;
+ }
- kfree(data);
+ llist_add(&data->llist, &trigger_data_free_list);
+ wake_up_process(trigger_kthread);
}
static inline void data_ops_trigger(struct event_trigger_data *data,
--
2.51.0
^ permalink raw reply related
* [PATCH v2 1/3] tracing: Remove unneeded event_mutex lock in event_trigger_regex_release()
From: Steven Rostedt @ 2025-11-25 21:40 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
Tom Zanussi
In-Reply-To: <20251125214004.396482401@kernel.org>
From: Steven Rostedt <rostedt@goodmis.org>
In event_trigger_regex_release(), the only code is:
mutex_lock(&event_mutex);
if (file->f_mode & FMODE_READ)
seq_release(inode, file);
mutex_unlock(&event_mutex);
return 0;
There's nothing special about the file->f_mode or the seq_release() that
requires any locking. Remove the unnecessary locks.
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
kernel/trace/trace_events_trigger.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
index 7795af600466..e5dcfcbb2cd5 100644
--- a/kernel/trace/trace_events_trigger.c
+++ b/kernel/trace/trace_events_trigger.c
@@ -314,13 +314,9 @@ static ssize_t event_trigger_regex_write(struct file *file,
static int event_trigger_regex_release(struct inode *inode, struct file *file)
{
- mutex_lock(&event_mutex);
-
if (file->f_mode & FMODE_READ)
seq_release(inode, file);
- mutex_unlock(&event_mutex);
-
return 0;
}
--
2.51.0
^ permalink raw reply related
* [PATCH v2 0/3] tracing: More clean ups of triggers
From: Steven Rostedt @ 2025-11-25 21:40 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel
Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
Tom Zanussi
This is based on top of:
https://lore.kernel.org/linux-trace-kernel/20251125200857.975524485@kernel.org
Changes since v1: https://lore.kernel.org/linux-trace-kernel/20251120205600.570673392@kernel.org/
- Moved include of llist.h to trace.h as it is used there (Masami Hiramatsu)
Steven Rostedt (3):
tracing: Remove unneeded event_mutex lock in event_trigger_regex_release()
tracing: Add bulk garbage collection of freeing event_trigger_data
tracing: Use strim() in trigger_process_regex() instead of skip_spaces()
----
kernel/trace/trace.h | 2 ++
kernel/trace/trace_events_trigger.c | 64 +++++++++++++++++++++++++++++++------
2 files changed, 56 insertions(+), 10 deletions(-)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox