Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* Deterministic automata helper functions, to be used with the automata
* models in C generated by the dot2k tool.
*/
#ifndef _RV_AUTOMATA_H
#define _RV_AUTOMATA_H
#ifndef MONITOR_NAME
#error "MONITOR_NAME macro is not defined. Did you include $(MODEL_NAME).h generated by rvgen?"
#endif
#define RV_AUTOMATON_NAME CONCATENATE(automaton_, MONITOR_NAME)
#define EVENT_MAX CONCATENATE(event_max_, MONITOR_NAME)
#define STATE_MAX CONCATENATE(state_max_, MONITOR_NAME)
#define events CONCATENATE(events_, MONITOR_NAME)
#define states CONCATENATE(states_, MONITOR_NAME)
/*
* model_get_state_name - return the (string) name of the given state
*/
static char *model_get_state_name(enum states state)
{
if ((state < 0) || (state >= STATE_MAX))
return "INVALID";
return RV_AUTOMATON_NAME.state_names[state];
}
/*
* model_get_event_name - return the (string) name of the given event
*/
static char *model_get_event_name(enum events event)
{
if ((event < 0) || (event >= EVENT_MAX))
return "INVALID";
return RV_AUTOMATON_NAME.event_names[event];
}
/*
* model_get_initial_state - return the automaton's initial state
*/
static inline enum states model_get_initial_state(void)
{
return RV_AUTOMATON_NAME.initial_state;
}
/*
* model_get_next_state - process an automaton event occurrence
*
* Given the current state (curr_state) and the event (event), returns
* the next state, or INVALID_STATE in case of error.
*/
static inline enum states model_get_next_state(enum states curr_state,
enum events event)
{
if ((curr_state < 0) || (curr_state >= STATE_MAX))
return INVALID_STATE;
if ((event < 0) || (event >= EVENT_MAX))
return INVALID_STATE;
return RV_AUTOMATON_NAME.function[curr_state][event];
}
/*
* model_is_final_state - check if the given state is a final state
*/
static inline bool model_is_final_state(enum states state)
{
if ((state < 0) || (state >= STATE_MAX))
return 0;
return RV_AUTOMATON_NAME.final_states[state];
}
#endif
+911
View File
@@ -0,0 +1,911 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* Deterministic automata (DA) monitor functions, to be used together
* with automata models in C generated by the rvgen tool.
*
* The rvgen tool is available at tools/verification/rvgen/
*
* For further information, see:
* Documentation/trace/rv/monitor_synthesis.rst
*/
#ifndef _RV_DA_MONITOR_H
#define _RV_DA_MONITOR_H
#include <rv/automata.h>
#include <linux/rv.h>
#include <linux/stringify.h>
#include <linux/bug.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/hashtable.h>
/*
* Per-cpu variables require a unique name although static in some
* configurations (e.g. CONFIG_DEBUG_FORCE_WEAK_PER_CPU or alpha modules).
*/
#define DA_MON_NAME CONCATENATE(da_mon_, MONITOR_NAME)
static struct rv_monitor rv_this;
/*
* Hook to allow the implementation of hybrid automata: define it with a
* function that takes curr_state, event and next_state and returns true if the
* environment constraints (e.g. timing) are satisfied, false otherwise.
*/
#ifndef da_monitor_event_hook
#define da_monitor_event_hook(...) true
#endif
/*
* Hook to allow the implementation of hybrid automata: define it with a
* function that takes the da_monitor and performs further initialisation
* (e.g. reset set up timers).
*/
#ifndef da_monitor_init_hook
#define da_monitor_init_hook(da_mon)
#endif
/*
* Hook to allow the implementation of hybrid automata: define it with a
* function that takes the da_monitor and performs further reset (e.g. reset
* all clocks).
*/
#ifndef da_monitor_reset_hook
#define da_monitor_reset_hook(da_mon)
#endif
/*
* Hook to allow the implementation of hybrid automata: define it with a
* function that waits for the termination of all monitors background
* activities (e.g. all timers). This hook can sleep.
*/
#ifndef da_monitor_sync_hook
#define da_monitor_sync_hook()
#endif
/*
* Type for the target id, default to int but can be overridden.
* A long type can work as hash table key (PER_OBJ) but will be downgraded to
* int in the event tracepoint.
* Unused for implicit monitors.
*/
#ifndef da_id_type
#define da_id_type int
#endif
static void react(enum states curr_state, enum events event)
{
rv_react(&rv_this,
"rv: monitor %s does not allow event %s on state %s\n",
__stringify(MONITOR_NAME),
model_get_event_name(event),
model_get_state_name(curr_state));
}
/*
* da_monitor_reset_state - reset a monitor and setting it to init state
*/
static inline void da_monitor_reset_state(struct da_monitor *da_mon)
{
WRITE_ONCE(da_mon->monitoring, 0);
/* Pair with load in __ha_monitor_timer_callback */
smp_store_release(&da_mon->curr_state, model_get_initial_state());
}
/*
* da_monitor_reset - reset a monitor and setting it to init state
*/
static inline void da_monitor_reset(struct da_monitor *da_mon)
{
da_monitor_reset_hook(da_mon);
da_monitor_reset_state(da_mon);
}
/*
* da_monitor_start - start monitoring
*
* The monitor will ignore all events until monitoring is set to true. This
* function needs to be called to tell the monitor to start monitoring.
*/
static inline void da_monitor_start(struct da_monitor *da_mon)
{
da_mon->curr_state = model_get_initial_state();
da_monitor_init_hook(da_mon);
/* Pairs with smp_load_acquire in da_monitoring(). */
smp_store_release(&da_mon->monitoring, 1);
}
/*
* da_monitoring - returns true if the monitor is processing events
*/
static inline bool da_monitoring(struct da_monitor *da_mon)
{
/* Pairs with smp_store_release in da_monitor_start(). */
return smp_load_acquire(&da_mon->monitoring);
}
/*
* da_monitor_enabled - checks if the monitor is enabled
*/
static inline bool da_monitor_enabled(void)
{
/* global switch */
if (unlikely(!rv_monitoring_on()))
return 0;
/* monitor enabled */
if (unlikely(!rv_this.enabled))
return 0;
return 1;
}
/*
* da_monitor_handling_event - checks if the monitor is ready to handle events
*/
static inline bool da_monitor_handling_event(struct da_monitor *da_mon)
{
if (!da_monitor_enabled())
return 0;
/* monitor is actually monitoring */
if (unlikely(!da_monitoring(da_mon)))
return 0;
return 1;
}
#if RV_MON_TYPE == RV_MON_GLOBAL
/*
* Functions to define, init and get a global monitor.
*/
/*
* global monitor (a single variable)
*/
static struct da_monitor DA_MON_NAME;
/*
* da_get_monitor - return the global monitor address
*/
static struct da_monitor *da_get_monitor(void)
{
return &DA_MON_NAME;
}
/*
* __da_monitor_reset_all - reset the single monitor
*/
static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
reset(da_get_monitor());
}
/*
* da_monitor_reset_all - reset the single monitor
*/
static void da_monitor_reset_all(void)
{
__da_monitor_reset_all(da_monitor_reset);
}
/*
* da_monitor_reset_state_all - reset the single monitor
*/
static inline void da_monitor_reset_state_all(void)
{
__da_monitor_reset_all(da_monitor_reset_state);
}
/*
* da_monitor_init - initialize a monitor
*/
static inline int da_monitor_init(void)
{
da_monitor_reset_state_all();
return 0;
}
/*
* da_monitor_destroy - destroy the monitor
*/
static inline void da_monitor_destroy(void)
{
da_monitor_reset_all();
da_monitor_sync_hook();
}
#ifndef da_implicit_guard
#define da_implicit_guard()
#endif
#elif RV_MON_TYPE == RV_MON_PER_CPU
/*
* Functions to define, init and get a per-cpu monitor.
*/
/*
* per-cpu monitor variables
*/
static DEFINE_PER_CPU(struct da_monitor, DA_MON_NAME);
/*
* da_get_monitor - return current CPU monitor address
*/
static struct da_monitor *da_get_monitor(void)
{
return this_cpu_ptr(&DA_MON_NAME);
}
/*
* __da_monitor_reset_all - reset all CPUs' monitor
*/
static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
struct da_monitor *da_mon;
int cpu;
for_each_cpu(cpu, cpu_online_mask) {
da_mon = per_cpu_ptr(&DA_MON_NAME, cpu);
reset(da_mon);
}
}
/*
* da_monitor_reset_all - reset all CPUs' monitor
*/
static void da_monitor_reset_all(void)
{
__da_monitor_reset_all(da_monitor_reset);
}
/*
* da_monitor_reset_state_all - reset all CPUs' monitor
*/
static inline void da_monitor_reset_state_all(void)
{
__da_monitor_reset_all(da_monitor_reset_state);
}
/*
* da_monitor_init - initialize all CPUs' monitor
*/
static inline int da_monitor_init(void)
{
da_monitor_reset_state_all();
return 0;
}
/*
* da_monitor_destroy - destroy the monitor
*/
static inline void da_monitor_destroy(void)
{
da_monitor_reset_all();
da_monitor_sync_hook();
}
#ifndef da_implicit_guard
#define da_implicit_guard() guard(migrate)()
#endif
#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
* Functions to define, init and get a per-task monitor.
*/
/*
* The per-task monitor is stored a vector in the task struct. This variable
* stores the position on the vector reserved for this monitor.
*/
static int task_mon_slot = RV_PER_TASK_MONITOR_INIT;
/*
* da_get_monitor - return the monitor in the allocated slot for tsk
*/
static inline struct da_monitor *da_get_monitor(struct task_struct *tsk)
{
return &tsk->rv[task_mon_slot].da_mon;
}
/*
* da_get_target - return the task associated to the monitor
*/
static inline struct task_struct *da_get_target(struct da_monitor *da_mon)
{
return container_of(da_mon, struct task_struct, rv[task_mon_slot].da_mon);
}
/*
* da_get_id - return the id associated to the monitor
*
* For per-task monitors, the id is the task's PID.
*/
static inline da_id_type da_get_id(struct da_monitor *da_mon)
{
return da_get_target(da_mon)->pid;
}
static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
struct task_struct *g, *p;
int cpu;
read_lock(&tasklist_lock);
for_each_process_thread(g, p)
reset(da_get_monitor(p));
for_each_present_cpu(cpu)
reset(da_get_monitor(idle_task(cpu)));
read_unlock(&tasklist_lock);
}
static void da_monitor_reset_all(void)
{
__da_monitor_reset_all(da_monitor_reset);
}
static inline void da_monitor_reset_state_all(void)
{
__da_monitor_reset_all(da_monitor_reset_state);
}
/*
* da_monitor_init - initialize the per-task monitor
*
* Try to allocate a slot in the task's vector of monitors. If there
* is an available slot, use it and reset all task's monitor.
*/
static int da_monitor_init(void)
{
int slot;
slot = rv_get_task_monitor_slot();
if (slot < 0 || slot >= RV_PER_TASK_MONITOR_INIT)
return slot;
task_mon_slot = slot;
da_monitor_reset_state_all();
return 0;
}
/*
* da_monitor_destroy - return the allocated slot
*
* Wait for all in-flight handlers before returning the slot to avoid
* out-of-bound accesses.
*/
static inline void da_monitor_destroy(void)
{
if (task_mon_slot == RV_PER_TASK_MONITOR_INIT) {
WARN_ONCE(1, "Disabling a disabled monitor: " __stringify(MONITOR_NAME));
return;
}
tracepoint_synchronize_unregister();
da_monitor_reset_all();
da_monitor_sync_hook();
rv_put_task_monitor_slot(task_mon_slot);
task_mon_slot = RV_PER_TASK_MONITOR_INIT;
}
#elif RV_MON_TYPE == RV_MON_PER_OBJ
/*
* Functions to define, init and get a per-object monitor.
*/
struct da_monitor_storage {
da_id_type id;
monitor_target target;
union rv_task_monitor rv;
struct hlist_node node;
struct rcu_head rcu;
};
#ifndef DA_MONITOR_HT_BITS
#define DA_MONITOR_HT_BITS 10
#endif
static DEFINE_HASHTABLE(da_monitor_ht, DA_MONITOR_HT_BITS);
/*
* da_create_empty_storage - pre-allocate an empty storage
*/
static inline struct da_monitor_storage *da_create_empty_storage(da_id_type id)
{
struct da_monitor_storage *mon_storage;
mon_storage = kmalloc_nolock(sizeof(struct da_monitor_storage),
__GFP_ZERO, NUMA_NO_NODE);
if (!mon_storage)
return NULL;
hash_add_rcu(da_monitor_ht, &mon_storage->node, id);
mon_storage->id = id;
return mon_storage;
}
/*
* da_create_storage - create the per-object storage
*
* The caller is responsible to synchronise writers, either with locks or
* implicitly. For instance, if da_create_storage is only called from a single
* event for target (e.g. sched_switch), it's safe to call this without locks.
*/
static inline struct da_monitor *da_create_storage(da_id_type id,
monitor_target target,
struct da_monitor *da_mon)
{
struct da_monitor_storage *mon_storage;
if (da_mon)
return da_mon;
mon_storage = da_create_empty_storage(id);
if (!mon_storage)
return NULL;
mon_storage->target = target;
return &mon_storage->rv.da_mon;
}
/*
* __da_get_mon_storage - get the monitor storage from the hash table
*/
static inline struct da_monitor_storage *__da_get_mon_storage(da_id_type id)
{
struct da_monitor_storage *mon_storage;
lockdep_assert_in_rcu_read_lock();
hash_for_each_possible_rcu(da_monitor_ht, mon_storage, node, id) {
if (mon_storage->id == id)
return mon_storage;
}
return NULL;
}
/*
* da_get_monitor - return the monitor for target
*/
static struct da_monitor *da_get_monitor(da_id_type id, monitor_target target)
{
struct da_monitor_storage *mon_storage;
mon_storage = __da_get_mon_storage(id);
return mon_storage ? &mon_storage->rv.da_mon : NULL;
}
/*
* da_get_target - return the object associated to the monitor
*/
static inline monitor_target da_get_target(struct da_monitor *da_mon)
{
return container_of(da_mon, struct da_monitor_storage, rv.da_mon)->target;
}
/*
* da_get_id - return the id associated to the monitor
*/
static inline da_id_type da_get_id(struct da_monitor *da_mon)
{
return container_of(da_mon, struct da_monitor_storage, rv.da_mon)->id;
}
/*
* da_create_or_get - create the per-object storage if not already there
*
* This needs a lookup so should be guarded by RCU, the condition is checked
* directly in da_create_storage()
*/
static inline void da_create_or_get(da_id_type id, monitor_target target)
{
guard(rcu)();
da_create_storage(id, target, da_get_monitor(id, target));
}
/*
* da_fill_empty_storage - store the target in a pre-allocated storage
*
* Can be used as a substitute of da_create_storage when starting a monitor in
* an environment where allocation is unsafe.
*/
static inline struct da_monitor *da_fill_empty_storage(da_id_type id,
monitor_target target,
struct da_monitor *da_mon)
{
if (unlikely(da_mon && !da_get_target(da_mon)))
container_of(da_mon, struct da_monitor_storage, rv.da_mon)->target = target;
return da_mon;
}
/*
* da_get_target_by_id - return the object associated to the id
*/
static inline monitor_target da_get_target_by_id(da_id_type id)
{
struct da_monitor_storage *mon_storage;
guard(rcu)();
mon_storage = __da_get_mon_storage(id);
if (unlikely(!mon_storage))
return NULL;
return mon_storage->target;
}
/*
* da_destroy_storage - destroy the per-object storage
*
* The caller is responsible to synchronise writers, either with locks or
* implicitly. For instance, if da_destroy_storage is called at sched_exit and
* da_create_storage can never occur after that, it's safe to call this without
* locks.
* This function includes an RCU read-side critical section to synchronise
* against da_monitor_destroy().
*/
static inline void da_destroy_storage(da_id_type id)
{
struct da_monitor_storage *mon_storage;
guard(rcu)();
mon_storage = __da_get_mon_storage(id);
if (!mon_storage)
return;
da_monitor_reset_hook(&mon_storage->rv.da_mon);
hash_del_rcu(&mon_storage->node);
kfree_rcu(mon_storage, rcu);
}
static void __da_monitor_reset_all(void (*reset)(struct da_monitor *))
{
struct da_monitor_storage *mon_storage;
int bkt;
guard(rcu)();
hash_for_each_rcu(da_monitor_ht, bkt, mon_storage, node)
reset(&mon_storage->rv.da_mon);
}
static void da_monitor_reset_all(void)
{
__da_monitor_reset_all(da_monitor_reset);
}
static inline void da_monitor_reset_state_all(void)
{
__da_monitor_reset_all(da_monitor_reset_state);
}
static inline int da_monitor_init(void)
{
hash_init(da_monitor_ht);
return 0;
}
static inline void da_monitor_destroy(void)
{
struct da_monitor_storage *mon_storage;
struct hlist_node *tmp;
int bkt;
tracepoint_synchronize_unregister();
da_monitor_reset_all();
da_monitor_sync_hook();
/*
* This function is called after all probes are disabled and no longer
* pending, we can safely assume no concurrent user.
*/
hash_for_each_safe(da_monitor_ht, bkt, tmp, mon_storage, node) {
hash_del_rcu(&mon_storage->node);
kfree(mon_storage);
}
}
/*
* Allow the per-object monitors to run allocation manually, necessary if the
* start condition is in a context problematic for allocation (e.g. scheduling).
* In such case, if the storage was pre-allocated without a target, set it now.
*/
#ifdef DA_SKIP_AUTO_ALLOC
#define da_prepare_storage da_fill_empty_storage
#else
#define da_prepare_storage da_create_storage
#endif /* DA_SKIP_AUTO_ALLOC */
#endif /* RV_MON_TYPE */
#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
/*
* Trace events for implicit monitors. Implicit monitor is the one which the
* handler does not need to specify which da_monitor to manipulate. Examples
* of implicit monitor are the per_cpu or the global ones.
*/
static inline void da_trace_event(struct da_monitor *da_mon,
char *curr_state, char *event,
char *next_state, bool is_final,
da_id_type id)
{
CONCATENATE(trace_event_, MONITOR_NAME)(curr_state, event, next_state,
is_final);
}
static inline void da_trace_error(struct da_monitor *da_mon,
char *curr_state, char *event,
da_id_type id)
{
CONCATENATE(trace_error_, MONITOR_NAME)(curr_state, event);
}
/*
* da_get_id - unused for implicit monitors
*/
static inline da_id_type da_get_id(struct da_monitor *da_mon)
{
return 0;
}
#elif RV_MON_TYPE == RV_MON_PER_TASK || RV_MON_TYPE == RV_MON_PER_OBJ
/*
* Trace events for per_task/per_object monitors, report the target id.
*/
static inline void da_trace_event(struct da_monitor *da_mon,
char *curr_state, char *event,
char *next_state, bool is_final,
da_id_type id)
{
CONCATENATE(trace_event_, MONITOR_NAME)(id, curr_state, event,
next_state, is_final);
}
static inline void da_trace_error(struct da_monitor *da_mon,
char *curr_state, char *event,
da_id_type id)
{
CONCATENATE(trace_error_, MONITOR_NAME)(id, curr_state, event);
}
#endif /* RV_MON_TYPE */
/*
* da_event - handle an event for the da_mon
*
* This function is valid for both implicit and id monitors.
* Retry in case there is a race between getting and setting the next state,
* warn and reset the monitor if it runs out of retries. The monitor should be
* able to handle various orders.
*/
static inline bool da_event(struct da_monitor *da_mon, enum events event, da_id_type id)
{
enum states curr_state, next_state;
curr_state = READ_ONCE(da_mon->curr_state);
for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) {
next_state = model_get_next_state(curr_state, event);
if (next_state == INVALID_STATE) {
react(curr_state, event);
da_trace_error(da_mon, model_get_state_name(curr_state),
model_get_event_name(event), id);
return false;
}
if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
if (!da_monitor_event_hook(da_mon, curr_state, event, next_state, id))
return false;
da_trace_event(da_mon, model_get_state_name(curr_state),
model_get_event_name(event),
model_get_state_name(next_state),
model_is_final_state(next_state), id);
return true;
}
}
trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(event));
pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS)
" retries reached for event %s, resetting monitor %s",
model_get_event_name(event), __stringify(MONITOR_NAME));
return false;
}
static inline void __da_handle_event_common(struct da_monitor *da_mon,
enum events event, da_id_type id)
{
if (!da_event(da_mon, event, id))
da_monitor_reset(da_mon);
}
static inline void __da_handle_event(struct da_monitor *da_mon,
enum events event, da_id_type id)
{
if (da_monitor_handling_event(da_mon))
__da_handle_event_common(da_mon, event, id);
}
static inline bool __da_handle_start_event(struct da_monitor *da_mon,
enum events event, da_id_type id)
{
if (!da_monitor_enabled())
return 0;
if (unlikely(!da_monitoring(da_mon))) {
da_monitor_start(da_mon);
return 0;
}
__da_handle_event_common(da_mon, event, id);
return 1;
}
static inline bool __da_handle_start_run_event(struct da_monitor *da_mon,
enum events event, da_id_type id)
{
if (!da_monitor_enabled())
return 0;
if (unlikely(!da_monitoring(da_mon)))
da_monitor_start(da_mon);
__da_handle_event_common(da_mon, event, id);
return 1;
}
#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
/*
* Handle event for implicit monitor: da_get_monitor() will figure out
* the monitor.
*/
/*
* da_handle_event - handle an event
*/
static inline void da_handle_event(enum events event)
{
da_implicit_guard();
__da_handle_event(da_get_monitor(), event, 0);
}
/*
* da_handle_start_event - start monitoring or handle event
*
* This function is used to notify the monitor that the system is returning
* to the initial state, so the monitor can start monitoring in the next event.
* Thus:
*
* If the monitor already started, handle the event.
* If the monitor did not start yet, start the monitor but skip the event.
*/
static inline bool da_handle_start_event(enum events event)
{
da_implicit_guard();
return __da_handle_start_event(da_get_monitor(), event, 0);
}
/*
* da_handle_start_run_event - start monitoring and handle event
*
* This function is used to notify the monitor that the system is in the
* initial state, so the monitor can start monitoring and handling event.
*/
static inline bool da_handle_start_run_event(enum events event)
{
da_implicit_guard();
return __da_handle_start_run_event(da_get_monitor(), event, 0);
}
#elif RV_MON_TYPE == RV_MON_PER_TASK
/*
* Handle event for per task.
*/
/*
* da_handle_event - handle an event
*/
static inline void da_handle_event(struct task_struct *tsk, enum events event)
{
__da_handle_event(da_get_monitor(tsk), event, tsk->pid);
}
/*
* da_handle_start_event - start monitoring or handle event
*
* This function is used to notify the monitor that the system is returning
* to the initial state, so the monitor can start monitoring in the next event.
* Thus:
*
* If the monitor already started, handle the event.
* If the monitor did not start yet, start the monitor but skip the event.
*/
static inline bool da_handle_start_event(struct task_struct *tsk,
enum events event)
{
return __da_handle_start_event(da_get_monitor(tsk), event, tsk->pid);
}
/*
* da_handle_start_run_event - start monitoring and handle event
*
* This function is used to notify the monitor that the system is in the
* initial state, so the monitor can start monitoring and handling event.
*/
static inline bool da_handle_start_run_event(struct task_struct *tsk,
enum events event)
{
return __da_handle_start_run_event(da_get_monitor(tsk), event, tsk->pid);
}
#elif RV_MON_TYPE == RV_MON_PER_OBJ
/*
* Handle event for per object.
*/
/*
* da_handle_event - handle an event
*/
static inline void da_handle_event(da_id_type id, monitor_target target, enum events event)
{
struct da_monitor *da_mon;
guard(rcu)();
da_mon = da_get_monitor(id, target);
if (likely(da_mon))
__da_handle_event(da_mon, event, id);
}
/*
* da_handle_start_event - start monitoring or handle event
*
* This function is used to notify the monitor that the system is returning
* to the initial state, so the monitor can start monitoring in the next event.
* Thus:
*
* If the monitor already started, handle the event.
* If the monitor did not start yet, start the monitor but skip the event.
*/
static inline bool da_handle_start_event(da_id_type id, monitor_target target,
enum events event)
{
struct da_monitor *da_mon;
guard(rcu)();
da_mon = da_get_monitor(id, target);
da_mon = da_prepare_storage(id, target, da_mon);
if (unlikely(!da_mon))
return 0;
return __da_handle_start_event(da_mon, event, id);
}
/*
* da_handle_start_run_event - start monitoring and handle event
*
* This function is used to notify the monitor that the system is in the
* initial state, so the monitor can start monitoring and handling event.
*/
static inline bool da_handle_start_run_event(da_id_type id, monitor_target target,
enum events event)
{
struct da_monitor *da_mon;
guard(rcu)();
da_mon = da_get_monitor(id, target);
da_mon = da_prepare_storage(id, target, da_mon);
if (unlikely(!da_mon))
return 0;
return __da_handle_start_run_event(da_mon, event, id);
}
static inline void da_reset(da_id_type id, monitor_target target)
{
struct da_monitor *da_mon;
guard(rcu)();
da_mon = da_get_monitor(id, target);
if (likely(da_mon))
da_monitor_reset(da_mon);
}
#endif /* RV_MON_TYPE */
#endif
+561
View File
@@ -0,0 +1,561 @@
/* 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 inline void ha_cancel_timer_sync(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
#define da_monitor_sync_hook() synchronize_rcu()
#if !defined(HA_SKIP_AUTO_CLEANUP) && RV_MON_TYPE == RV_MON_PER_TASK
/*
* Automatic cleanup handlers for per-task HA monitors, only skip if you know
* what you are doing (e.g. you want to implement cleanup manually in another
* handler doing more things).
*/
static void ha_handle_sched_process_exit(void *data, struct task_struct *p,
bool group_dead);
#define ha_monitor_enable_hook() \
rv_attach_trace_probe(__stringify(MONITOR_NAME), sched_process_exit, \
ha_handle_sched_process_exit)
#define ha_monitor_disable_hook() \
rv_detach_trace_probe(__stringify(MONITOR_NAME), sched_process_exit, \
ha_handle_sched_process_exit)
#else
#define ha_monitor_enable_hook() ((void)0)
#define ha_monitor_disable_hook() ((void)0)
#endif
#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 */
static bool ha_mon_destroying;
static int ha_monitor_init(void)
{
int ret;
WRITE_ONCE(ha_mon_destroying, false);
ret = da_monitor_init();
if (ret == 0)
ha_monitor_enable_hook();
return ret;
}
static void ha_monitor_destroy(void)
{
WRITE_ONCE(ha_mon_destroying, true);
ha_monitor_disable_hook();
da_monitor_destroy();
}
/* 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.
* Monitors that never started have their timer uninitialized, do not stop those.
*/
static inline void ha_monitor_reset_env(struct da_monitor *da_mon)
{
struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
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);
}
#if !defined(HA_SKIP_AUTO_CLEANUP) && RV_MON_TYPE == RV_MON_PER_TASK
static void ha_handle_sched_process_exit(void *data, struct task_struct *p,
bool group_dead)
{
struct da_monitor *da_mon = da_get_monitor(p);
if (likely(da_monitoring(da_mon))) {
da_monitor_reset(da_mon);
ha_cancel_timer_sync(to_ha_monitor(da_mon));
}
}
#endif
#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;
}
/*
* __ha_monitor_timer_callback - generic callback representation
*
* This callback runs in an RCU read-side critical section to allow the
* destruction sequence to easily synchronize_rcu() with all pending timers
* after asynchronously disabling them. The ha_mon_destroying check ensures
* any callback entering the RCU section after synchronize_rcu() completes
* will see the flag and bail out immediately.
*/
static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon)
{
DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
enum states curr_state;
u64 time_ns;
guard(rcu)();
if (unlikely(READ_ONCE(ha_mon_destroying)))
return;
/* Ensure consistent curr_state if we race with da_monitor_reset */
curr_state = smp_load_acquire(&ha_mon->da_mon.curr_state);
if (unlikely(!da_monitor_handling_event(&ha_mon->da_mon)))
return;
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);
}
static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon)
{
timer_delete_sync(&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;
}
static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon)
{
hrtimer_cancel(&ha_mon->hrtimer);
}
#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;
}
static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon) { }
#endif
#endif
+29
View File
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org>
*
* Helper functions to facilitate the instrumentation of auto-generated
* RV monitors create by dot2k.
*
* The dot2k tool is available at tools/verification/dot2/
*/
#include <linux/ftrace.h>
/*
* rv_attach_trace_probe - check and attach a handler function to a tracepoint
*/
#define rv_attach_trace_probe(monitor, tp, rv_handler) \
do { \
check_trace_callback_type_##tp(rv_handler); \
WARN_ONCE(register_trace_##tp(rv_handler, NULL), \
"fail attaching " #monitor " " #tp "handler"); \
} while (0)
/*
* rv_detach_trace_probe - detach a handler function to a tracepoint
*/
#define rv_detach_trace_probe(monitor, tp, rv_handler) \
do { \
unregister_trace_##tp(rv_handler, NULL); \
} while (0)
+174
View File
@@ -0,0 +1,174 @@
/* SPDX-License-Identifier: GPL-2.0 */
/**
* This file must be combined with the $(MODEL_NAME).h file generated by
* tools/verification/rvgen.
*/
#include <linux/args.h>
#include <linux/rv.h>
#include <linux/stringify.h>
#include <linux/seq_buf.h>
#include <rv/instrumentation.h>
#include <trace/events/task.h>
#include <trace/events/sched.h>
#ifndef MONITOR_NAME
#error "Please include $(MODEL_NAME).h generated by rvgen"
#endif
#define RV_MONITOR_NAME CONCATENATE(rv_, MONITOR_NAME)
static struct rv_monitor RV_MONITOR_NAME;
static int ltl_monitor_slot = RV_PER_TASK_MONITOR_INIT;
static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon);
static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation);
static struct ltl_monitor *ltl_get_monitor(struct task_struct *task)
{
return &task->rv[ltl_monitor_slot].ltl_mon;
}
static void ltl_task_init(struct task_struct *task, bool task_creation)
{
struct ltl_monitor *mon = ltl_get_monitor(task);
memset(&mon->states, 0, sizeof(mon->states));
for (int i = 0; i < LTL_NUM_ATOM; ++i)
__set_bit(i, mon->unknown_atoms);
ltl_atoms_init(task, mon, task_creation);
ltl_atoms_fetch(task, mon);
}
static void handle_task_newtask(void *data, struct task_struct *task, u64 flags)
{
ltl_task_init(task, true);
}
static int ltl_monitor_init(void)
{
struct task_struct *g, *p;
int ret, cpu;
ret = rv_get_task_monitor_slot();
if (ret < 0)
return ret;
ltl_monitor_slot = ret;
rv_attach_trace_probe(name, task_newtask, handle_task_newtask);
read_lock(&tasklist_lock);
for_each_process_thread(g, p)
ltl_task_init(p, false);
for_each_present_cpu(cpu)
ltl_task_init(idle_task(cpu), false);
read_unlock(&tasklist_lock);
return 0;
}
static void ltl_monitor_destroy(void)
{
rv_detach_trace_probe(name, task_newtask, handle_task_newtask);
tracepoint_synchronize_unregister();
rv_put_task_monitor_slot(ltl_monitor_slot);
ltl_monitor_slot = RV_PER_TASK_MONITOR_INIT;
}
static void ltl_illegal_state(struct task_struct *task, struct ltl_monitor *mon)
{
CONCATENATE(trace_error_, MONITOR_NAME)(task);
rv_react(&RV_MONITOR_NAME, "rv: "__stringify(MONITOR_NAME)": %s[%d]: violation detected\n",
task->comm, task->pid);
}
static void ltl_attempt_start(struct task_struct *task, struct ltl_monitor *mon)
{
if (rv_ltl_all_atoms_known(mon))
ltl_start(task, mon);
}
static inline void ltl_atom_set(struct ltl_monitor *mon, enum ltl_atom atom, bool value)
{
__clear_bit(atom, mon->unknown_atoms);
if (value)
__set_bit(atom, mon->atoms);
else
__clear_bit(atom, mon->atoms);
}
static void
ltl_trace_event(struct task_struct *task, struct ltl_monitor *mon, unsigned long *next_state)
{
const char *format_str = "%s";
DECLARE_SEQ_BUF(atoms, 64);
char states[32], next[32];
int i;
if (!CONCATENATE(CONCATENATE(trace_event_, MONITOR_NAME), _enabled)())
return;
snprintf(states, sizeof(states), "%*pbl", RV_MAX_BA_STATES, mon->states);
snprintf(next, sizeof(next), "%*pbl", RV_MAX_BA_STATES, next_state);
for (i = 0; i < LTL_NUM_ATOM; ++i) {
if (test_bit(i, mon->atoms)) {
seq_buf_printf(&atoms, format_str, ltl_atom_str(i));
format_str = ",%s";
}
}
CONCATENATE(trace_event_, MONITOR_NAME)(task, states, atoms.buffer, next);
}
static void ltl_validate(struct task_struct *task, struct ltl_monitor *mon)
{
DECLARE_BITMAP(next_states, RV_MAX_BA_STATES) = {0};
if (!rv_ltl_valid_state(mon))
return;
for (unsigned int i = 0; i < RV_NUM_BA_STATES; ++i) {
if (test_bit(i, mon->states))
ltl_possible_next_states(mon, i, next_states);
}
ltl_trace_event(task, mon, next_states);
memcpy(mon->states, next_states, sizeof(next_states));
if (!rv_ltl_valid_state(mon))
ltl_illegal_state(task, mon);
}
static void ltl_atom_update(struct task_struct *task, enum ltl_atom atom, bool value)
{
struct ltl_monitor *mon = ltl_get_monitor(task);
ltl_atom_set(mon, atom, value);
ltl_atoms_fetch(task, mon);
if (!rv_ltl_valid_state(mon)) {
ltl_attempt_start(task, mon);
return;
}
ltl_validate(task, mon);
}
static void __maybe_unused ltl_atom_pulse(struct task_struct *task, enum ltl_atom atom, bool value)
{
struct ltl_monitor *mon = ltl_get_monitor(task);
ltl_atom_update(task, atom, value);
ltl_atom_set(mon, atom, !value);
ltl_validate(task, mon);
}