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

Pull runtime verification updates from Steven Rostedt:

 - Refactor da_monitor header to share handlers across monitor types

   No functional changes, only less code duplication.

 - Add Hybrid Automata model class

   Add a new model class that extends deterministic automata by adding
   constraints on transitions and states. Those constraints can take
   into account wall-clock time and as such allow RV monitor to make
   assertions on real time. Add documentation and code generation
   scripts.

 - Add stall monitor as hybrid automaton example

   Add a monitor that triggers a violation when a task is stalling as an
   example of automaton working with real time variables.

 - Convert the opid monitor to a hybrid automaton

   The opid monitor can be heavily simplified if written as a hybrid
   automaton: instead of tracking preempt and interrupt enable/disable
   events, it can just run constraints on the preemption/interrupt
   states when events like wakeup and need_resched verify.

 - Add support for per-object monitors in DA/HA

   Allow writing deterministic and hybrid automata monitors for generic
   objects (e.g. any struct), by exploiting a hash table where objects
   are saved. This allows to track more than just tasks in RV. For
   instance it will be used to track deadline entities in deadline
   monitors.

 - Add deadline tracepoints and move some deadline utilities

   Prepare the ground for deadline monitors by defining events and
   exporting helpers.

 - Add nomiss deadline monitor

   Add first example of deadline monitor asserting all entities complete
   before their deadline.

 - Improve rvgen error handling

   Introduce AutomataError exception class and better handle expected
   exceptions while showing a backtrace for unexpected ones.

 - Improve python code quality in rvgen

   Refactor the rvgen generation scripts to align with python best
   practices: use f-strings instead of %, use len() instead of
   __len__(), remove semicolons, use context managers for file
   operations, fix whitespace violations, extract magic strings into
   constants, remove unused imports and methods.

 - Fix small bugs in rvgen

   The generator scripts presented some corner case bugs: logical error
   in validating what a correct dot file looks like, fix an isinstance()
   check, enforce a dot file has an initial state, fix type annotations
   and typos in comments.

 - rvgen refactoring

   Refactor automata.py to use iterator-based parsing and handle
   required arguments directly in argparse.

 - Allow epoll in rtapp-sleep monitor

   The epoll_wait call is now rt-friendly so it should be allowed in the
   sleep monitor as a valid sleep method.

* tag 'trace-rv-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (32 commits)
  rv: Allow epoll in rtapp-sleep monitor
  rv/rvgen: fix _fill_states() return type annotation
  rv/rvgen: fix unbound loop variable warning
  rv/rvgen: enforce presence of initial state
  rv/rvgen: extract node marker string to class constant
  rv/rvgen: fix isinstance check in Variable.expand()
  rv/rvgen: make monitor arguments required in rvgen
  rv/rvgen: remove unused __get_main_name method
  rv/rvgen: remove unused sys import from dot2c
  rv/rvgen: refactor automata.py to use iterator-based parsing
  rv/rvgen: use class constant for init marker
  rv/rvgen: fix DOT file validation logic error
  rv/rvgen: fix PEP 8 whitespace violations
  rv/rvgen: fix typos in automata and generator docstring and comments
  rv/rvgen: use context managers for file operations
  rv/rvgen: remove unnecessary semicolons
  rv/rvgen: replace __len__() calls with len()
  rv/rvgen: replace % string formatting with f-strings
  rv/rvgen: remove bare except clauses in generator
  rv/rvgen: introduce AutomataError exception class
  ...
