mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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:
@@ -16,3 +16,4 @@ Runtime verification (rv) tool
|
||||
rv-mon-wip
|
||||
rv-mon-wwnr
|
||||
rv-mon-sched
|
||||
rv-mon-stall
|
||||
|
||||
@@ -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*.
|
||||
|
||||
@@ -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.
|
||||
@@ -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 ------+
|
||||
@@ -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
|
||||
----------
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
-------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user