This commit is contained in:
Linus Torvalds
2026-04-15 17:15:18 -07:00
50 changed files with 3891 additions and 694 deletions
+1
View File
@@ -16,3 +16,4 @@ Runtime verification (rv) tool
rv-mon-wip
rv-mon-wwnr
rv-mon-sched
rv-mon-stall
+44
View File
@@ -0,0 +1,44 @@
.. SPDX-License-Identifier: GPL-2.0
============
rv-mon-stall
============
--------------------
Stalled task monitor
--------------------
:Manual section: 1
SYNOPSIS
========
**rv mon stall** [*OPTIONS*]
DESCRIPTION
===========
The stalled task (**stall**) monitor is a sample per-task timed monitor that
checks if tasks are scheduled within a defined threshold after they are ready.
See kernel documentation for further information about this monitor:
<https://docs.kernel.org/trace/rv/monitor_stall.html>
OPTIONS
=======
.. include:: common_ikm.rst
SEE ALSO
========
**rv**\(1), **rv-mon**\(1)
Linux kernel *RV* documentation:
<https://www.kernel.org/doc/html/latest/trace/rv/index.html>
AUTHOR
======
Written by Gabriele Monaco <gmonaco@redhat.com>
.. include:: common_appendix.rst
@@ -11,7 +11,7 @@ where:
- *E* is the finite set of events;
- x\ :subscript:`0` is the initial state;
- X\ :subscript:`m` (subset of *X*) is the set of marked (or final) states.
- *f* : *X* x *E* -> *X* $ is the transition function. It defines the state
- *f* : *X* x *E* -> *X* is the transition function. It defines the state
transition in the occurrence of an event from *E* in the state *X*. In the
special case of deterministic automata, the occurrence of the event in *E*
in a state in *X* has a deterministic next state from *X*.
+341
View File
@@ -0,0 +1,341 @@
Hybrid Automata
===============
Hybrid automata are an extension of deterministic automata, there are several
definitions of hybrid automata in the literature. The adaptation implemented
here is formally denoted by G and defined as a 7-tuple:
*G* = { *X*, *E*, *V*, *f*, x\ :subscript:`0`, X\ :subscript:`m`, *i* }
- *X* is the set of states;
- *E* is the finite set of events;
- *V* is the finite set of environment variables;
- x\ :subscript:`0` is the initial state;
- X\ :subscript:`m` (subset of *X*) is the set of marked (or final) states.
- *f* : *X* x *E* x *C(V)* -> *X* is the transition function.
It defines the state transition in the occurrence of an event from *E* in the
state *X*. Unlike deterministic automata, the transition function also
includes guards from the set of all possible constraints (defined as *C(V)*).
Guards can be true or false with the valuation of *V* when the event occurs,
and the transition is possible only when constraints are true. Similarly to
deterministic automata, the occurrence of the event in *E* in a state in *X*
has a deterministic next state from *X*, if the guard is true.
- *i* : *X* -> *C'(V)* is the invariant assignment function, this is a
constraint assigned to each state in *X*, every state in *X* must be left
before the invariant turns to false. We can omit the representation of
invariants whose value is true regardless of the valuation of *V*.
The set of all possible constraints *C(V)* is defined according to the
following grammar:
g = v < c | v > c | v <= c | v >= c | v == c | v != c | g && g | true
With v a variable in *V* and c a numerical value.
We define the special case of hybrid automata whose variables grow with uniform
rates as timed automata. In this case, the variables are called clocks.
As the name implies, timed automata can be used to describe real time.
Additionally, clocks support another type of guard which always evaluates to true:
reset(v)
The reset constraint is used to set the value of a clock to 0.
The set of invariant constraints *C'(V)* is a subset of *C(V)* including only
constraint of the form:
g = v < c | true
This simplifies the implementation as a clock expiration is a necessary and
sufficient condition for the violation of invariants while still allowing more
complex constraints to be specified as guards.
It is important to note that any hybrid automaton is a valid deterministic
automaton with additional guards and invariants. Those can only further
constrain what transitions are valid but it is not possible to define
transition functions starting from the same state in *X* and the same event in
*E* but ending up in different states in *X* based on the valuation of *V*.
Examples
--------
Wip as hybrid automaton
~~~~~~~~~~~~~~~~~~~~~~~
The 'wip' (wakeup in preemptive) example introduced as a deterministic automaton
can also be described as:
- *X* = { ``any_thread_running`` }
- *E* = { ``sched_waking`` }
- *V* = { ``preemptive`` }
- x\ :subscript:`0` = ``any_thread_running``
- X\ :subscript:`m` = {``any_thread_running``}
- *f* =
- *f*\ (``any_thread_running``, ``sched_waking``, ``preemptive==0``) = ``any_thread_running``
- *i* =
- *i*\ (``any_thread_running``) = ``true``
Which can be represented graphically as::
|
|
v
#====================# sched_waking;preemptive==0
H H ------------------------------+
H any_thread_running H |
H H <-----------------------------+
#====================#
In this example, by using the preemptive state of the system as an environment
variable, we can assert this constraint on ``sched_waking`` without requiring
preemption events (as we would in a deterministic automaton), which can be
useful in case those events are not available or not reliable on the system.
Since all the invariants in *i* are true, we can omit them from the representation.
Stall model with guards (iteration 1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As a sample timed automaton we can define 'stall' as:
- *X* = { ``dequeued``, ``enqueued``, ``running``}
- *E* = { ``enqueue``, ``dequeue``, ``switch_in``}
- *V* = { ``clk`` }
- x\ :subscript:`0` = ``dequeue``
- X\ :subscript:`m` = {``dequeue``}
- *f* =
- *f*\ (``enqueued``, ``switch_in``, ``clk < threshold``) = ``running``
- *f*\ (``running``, ``dequeue``) = ``dequeued``
- *f*\ (``dequeued``, ``enqueue``, ``reset(clk)``) = ``enqueued``
- *i* = *omitted as all true*
Graphically represented as::
|
|
v
#============================#
H dequeued H <+
#============================# |
| |
| enqueue; reset(clk) |
v |
+----------------------------+ |
| enqueued | | dequeue
+----------------------------+ |
| |
| switch_in; clk < threshold |
v |
+----------------------------+ |
| running | -+
+----------------------------+
This model imposes that the time between when a task is enqueued (it becomes
runnable) and when the task gets to run must be lower than a certain threshold.
A failure in this model means that the task is starving.
One problem in using guards on the edges in this case is that the model will
not report a failure until the ``switch_in`` event occurs. This means that,
according to the model, it is valid for the task never to run.
Stall model with invariants (iteration 2)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The first iteration isn't exactly what was intended, we can change the model as:
- *X* = { ``dequeued``, ``enqueued``, ``running``}
- *E* = { ``enqueue``, ``dequeue``, ``switch_in``}
- *V* = { ``clk`` }
- x\ :subscript:`0` = ``dequeue``
- X\ :subscript:`m` = {``dequeue``}
- *f* =
- *f*\ (``enqueued``, ``switch_in``) = ``running``
- *f*\ (``running``, ``dequeue``) = ``dequeued``
- *f*\ (``dequeued``, ``enqueue``, ``reset(clk)``) = ``enqueued``
- *i* =
- *i*\ (``enqueued``) = ``clk < threshold``
Graphically::
|
|
v
#=========================#
H dequeued H <+
#=========================# |
| |
| enqueue; reset(clk) |
v |
+-------------------------+ |
| enqueued | |
| clk < threshold | | dequeue
+-------------------------+ |
| |
| switch_in |
v |
+-------------------------+ |
| running | -+
+-------------------------+
In this case, we moved the guard as an invariant to the ``enqueued`` state,
this means we not only forbid the occurrence of ``switch_in`` when ``clk`` is
past the threshold but also mark as invalid in case we are *still* in
``enqueued`` after the threshold. This model is effectively in an invalid state
as soon as a task is starving, rather than when the starving task finally runs.
Hybrid Automaton in C
---------------------
The definition of hybrid automata in C is heavily based on the deterministic
automata one. Specifically, we add the set of environment variables and the
constraints (both guards on transitions and invariants on states) as follows.
This is a combination of both iterations of the stall example::
/* enum representation of X (set of states) to be used as index */
enum states {
dequeued,
enqueued,
running,
state_max,
};
#define INVALID_STATE state_max
/* enum representation of E (set of events) to be used as index */
enum events {
dequeue,
enqueue,
switch_in,
event_max,
};
/* enum representation of V (set of environment variables) to be used as index */
enum envs {
clk,
env_max,
env_max_stored = env_max,
};
struct automaton {
char *state_names[state_max]; // X: the set of states
char *event_names[event_max]; // E: the finite set of events
char *env_names[env_max]; // V: the finite set of env vars
unsigned char function[state_max][event_max]; // f: transition function
unsigned char initial_state; // x_0: the initial state
bool final_states[state_max]; // X_m: the set of marked states
};
struct automaton aut = {
.state_names = {
"dequeued",
"enqueued",
"running",
},
.event_names = {
"dequeue",
"enqueue",
"switch_in",
},
.env_names = {
"clk",
},
.function = {
{ INVALID_STATE, enqueued, INVALID_STATE },
{ INVALID_STATE, INVALID_STATE, running },
{ dequeued, INVALID_STATE, INVALID_STATE },
},
.initial_state = dequeued,
.final_states = { 1, 0, 0 },
};
static bool verify_constraint(enum states curr_state, enum events event,
enum states next_state)
{
bool res = true;
/* Validate guards as part of f */
if (curr_state == enqueued && event == switch_in)
res = get_env(clk) < threshold;
else if (curr_state == dequeued && event == enqueue)
reset_env(clk);
/* Validate invariants in i */
if (next_state == curr_state || !res)
return res;
if (next_state == enqueued)
ha_start_timer_jiffy(ha_mon, clk, threshold_jiffies);
else if (curr_state == enqueued)
res = !ha_cancel_timer(ha_mon);
return res;
}
The function ``verify_constraint``, here reported as simplified, checks guards,
performs resets and starts timers to validate invariants according to
specification, those cannot easily be represented in the automaton struct.
Due to the complex nature of environment variables, the user needs to provide
functions to get and reset environment variables that are not common clocks
(e.g. clocks with ns or jiffy granularity).
Since invariants are only defined as clock expirations (e.g. *clk <
threshold*), reaching the expiration of a timer armed when entering the state
is in fact a failure in the model and triggers a reaction. Leaving the state
stops the timer.
It is important to note that timers implemented with hrtimers introduce
overhead, if the monitor has several instances (e.g. all tasks) this can become
an issue. The impact can be decreased using the timer wheel (``HA_TIMER_TYPE``
set to ``HA_TIMER_WHEEL``), this lowers the responsiveness of the timer without
damaging the accuracy of the model, since the invariant condition is checked
before disabling the timer in case the callback is late.
Alternatively, if the monitor is guaranteed to *eventually* leave the state and
the incurred delay to wait for the next event is acceptable, guards can be used
in place of invariants, as seen in the stall example.
Graphviz .dot format
--------------------
Also the Graphviz representation of hybrid automata is an extension of the
deterministic automata one. Specifically, guards can be provided in the event
name separated by ``;``::
"state_start" -> "state_dest" [ label = "sched_waking;preemptible==0;reset(clk)" ];
Invariant can be specified in the state label (not the node name!) separated by ``\n``::
"enqueued" [label = "enqueued\nclk < threshold_jiffies"];
Constraints can be specified as valid C comparisons and allow spaces, the first
element of the comparison must be the clock while the second is a numerical or
parametrised value. Guards allow comparisons to be combined with boolean
operations (``&&`` and ``||``), resets must be separated from other constraints.
This is the full example of the last version of the 'stall' model in DOT::
digraph state_automaton {
{node [shape = circle] "enqueued"};
{node [shape = plaintext, style=invis, label=""] "__init_dequeued"};
{node [shape = doublecircle] "dequeued"};
{node [shape = circle] "running"};
"__init_dequeued" -> "dequeued";
"enqueued" [label = "enqueued\nclk < threshold_jiffies"];
"running" [label = "running"];
"dequeued" [label = "dequeued"];
"enqueued" -> "running" [ label = "switch_in" ];
"running" -> "dequeued" [ label = "dequeue" ];
"dequeued" -> "enqueued" [ label = "enqueue;reset(clk)" ];
{ rank = min ;
"__init_dequeued";
"dequeued";
}
}
References
----------
One book covering model checking and timed automata is::
Christel Baier and Joost-Pieter Katoen: Principles of Model Checking,
The MIT Press, 2008.
Hybrid automata are described in detail in::
Thomas Henzinger: The theory of hybrid automata,
Proceedings 11th Annual IEEE Symposium on Logic in Computer Science, 1996.
+3
View File
@@ -9,9 +9,12 @@ Runtime Verification
runtime-verification.rst
deterministic_automata.rst
linear_temporal_logic.rst
hybrid_automata.rst
monitor_synthesis.rst
da_monitor_instrumentation.rst
monitor_wip.rst
monitor_wwnr.rst
monitor_sched.rst
monitor_rtapp.rst
monitor_stall.rst
monitor_deadline.rst
@@ -0,0 +1,84 @@
Deadline monitors
=================
- Name: deadline
- Type: container for multiple monitors
- Author: Gabriele Monaco <gmonaco@redhat.com>
Description
-----------
The deadline monitor is a set of specifications to describe the deadline
scheduler behaviour. It includes monitors per scheduling entity (deadline tasks
and servers) that work independently to verify different specifications the
deadline scheduler should follow.
Specifications
--------------
Monitor nomiss
~~~~~~~~~~~~~~
The nomiss monitor ensures dl entities get to run *and* run to completion
before their deadline, although deferrable servers may not run. An entity is
considered done if ``throttled``, either because it yielded or used up its
runtime, or when it voluntarily starts ``sleeping``.
The monitor includes a user configurable deadline threshold. If the total
utilisation of deadline tasks is larger than 1, they are only guaranteed
bounded tardiness. See Documentation/scheduler/sched-deadline.rst for more
details. The threshold (module parameter ``nomiss.deadline_thresh``) can be
configured to avoid the monitor to fail based on the acceptable tardiness in
the system. Since ``dl_throttle`` is a valid outcome for the entity to be done,
the minimum tardiness needs be 1 tick to consider the throttle delay, unless
the ``HRTICK_DL`` scheduler feature is active.
Servers have also an intermediate ``idle`` state, occurring as soon as no
runnable task is available from ready or running where no timing constraint
is applied. A server goes to sleep by stopping, there is no wakeup equivalent
as the order of a server starting and replenishing is not defined, hence a
server can run from sleeping without being ready::
|
sched_wakeup v
dl_replenish;reset(clk) -- #=========================#
| H H dl_replenish;reset(clk)
+-----------> H H <--------------------+
H H |
+- dl_server_stop ---- H ready H |
| +-----------------> H clk < DEADLINE_NS() H dl_throttle; |
| | H H is_defer == 1 |
| | sched_switch_in - H H -----------------+ |
| | | #=========================# | |
| | | | ^ | |
| | | dl_server_idle dl_replenish;reset(clk) | |
| | | v | | |
| | | +--------------+ | |
| | | +------ | | | |
| | | dl_server_idle | | dl_throttle | |
| | | | | idle | -----------------+ | |
| | | +-----> | | | | |
| | | | | | | |
| | | | | | | |
+--+--+---+--- dl_server_stop -- +--------------+ | | |
| | | | | ^ | | |
| | | | sched_switch_in dl_server_idle | | |
| | | | v | | | |
| | | | +---------- +---------------------+ | | |
| | | | sched_switch_in | | | | |
| | | | sched_wakeup | | | | |
| | | | dl_replenish; | running | -------+ | | |
| | | | reset(clk) | clk < DEADLINE_NS() | | | | |
| | | | +---------> | | dl_throttle | | |
| | | +----------------> | | | | | |
| | | +---------------------+ | | | |
| | sched_wakeup ^ sched_switch_suspend | | | |
v v dl_replenish;reset(clk) | dl_server_stop | | | |
+--------------+ | | v v v |
| | - sched_switch_in + | +---------------+
| | <---------------------+ dl_throttle +-- | |
| sleeping | sched_wakeup | | throttled |
| | -- dl_server_stop dl_server_idle +-> | |
| | dl_server_idle sched_switch_suspend +---------------+
+--------------+ <---------+ ^
| |
+------ dl_throttle;is_constr_dl == 1 || is_defer == 1 ------+
+13 -47
View File
@@ -346,55 +346,21 @@ Monitor opid
The operations with preemption and irq disabled (opid) monitor ensures
operations like ``wakeup`` and ``need_resched`` occur with interrupts and
preemption disabled or during interrupt context, in such case preemption may
not be disabled explicitly.
preemption disabled.
``need_resched`` can be set by some RCU internals functions, in which case it
doesn't match a task wakeup and might occur with only interrupts disabled::
doesn't match a task wakeup and might occur with only interrupts disabled.
The interrupt and preemption status are validated by the hybrid automaton
constraints when processing the events::
| sched_need_resched
| sched_waking
| irq_entry
| +--------------------+
v v |
+------------------------------------------------------+
+----------- | disabled | <+
| +------------------------------------------------------+ |
| | ^ |
| | preempt_disable sched_need_resched |
| preempt_enable | +--------------------+ |
| v | v | |
| +------------------------------------------------------+ |
| | irq_disabled | |
| +------------------------------------------------------+ |
| | | ^ |
| irq_entry irq_entry | | |
| sched_need_resched v | irq_disable |
| sched_waking +--------------+ | | |
| +----- | | irq_enable | |
| | | in_irq | | | |
| +----> | | | | |
| +--------------+ | | irq_disable
| | | | |
| irq_enable | irq_enable | | |
| v v | |
| #======================================================# |
| H enabled H |
| #======================================================# |
| | ^ ^ preempt_enable | |
| preempt_disable preempt_enable +--------------------+ |
| v | |
| +------------------+ | |
+----------> | preempt_disabled | -+ |
+------------------+ |
| |
+-------------------------------------------------------+
This monitor is designed to work on ``PREEMPT_RT`` kernels, the special case of
events occurring in interrupt context is a shortcut to identify valid scenarios
where the preemption tracepoints might not be visible, during interrupts
preemption is always disabled. On non- ``PREEMPT_RT`` kernels, the interrupts
might invoke a softirq to set ``need_resched`` and wake up a task. This is
another special case that is currently not supported by the monitor.
|
|
v
#=========# sched_need_resched;irq_off == 1
H H sched_waking;irq_off == 1 && preempt_off == 1
H any H ------------------------------------------------+
H H |
H H <-----------------------------------------------+
#=========#
References
----------
+43
View File
@@ -0,0 +1,43 @@
Monitor stall
=============
- Name: stall - stalled task monitor
- Type: per-task hybrid automaton
- Author: Gabriele Monaco <gmonaco@redhat.com>
Description
-----------
The stalled task (stall) monitor is a sample per-task timed monitor that checks
if tasks are scheduled within a defined threshold after they are ready::
|
|
v
#==========================#
+-----------------> H dequeued H
| #==========================#
| |
sched_switch_wait | sched_wakeup;reset(clk)
| v
| +--------------------------+ <+
| | enqueued | | sched_wakeup
| | clk < threshold_jiffies | -+
| +--------------------------+
| | ^
| sched_switch_in sched_switch_preempt;reset(clk)
| v |
| +--------------------------+
+------------------ | running |
+--------------------------+
^ sched_switch_in |
| sched_wakeup |
+----------------------+
The threshold can be configured as a parameter by either booting with the
``stall.threshold_jiffies=<new value>`` argument or writing a new value to
``/sys/module/stall/parameters/threshold_jiffies``.
Specification
-------------
Graphviz Dot file in tools/verification/models/stall.dot
+115 -2
View File
@@ -18,8 +18,8 @@ functions that glue the monitor to the system reference model, and the
trace output as a reaction to event parsing and exceptions, as depicted
below::
Linux +----- RV Monitor ----------------------------------+ Formal
Realm | | Realm
Linux +---- RV Monitor ----------------------------------+ Formal
Realm | | Realm
+-------------------+ +----------------+ +-----------------+
| Linux kernel | | Monitor | | Reference |
| Tracing | -> | Instance(s) | <- | Model |
@@ -45,6 +45,7 @@ creating monitors. The header files are:
* rv/da_monitor.h for deterministic automaton monitor.
* rv/ltl_monitor.h for linear temporal logic monitor.
* rv/ha_monitor.h for hybrid automaton monitor.
rvgen
-----
@@ -252,6 +253,118 @@ the task, the monitor may need some time to start validating tasks which have
been running before the monitor is enabled. Therefore, it is recommended to
start the tasks of interest after enabling the monitor.
rv/ha_monitor.h
+++++++++++++++
The implementation of hybrid automaton monitors derives directly from the
deterministic automaton one. Despite using a different header
(``ha_monitor.h``) the functions to handle events are the same (e.g.
``da_handle_event``).
Additionally, the `rvgen` tool populates skeletons for the
``ha_verify_constraint``, ``ha_get_env`` and ``ha_reset_env`` based on the
monitor specification in the monitor source file.
``ha_verify_constraint`` is typically ready as it is generated by `rvgen`:
* standard constraints on edges are turned into the form::
res = ha_get_env(ha_mon, ENV) < VALUE;
* reset constraints are turned into the form::
ha_reset_env(ha_mon, ENV);
* constraints on the state are implemented using timers
- armed before entering the state
- cancelled while entering any other state
- untouched if the state does not change as a result of the event
- checked if the timer expired but the callback did not run
- available implementation are `HA_TIMER_HRTIMER` and `HA_TIMER_WHEEL`
- hrtimers are more precise but may have higher overhead
- select by defining `HA_TIMER_TYPE` before including the header::
#define HA_TIMER_TYPE HA_TIMER_HRTIMER
Constraint values can be specified in different forms:
* literal value (with optional unit). E.g.::
preemptive == 0
clk < 100ns
threshold <= 10j
* constant value (uppercase string). E.g.::
clk < MAX_NS
* parameter (lowercase string). E.g.::
clk <= threshold_jiffies
* macro (uppercase string with parentheses). E.g.::
clk < MAX_NS()
* function (lowercase string with parentheses). E.g.::
clk <= threshold_jiffies()
In all cases, `rvgen` will try to understand the type of the environment
variable from the name or unit. For instance, constants or parameters
terminating with ``_NS`` or ``_jiffies`` are intended as clocks with ns and jiffy
granularity, respectively. Literals with measure unit `j` are jiffies and if a
time unit is specified (`ns` to `s`), `rvgen` will convert the value to `ns`.
Constants need to be defined by the user (but unlike the name, they don't
necessarily need to be defined as constants). Parameters get converted to
module parameters and the user needs to provide a default value.
Also function and macros are defined by the user, by default they get as an
argument the ``ha_monitor``, a common usage would be to get the required value
from the target, e.g. the task in per-task monitors, using the helper
``ha_get_target(ha_mon)``.
If `rvgen` determines that the variable is a clock, it provides the getter and
resetter based on the unit. Otherwise, the user needs to provide an appropriate
definition.
Typically non-clock environment variables are not reset. In such case only the
getter skeleton will be present in the file generated by `rvgen`.
For instance, the getter for preemptive can be filled as::
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env)
{
if (env == preemptible)
return preempt_count() == 0;
return ENV_INVALID_VALUE;
}
The function is supplied the ``ha_mon`` parameter in case some storage is
required (as it is for clocks), but environment variables without reset do not
require a storage and can ignore that argument.
The number of environment variables requiring a storage is limited by
``MAX_HA_ENV_LEN``, however such limitation doesn't stand for other variables.
Finally, constraints on states are only valid for clocks and only if the
constraint is of the form `clk < N`. This is because such constraints are
implemented with the expiration of a timer.
Typically the clock variables are reset just before arming the timer, but this
doesn't have to be the case and the available functions take care of it.
It is a responsibility of per-task monitors to make sure no timer is left
running when the task exits.
By default the generator implements timers with hrtimers (setting
``HA_TIMER_TYPE`` to ``HA_TIMER_HRTIMER``), this gives better responsiveness
but higher overhead. The timer wheel (``HA_TIMER_WHEEL``) is a good alternative
for monitors with several instances (e.g. per-task) that achieves lower
overhead with increased latency, yet without compromising precision.
Final remarks
-------------
+39
View File
@@ -13,6 +13,7 @@
#define RV_MON_GLOBAL 0
#define RV_MON_PER_CPU 1
#define RV_MON_PER_TASK 2
#define RV_MON_PER_OBJ 3
#ifdef CONFIG_RV
#include <linux/array_size.h>
@@ -81,11 +82,49 @@ struct ltl_monitor {};
#endif /* CONFIG_RV_LTL_MONITOR */
#ifdef CONFIG_RV_HA_MONITOR
/*
* In the future, hybrid automata may rely on multiple
* environment variables, e.g. different clocks started at
* different times or running at different speed.
* For now we support only 1 variable.
*/
#define MAX_HA_ENV_LEN 1
/*
* Monitors can pick the preferred timer implementation:
* No timer: if monitors don't have state invariants.
* Timer wheel: lightweight invariants check but far less precise.
* Hrtimer: accurate invariants check with higher overhead.
*/
#define HA_TIMER_NONE 0
#define HA_TIMER_WHEEL 1
#define HA_TIMER_HRTIMER 2
/*
* Hybrid automaton per-object variables.
*/
struct ha_monitor {
struct da_monitor da_mon;
u64 env_store[MAX_HA_ENV_LEN];
union {
struct hrtimer hrtimer;
struct timer_list timer;
};
};
#else
struct ha_monitor { };
#endif /* CONFIG_RV_HA_MONITOR */
#define RV_PER_TASK_MONITOR_INIT (CONFIG_RV_PER_TASK_MONITORS)
union rv_task_monitor {
struct da_monitor da_mon;
struct ltl_monitor ltl_mon;
struct ha_monitor ha_mon;
};
#ifdef CONFIG_RV_REACTORS
+27
View File
@@ -37,4 +37,31 @@ extern void dl_clear_root_domain_cpu(int cpu);
extern u64 dl_cookie;
extern bool dl_bw_visited(int cpu, u64 cookie);
static inline bool dl_server(struct sched_dl_entity *dl_se)
{
return dl_se->dl_server;
}
static inline struct task_struct *dl_task_of(struct sched_dl_entity *dl_se)
{
BUG_ON(dl_server(dl_se));
return container_of(dl_se, struct task_struct, dl);
}
/*
* Regarding the deadline, a task with implicit deadline has a relative
* deadline == relative period. A task with constrained deadline has a
* relative deadline <= relative period.
*
* We support constrained deadline tasks. However, there are some restrictions
* applied only for tasks which do not have an implicit deadline. See
* update_dl_entity() to know more about such restrictions.
*
* The dl_is_implicit() returns true if the task has an implicit deadline.
*/
static inline bool dl_is_implicit(struct sched_dl_entity *dl_se)
{
return dl_se->dl_deadline == dl_se->dl_period;
}
#endif /* _LINUX_SCHED_DEADLINE_H */
+489 -173
View File
File diff suppressed because it is too large Load Diff
+478
View File
@@ -0,0 +1,478 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2025-2028 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
*
* Hybrid automata (HA) monitor functions, to be used together
* with automata models in C generated by the rvgen tool.
*
* This type of monitors extends the Deterministic automata (DA) class by
* adding a set of environment variables (e.g. clocks) that can be used to
* constraint the valid transitions.
*
* The rvgen tool is available at tools/verification/rvgen/
*
* For further information, see:
* Documentation/trace/rv/monitor_synthesis.rst
*/
#ifndef _RV_HA_MONITOR_H
#define _RV_HA_MONITOR_H
#include <rv/automata.h>
#ifndef da_id_type
#define da_id_type int
#endif
static inline void ha_monitor_init_env(struct da_monitor *da_mon);
static inline void ha_monitor_reset_env(struct da_monitor *da_mon);
static inline void ha_setup_timer(struct ha_monitor *ha_mon);
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon);
static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
enum states curr_state,
enum events event,
enum states next_state,
da_id_type id);
#define da_monitor_event_hook ha_monitor_handle_constraint
#define da_monitor_init_hook ha_monitor_init_env
#define da_monitor_reset_hook ha_monitor_reset_env
#include <rv/da_monitor.h>
#include <linux/seq_buf.h>
/* This simplifies things since da_mon and ha_mon coexist in the same union */
_Static_assert(offsetof(struct ha_monitor, da_mon) == 0,
"da_mon must be the first element in an ha_mon!");
#define to_ha_monitor(da) container_of(da, struct ha_monitor, da_mon)
#define ENV_MAX CONCATENATE(env_max_, MONITOR_NAME)
#define ENV_MAX_STORED CONCATENATE(env_max_stored_, MONITOR_NAME)
#define envs CONCATENATE(envs_, MONITOR_NAME)
/* Environment storage before being reset */
#define ENV_INVALID_VALUE U64_MAX
/* Error with no event occurs only on timeouts */
#define EVENT_NONE EVENT_MAX
#define EVENT_NONE_LBL "none"
#define ENV_BUFFER_SIZE 64
#ifdef CONFIG_RV_REACTORS
/*
* ha_react - trigger the reaction after a failed environment constraint
*
* The transition from curr_state with event is otherwise valid, but the
* environment constraint is false. This function can be called also with no
* event from a timer (state constraints only).
*/
static void ha_react(enum states curr_state, enum events event, char *env)
{
rv_react(&rv_this,
"rv: monitor %s does not allow event %s on state %s with env %s\n",
__stringify(MONITOR_NAME),
event == EVENT_NONE ? EVENT_NONE_LBL : model_get_event_name(event),
model_get_state_name(curr_state), env);
}
#else /* CONFIG_RV_REACTOR */
static void ha_react(enum states curr_state, enum events event, char *env) { }
#endif
/*
* model_get_state_name - return the (string) name of the given state
*/
static char *model_get_env_name(enum envs env)
{
if ((env < 0) || (env >= ENV_MAX))
return "INVALID";
return RV_AUTOMATON_NAME.env_names[env];
}
/*
* Monitors requiring a timer implementation need to request it explicitly.
*/
#ifndef HA_TIMER_TYPE
#define HA_TIMER_TYPE HA_TIMER_NONE
#endif
#if HA_TIMER_TYPE == HA_TIMER_WHEEL
static void ha_monitor_timer_callback(struct timer_list *timer);
#elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer);
#endif
/*
* ktime_get_ns is expensive, since we usually don't require precise accounting
* of changes within the same event, cache the current time at the beginning of
* the constraint handler and use the cache for subsequent calls.
* Monitors without ns clocks automatically skip this.
*/
#ifdef HA_CLK_NS
#define ha_get_ns() ktime_get_ns()
#else
#define ha_get_ns() 0
#endif /* HA_CLK_NS */
/* Should be supplied by the monitor */
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env, u64 time_ns);
static bool ha_verify_constraint(struct ha_monitor *ha_mon,
enum states curr_state,
enum events event,
enum states next_state,
u64 time_ns);
/*
* ha_monitor_reset_all_stored - reset all environment variables in the monitor
*/
static inline void ha_monitor_reset_all_stored(struct ha_monitor *ha_mon)
{
for (int i = 0; i < ENV_MAX_STORED; i++)
WRITE_ONCE(ha_mon->env_store[i], ENV_INVALID_VALUE);
}
/*
* ha_monitor_init_env - setup timer and reset all environment
*
* Called from a hook in the DA start functions, it supplies the da_mon
* corresponding to the current ha_mon.
* Not all hybrid automata require the timer, still set it for simplicity.
*/
static inline void ha_monitor_init_env(struct da_monitor *da_mon)
{
struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
ha_monitor_reset_all_stored(ha_mon);
ha_setup_timer(ha_mon);
}
/*
* ha_monitor_reset_env - stop timer and reset all environment
*
* Called from a hook in the DA reset functions, it supplies the da_mon
* corresponding to the current ha_mon.
* Not all hybrid automata require the timer, still clear it for simplicity.
*/
static inline void ha_monitor_reset_env(struct da_monitor *da_mon)
{
struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
/* Initialisation resets the monitor before initialising the timer */
if (likely(da_monitoring(da_mon)))
ha_cancel_timer(ha_mon);
}
/*
* ha_monitor_env_invalid - return true if env has not been initialised
*/
static inline bool ha_monitor_env_invalid(struct ha_monitor *ha_mon, enum envs env)
{
return READ_ONCE(ha_mon->env_store[env]) == ENV_INVALID_VALUE;
}
static inline void ha_get_env_string(struct seq_buf *s,
struct ha_monitor *ha_mon, u64 time_ns)
{
const char *format_str = "%s=%llu";
for (int i = 0; i < ENV_MAX; i++) {
seq_buf_printf(s, format_str, model_get_env_name(i),
ha_get_env(ha_mon, i, time_ns));
format_str = ",%s=%llu";
}
}
#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
char *curr_state, char *event, char *env,
da_id_type id)
{
CONCATENATE(trace_error_env_, MONITOR_NAME)(curr_state, event, env);
}
#elif RV_MON_TYPE == RV_MON_PER_TASK || RV_MON_TYPE == RV_MON_PER_OBJ
#define ha_get_target(ha_mon) da_get_target(&ha_mon->da_mon)
static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
char *curr_state, char *event, char *env,
da_id_type id)
{
CONCATENATE(trace_error_env_, MONITOR_NAME)(id, curr_state, event, env);
}
#endif /* RV_MON_TYPE */
/*
* ha_get_monitor - return the current monitor
*/
#define ha_get_monitor(...) to_ha_monitor(da_get_monitor(__VA_ARGS__))
/*
* ha_monitor_handle_constraint - handle the constraint on the current transition
*
* If the monitor implementation defines a constraint in the transition from
* curr_state to event, react and trace appropriately as well as return false.
* This function is called from the hook in the DA event handle function and
* triggers a failure in the monitor.
*/
static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
enum states curr_state,
enum events event,
enum states next_state,
da_id_type id)
{
struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
u64 time_ns = ha_get_ns();
DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
if (ha_verify_constraint(ha_mon, curr_state, event, next_state, time_ns))
return true;
ha_get_env_string(&env_string, ha_mon, time_ns);
ha_react(curr_state, event, env_string.buffer);
ha_trace_error_env(ha_mon,
model_get_state_name(curr_state),
model_get_event_name(event),
env_string.buffer, id);
return false;
}
static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon)
{
enum states curr_state = READ_ONCE(ha_mon->da_mon.curr_state);
DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
u64 time_ns = ha_get_ns();
ha_get_env_string(&env_string, ha_mon, time_ns);
ha_react(curr_state, EVENT_NONE, env_string.buffer);
ha_trace_error_env(ha_mon, model_get_state_name(curr_state),
EVENT_NONE_LBL, env_string.buffer,
da_get_id(&ha_mon->da_mon));
da_monitor_reset(&ha_mon->da_mon);
}
/*
* The clock variables have 2 different representations in the env_store:
* - The guard representation is the timestamp of the last reset
* - The invariant representation is the timestamp when the invariant expires
* As the representations are incompatible, care must be taken when switching
* between them: the invariant representation can only be used when starting a
* timer when the previous representation was guard (e.g. no other invariant
* started since the last reset operation).
* Likewise, switching from invariant to guard representation without a reset
* can be done only by subtracting the exact value used to start the invariant.
*
* Reading the environment variable (ha_get_clk) also reflects this difference
* any reads in states that have an invariant return the (possibly negative)
* time since expiration, other reads return the time since last reset.
*/
/*
* Helper functions for env variables describing clocks with ns granularity
*/
static inline u64 ha_get_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
{
return time_ns - READ_ONCE(ha_mon->env_store[env]);
}
static inline void ha_reset_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
{
WRITE_ONCE(ha_mon->env_store[env], time_ns);
}
static inline void ha_set_invariant_ns(struct ha_monitor *ha_mon, enum envs env,
u64 value, u64 time_ns)
{
WRITE_ONCE(ha_mon->env_store[env], time_ns + value);
}
static inline bool ha_check_invariant_ns(struct ha_monitor *ha_mon,
enum envs env, u64 time_ns)
{
return READ_ONCE(ha_mon->env_store[env]) >= time_ns;
}
/*
* ha_invariant_passed_ns - prepare the invariant and return the time since reset
*/
static inline u64 ha_invariant_passed_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = 0;
if (env < 0 || env >= ENV_MAX_STORED)
return 0;
if (ha_monitor_env_invalid(ha_mon, env))
return 0;
passed = ha_get_env(ha_mon, env, time_ns);
ha_set_invariant_ns(ha_mon, env, expire - passed, time_ns);
return passed;
}
/*
* Helper functions for env variables describing clocks with jiffy granularity
*/
static inline u64 ha_get_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
{
return get_jiffies_64() - READ_ONCE(ha_mon->env_store[env]);
}
static inline void ha_reset_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
{
WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64());
}
static inline void ha_set_invariant_jiffy(struct ha_monitor *ha_mon,
enum envs env, u64 value)
{
WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64() + value);
}
static inline bool ha_check_invariant_jiffy(struct ha_monitor *ha_mon,
enum envs env, u64 time_ns)
{
return time_after64(READ_ONCE(ha_mon->env_store[env]), get_jiffies_64());
}
/*
* ha_invariant_passed_jiffy - prepare the invariant and return the time since reset
*/
static inline u64 ha_invariant_passed_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = 0;
if (env < 0 || env >= ENV_MAX_STORED)
return 0;
if (ha_monitor_env_invalid(ha_mon, env))
return 0;
passed = ha_get_env(ha_mon, env, time_ns);
ha_set_invariant_jiffy(ha_mon, env, expire - passed);
return passed;
}
/*
* Retrieve the last reset time (guard representation) from the invariant
* representation (expiration).
* It the caller's responsibility to make sure the storage was actually in the
* invariant representation (e.g. the current state has an invariant).
* The provided value must be the same used when starting the invariant.
*
* This function's access to the storage is NOT atomic, due to the rarity when
* this is used. If a monitor allows writes concurrent to this, likely
* other things are broken and need rethinking the model or additional locking.
*/
static inline void ha_inv_to_guard(struct ha_monitor *ha_mon, enum envs env,
u64 value, u64 time_ns)
{
WRITE_ONCE(ha_mon->env_store[env], READ_ONCE(ha_mon->env_store[env]) - value);
}
#if HA_TIMER_TYPE == HA_TIMER_WHEEL
/*
* Helper functions to handle the monitor timer.
* Not all monitors require a timer, in such case the timer will be set up but
* never armed.
* Timers start since the last reset of the supplied env or from now if env is
* not an environment variable. If env was not initialised no timer starts.
* Timers can expire on any CPU unless the monitor is per-cpu,
* where we assume every event occurs on the local CPU.
*/
static void ha_monitor_timer_callback(struct timer_list *timer)
{
struct ha_monitor *ha_mon = container_of(timer, struct ha_monitor, timer);
__ha_monitor_timer_callback(ha_mon);
}
static inline void ha_setup_timer(struct ha_monitor *ha_mon)
{
int mode = 0;
if (RV_MON_TYPE == RV_MON_PER_CPU)
mode |= TIMER_PINNED;
timer_setup(&ha_mon->timer, ha_monitor_timer_callback, mode);
}
static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
mod_timer(&ha_mon->timer, get_jiffies_64() + expire - passed);
}
static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
ha_start_timer_jiffy(ha_mon, ENV_MAX_STORED,
nsecs_to_jiffies(expire - passed + TICK_NSEC - 1), time_ns);
}
/*
* ha_cancel_timer - Cancel the timer
*
* Returns:
* * 1 when the timer was active
* * 0 when the timer was not active or running a callback
*/
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
{
return timer_delete(&ha_mon->timer);
}
#elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
/*
* Helper functions to handle the monitor timer.
* Not all monitors require a timer, in such case the timer will be set up but
* never armed.
* Timers start since the last reset of the supplied env or from now if env is
* not an environment variable. If env was not initialised no timer starts.
* Timers can expire on any CPU unless the monitor is per-cpu,
* where we assume every event occurs on the local CPU.
*/
static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer)
{
struct ha_monitor *ha_mon = container_of(hrtimer, struct ha_monitor, hrtimer);
__ha_monitor_timer_callback(ha_mon);
return HRTIMER_NORESTART;
}
static inline void ha_setup_timer(struct ha_monitor *ha_mon)
{
hrtimer_setup(&ha_mon->hrtimer, ha_monitor_timer_callback,
CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
}
static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
int mode = HRTIMER_MODE_REL_HARD;
u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
if (RV_MON_TYPE == RV_MON_PER_CPU)
mode |= HRTIMER_MODE_PINNED;
hrtimer_start(&ha_mon->hrtimer, ns_to_ktime(expire - passed), mode);
}
static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
ha_start_timer_ns(ha_mon, ENV_MAX_STORED,
jiffies_to_nsecs(expire - passed), time_ns);
}
/*
* ha_cancel_timer - Cancel the timer
*
* Returns:
* * 1 when the timer was active
* * 0 when the timer was not active or running a callback
*/
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
{
return hrtimer_try_to_cancel(&ha_mon->hrtimer) == 1;
}
#else /* HA_TIMER_NONE */
/*
* Start function is intentionally not defined, monitors using timers must
* set HA_TIMER_TYPE to either HA_TIMER_WHEEL or HA_TIMER_HRTIMER.
*/
static inline void ha_setup_timer(struct ha_monitor *ha_mon) { }
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
{
return false;
}
#endif
#endif
+26
View File
@@ -896,6 +896,32 @@ DECLARE_TRACE(sched_set_need_resched,
TP_PROTO(struct task_struct *tsk, int cpu, int tif),
TP_ARGS(tsk, cpu, tif));
#define DL_OTHER 0
#define DL_TASK 1
#define DL_SERVER_FAIR 2
#define DL_SERVER_EXT 3
DECLARE_TRACE(sched_dl_throttle,
TP_PROTO(struct sched_dl_entity *dl_se, int cpu, u8 type),
TP_ARGS(dl_se, cpu, type));
DECLARE_TRACE(sched_dl_replenish,
TP_PROTO(struct sched_dl_entity *dl_se, int cpu, u8 type),
TP_ARGS(dl_se, cpu, type));
/* Call to update_curr_dl_se not involving throttle or replenish */
DECLARE_TRACE(sched_dl_update,
TP_PROTO(struct sched_dl_entity *dl_se, int cpu, u8 type),
TP_ARGS(dl_se, cpu, type));
DECLARE_TRACE(sched_dl_server_start,
TP_PROTO(struct sched_dl_entity *dl_se, int cpu, u8 type),
TP_ARGS(dl_se, cpu, type));
DECLARE_TRACE(sched_dl_server_stop,
TP_PROTO(struct sched_dl_entity *dl_se, int cpu, u8 type),
TP_ARGS(dl_se, cpu, type));
#endif /* _TRACE_SCHED_H */
/* This part must be outside protection */
+5
View File
@@ -122,6 +122,11 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(sched_compute_energy_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_entry_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_exit_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_set_need_resched_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_dl_throttle_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_dl_replenish_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_dl_update_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_dl_server_start_tp);
EXPORT_TRACEPOINT_SYMBOL_GPL(sched_dl_server_stop_tp);
DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
DEFINE_PER_CPU(struct rnd_state, sched_rnd_state);
+24 -27
View File
@@ -18,6 +18,7 @@
#include <linux/cpuset.h>
#include <linux/sched/clock.h>
#include <linux/sched/deadline.h>
#include <uapi/linux/sched/types.h>
#include "sched.h"
#include "pelt.h"
@@ -57,17 +58,6 @@ static int __init sched_dl_sysctl_init(void)
late_initcall(sched_dl_sysctl_init);
#endif /* CONFIG_SYSCTL */
static bool dl_server(struct sched_dl_entity *dl_se)
{
return dl_se->dl_server;
}
static inline struct task_struct *dl_task_of(struct sched_dl_entity *dl_se)
{
BUG_ON(dl_server(dl_se));
return container_of(dl_se, struct task_struct, dl);
}
static inline struct rq *rq_of_dl_rq(struct dl_rq *dl_rq)
{
return container_of(dl_rq, struct rq, dl);
@@ -115,6 +105,19 @@ static inline bool is_dl_boosted(struct sched_dl_entity *dl_se)
}
#endif /* !CONFIG_RT_MUTEXES */
static inline u8 dl_get_type(struct sched_dl_entity *dl_se, struct rq *rq)
{
if (!dl_server(dl_se))
return DL_TASK;
if (dl_se == &rq->fair_server)
return DL_SERVER_FAIR;
#ifdef CONFIG_SCHED_CLASS_EXT
if (dl_se == &rq->ext_server)
return DL_SERVER_EXT;
#endif
return DL_OTHER;
}
static inline struct dl_bw *dl_bw_of(int i)
{
RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held(),
@@ -733,6 +736,7 @@ static inline void replenish_dl_new_period(struct sched_dl_entity *dl_se,
dl_se->dl_throttled = 1;
dl_se->dl_defer_armed = 1;
}
trace_sched_dl_replenish_tp(dl_se, cpu_of(rq), dl_get_type(dl_se, rq));
}
/*
@@ -848,6 +852,8 @@ static void replenish_dl_entity(struct sched_dl_entity *dl_se)
if (dl_se->dl_throttled)
dl_se->dl_throttled = 0;
trace_sched_dl_replenish_tp(dl_se, cpu_of(rq), dl_get_type(dl_se, rq));
/*
* If this is the replenishment of a deferred reservation,
* clear the flag and return.
@@ -974,22 +980,6 @@ update_dl_revised_wakeup(struct sched_dl_entity *dl_se, struct rq *rq)
dl_se->runtime = (dl_se->dl_density * laxity) >> BW_SHIFT;
}
/*
* Regarding the deadline, a task with implicit deadline has a relative
* deadline == relative period. A task with constrained deadline has a
* relative deadline <= relative period.
*
* We support constrained deadline tasks. However, there are some restrictions
* applied only for tasks which do not have an implicit deadline. See
* update_dl_entity() to know more about such restrictions.
*
* The dl_is_implicit() returns true if the task has an implicit deadline.
*/
static inline bool dl_is_implicit(struct sched_dl_entity *dl_se)
{
return dl_se->dl_deadline == dl_se->dl_period;
}
/*
* When a deadline entity is placed in the runqueue, its runtime and deadline
* might need to be updated. This is done by a CBS wake up rule. There are two
@@ -1345,6 +1335,7 @@ static inline void dl_check_constrained_dl(struct sched_dl_entity *dl_se)
dl_time_before(rq_clock(rq), dl_next_period(dl_se))) {
if (unlikely(is_dl_boosted(dl_se) || !start_dl_timer(dl_se)))
return;
trace_sched_dl_throttle_tp(dl_se, cpu_of(rq), dl_get_type(dl_se, rq));
dl_se->dl_throttled = 1;
if (dl_se->runtime > 0)
dl_se->runtime = 0;
@@ -1508,6 +1499,7 @@ static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64
throttle:
if (dl_runtime_exceeded(dl_se) || dl_se->dl_yielded) {
trace_sched_dl_throttle_tp(dl_se, cpu_of(rq), dl_get_type(dl_se, rq));
dl_se->dl_throttled = 1;
/* If requested, inform the user about runtime overruns. */
@@ -1532,6 +1524,8 @@ throttle:
if (!is_leftmost(dl_se, &rq->dl))
resched_curr(rq);
} else {
trace_sched_dl_update_tp(dl_se, cpu_of(rq), dl_get_type(dl_se, rq));
}
/*
@@ -1810,6 +1804,7 @@ void dl_server_start(struct sched_dl_entity *dl_se)
if (WARN_ON_ONCE(!cpu_online(cpu_of(rq))))
return;
trace_sched_dl_server_start_tp(dl_se, cpu_of(rq), dl_get_type(dl_se, rq));
dl_se->dl_server_active = 1;
enqueue_dl_entity(dl_se, ENQUEUE_WAKEUP);
if (!dl_task(dl_se->rq->curr) || dl_entity_preempt(dl_se, &rq->curr->dl))
@@ -1821,6 +1816,8 @@ void dl_server_stop(struct sched_dl_entity *dl_se)
if (!dl_server(dl_se) || !dl_server_active(dl_se))
return;
trace_sched_dl_server_stop_tp(dl_se, cpu_of(dl_se->rq),
dl_get_type(dl_se, dl_se->rq));
dequeue_dl_entity(dl_se, DEQUEUE_SLEEP);
hrtimer_try_to_cancel(&dl_se->dl_timer);
dl_se->dl_defer_armed = 0;
+18
View File
@@ -23,6 +23,19 @@ config LTL_MON_EVENTS_ID
config RV_LTL_MONITOR
bool
config RV_HA_MONITOR
bool
config HA_MON_EVENTS_IMPLICIT
select DA_MON_EVENTS_IMPLICIT
select RV_HA_MONITOR
bool
config HA_MON_EVENTS_ID
select DA_MON_EVENTS_ID
select RV_HA_MONITOR
bool
menuconfig RV
bool "Runtime Verification"
select TRACING
@@ -65,6 +78,11 @@ source "kernel/trace/rv/monitors/pagefault/Kconfig"
source "kernel/trace/rv/monitors/sleep/Kconfig"
# Add new rtapp monitors here
source "kernel/trace/rv/monitors/stall/Kconfig"
source "kernel/trace/rv/monitors/deadline/Kconfig"
source "kernel/trace/rv/monitors/nomiss/Kconfig"
# Add new deadline monitors here
# Add new monitors here
config RV_REACTORS
+3
View File
@@ -17,6 +17,9 @@ obj-$(CONFIG_RV_MON_STS) += monitors/sts/sts.o
obj-$(CONFIG_RV_MON_NRP) += monitors/nrp/nrp.o
obj-$(CONFIG_RV_MON_SSSW) += monitors/sssw/sssw.o
obj-$(CONFIG_RV_MON_OPID) += monitors/opid/opid.o
obj-$(CONFIG_RV_MON_STALL) += monitors/stall/stall.o
obj-$(CONFIG_RV_MON_DEADLINE) += monitors/deadline/deadline.o
obj-$(CONFIG_RV_MON_NOMISS) += monitors/nomiss/nomiss.o
# Add new monitors here
obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
+10
View File
@@ -0,0 +1,10 @@
config RV_MON_DEADLINE
depends on RV
bool "deadline monitor"
help
Collection of monitors to check the deadline scheduler and server
behave according to specifications. Enable this to enable all
scheduler specification supported by the current kernel.
For further information, see:
Documentation/trace/rv/monitor_deadline.rst
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <linux/kallsyms.h>
#define MODULE_NAME "deadline"
#include "deadline.h"
struct rv_monitor rv_deadline = {
.name = "deadline",
.description = "container for several deadline scheduler specifications.",
.enable = NULL,
.disable = NULL,
.reset = NULL,
.enabled = 0,
};
/* Used by other monitors */
struct sched_class *rv_ext_sched_class;
static int __init register_deadline(void)
{
if (IS_ENABLED(CONFIG_SCHED_CLASS_EXT)) {
rv_ext_sched_class = (void *)kallsyms_lookup_name("ext_sched_class");
if (!rv_ext_sched_class)
pr_warn("rv: Missing ext_sched_class, monitors may not work.\n");
}
return rv_register_monitor(&rv_deadline, NULL);
}
static void __exit unregister_deadline(void)
{
rv_unregister_monitor(&rv_deadline);
}
module_init(register_deadline);
module_exit(unregister_deadline);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("deadline: container for several deadline scheduler specifications.");

Some files were not shown because too many files have changed in this diff Show More