mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 04:44:45 +01:00
The refactoring in commit30984ccf31("rv: Refactor da_monitor to minimise macros") replaced per-monitor unique variable names (da_mon_##name) with a fixed name (da_mon_this). While this works for 'static' variables (each translation unit gets its own copy), DEFINE_PER_CPU internally generates a non-static dummy variable __pcpu_unique_<n> for each per-cpu definition. The requirement for this variable to be unique although static exists for modules on specific architectures (alpha) and if the kernel is built with CONFIG_DEBUG_FORCE_WEAK_PER_CPU (e.g. Fedora's debug kernel). When multiple per-cpu monitors (e.g. sco and sts) are built-in simultaneously, they all produce the same __pcpu_unique_da_mon_this symbol, causing a link error: ld: kernel/trace/rv/monitors/sts/sts.o: multiple definition of `__pcpu_unique_da_mon_this'; kernel/trace/rv/monitors/sco/sco.o: first defined here Fix this by introducing a DA_MON_NAME macro that expands to a per-monitor unique name (da_mon_<MONITOR_NAME>) via the existing CONCATENATE helper. This restores the uniqueness that was present before the refactoring. Fixes:30984ccf31("rv: Refactor da_monitor to minimise macros") Reviewed-by: Gabriele Monaco <gmonaco@redhat.com> Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com> Link: https://lore.kernel.org/r/20260216172707.1441516-1-mikhail.v.gavrilov@gmail.com Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
504 lines
12 KiB
C
504 lines
12 KiB
C
/* 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 dot2k tool.
|
|
*
|
|
* The dot2k tool is available at tools/verification/dot2k/
|
|
*
|
|
* 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>
|
|
|
|
/*
|
|
* 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;
|
|
|
|
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 - reset a monitor and setting it to init state
|
|
*/
|
|
static inline void da_monitor_reset(struct da_monitor *da_mon)
|
|
{
|
|
da_mon->monitoring = 0;
|
|
da_mon->curr_state = model_get_initial_state();
|
|
}
|
|
|
|
/*
|
|
* da_monitor_start - start monitoring
|
|
*
|
|
* The monitor will ignore all events until monitoring is set to true. This
|
|
* function needs to be called to tell the monitor to start monitoring.
|
|
*/
|
|
static inline void da_monitor_start(struct da_monitor *da_mon)
|
|
{
|
|
da_mon->curr_state = model_get_initial_state();
|
|
da_mon->monitoring = 1;
|
|
}
|
|
|
|
/*
|
|
* da_monitoring - returns true if the monitor is processing events
|
|
*/
|
|
static inline bool da_monitoring(struct da_monitor *da_mon)
|
|
{
|
|
return da_mon->monitoring;
|
|
}
|
|
|
|
/*
|
|
* da_monitor_enabled - checks if the monitor is enabled
|
|
*/
|
|
static inline bool da_monitor_enabled(void)
|
|
{
|
|
/* global switch */
|
|
if (unlikely(!rv_monitoring_on()))
|
|
return 0;
|
|
|
|
/* monitor enabled */
|
|
if (unlikely(!rv_this.enabled))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* da_monitor_handling_event - checks if the monitor is ready to handle events
|
|
*/
|
|
static inline bool da_monitor_handling_event(struct da_monitor *da_mon)
|
|
{
|
|
if (!da_monitor_enabled())
|
|
return 0;
|
|
|
|
/* monitor is actually monitoring */
|
|
if (unlikely(!da_monitoring(da_mon)))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
|
|
/*
|
|
* Event handler for implicit monitors. Implicit monitor is the one which the
|
|
* handler does not need to specify which da_monitor to manipulate. Examples
|
|
* of implicit monitor are the per_cpu or the global ones.
|
|
*
|
|
* 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)
|
|
{
|
|
enum states curr_state, next_state;
|
|
|
|
curr_state = READ_ONCE(da_mon->curr_state);
|
|
for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) {
|
|
next_state = model_get_next_state(curr_state, event);
|
|
if (next_state == INVALID_STATE) {
|
|
react(curr_state, event);
|
|
CONCATENATE(trace_error_, MONITOR_NAME)(
|
|
model_get_state_name(curr_state),
|
|
model_get_event_name(event));
|
|
return false;
|
|
}
|
|
if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
|
|
CONCATENATE(trace_event_, MONITOR_NAME)(
|
|
model_get_state_name(curr_state),
|
|
model_get_event_name(event),
|
|
model_get_state_name(next_state),
|
|
model_is_final_state(next_state));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(event));
|
|
pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS)
|
|
" retries reached for event %s, resetting monitor %s",
|
|
model_get_event_name(event), __stringify(MONITOR_NAME));
|
|
return false;
|
|
}
|
|
|
|
#elif RV_MON_TYPE == RV_MON_PER_TASK
|
|
/*
|
|
* Event handler for per_task 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, struct task_struct *tsk,
|
|
enum events event)
|
|
{
|
|
enum states curr_state, next_state;
|
|
|
|
curr_state = READ_ONCE(da_mon->curr_state);
|
|
for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) {
|
|
next_state = model_get_next_state(curr_state, event);
|
|
if (next_state == INVALID_STATE) {
|
|
react(curr_state, event);
|
|
CONCATENATE(trace_error_, MONITOR_NAME)(tsk->pid,
|
|
model_get_state_name(curr_state),
|
|
model_get_event_name(event));
|
|
return false;
|
|
}
|
|
if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) {
|
|
CONCATENATE(trace_event_, MONITOR_NAME)(tsk->pid,
|
|
model_get_state_name(curr_state),
|
|
model_get_event_name(event),
|
|
model_get_state_name(next_state),
|
|
model_is_final_state(next_state));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
trace_rv_retries_error(__stringify(MONITOR_NAME), model_get_event_name(event));
|
|
pr_warn("rv: " __stringify(MAX_DA_RETRY_RACING_EVENTS)
|
|
" retries reached for event %s, resetting monitor %s",
|
|
model_get_event_name(event), __stringify(MONITOR_NAME));
|
|
return false;
|
|
}
|
|
#endif /* RV_MON_TYPE */
|
|
|
|
#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)
|
|
{
|
|
da_monitor_reset(da_get_monitor());
|
|
}
|
|
|
|
/*
|
|
* da_monitor_init - initialize a monitor
|
|
*/
|
|
static inline int da_monitor_init(void)
|
|
{
|
|
da_monitor_reset_all();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* da_monitor_destroy - destroy the monitor
|
|
*/
|
|
static inline void da_monitor_destroy(void) { }
|
|
|
|
#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)
|
|
{
|
|
struct da_monitor *da_mon;
|
|
int cpu;
|
|
|
|
for_each_cpu(cpu, cpu_online_mask) {
|
|
da_mon = per_cpu_ptr(&DA_MON_NAME, cpu);
|
|
da_monitor_reset(da_mon);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* da_monitor_init - initialize all CPUs' monitor
|
|
*/
|
|
static inline int da_monitor_init(void)
|
|
{
|
|
da_monitor_reset_all();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* da_monitor_destroy - destroy the monitor
|
|
*/
|
|
static inline void da_monitor_destroy(void) { }
|
|
|
|
#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;
|
|
}
|
|
|
|
static void da_monitor_reset_all(void)
|
|
{
|
|
struct task_struct *g, *p;
|
|
int cpu;
|
|
|
|
read_lock(&tasklist_lock);
|
|
for_each_process_thread(g, p)
|
|
da_monitor_reset(da_get_monitor(p));
|
|
for_each_present_cpu(cpu)
|
|
da_monitor_reset(da_get_monitor(idle_task(cpu)));
|
|
read_unlock(&tasklist_lock);
|
|
}
|
|
|
|
/*
|
|
* da_monitor_init - initialize the per-task monitor
|
|
*
|
|
* Try to allocate a slot in the task's vector of monitors. If there
|
|
* is an available slot, use it and reset all task's monitor.
|
|
*/
|
|
static int da_monitor_init(void)
|
|
{
|
|
int slot;
|
|
|
|
slot = rv_get_task_monitor_slot();
|
|
if (slot < 0 || slot >= RV_PER_TASK_MONITOR_INIT)
|
|
return slot;
|
|
|
|
task_mon_slot = slot;
|
|
|
|
da_monitor_reset_all();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* da_monitor_destroy - return the allocated slot
|
|
*/
|
|
static inline void da_monitor_destroy(void)
|
|
{
|
|
if (task_mon_slot == RV_PER_TASK_MONITOR_INIT) {
|
|
WARN_ONCE(1, "Disabling a disabled monitor: " __stringify(MONITOR_NAME));
|
|
return;
|
|
}
|
|
rv_put_task_monitor_slot(task_mon_slot);
|
|
task_mon_slot = RV_PER_TASK_MONITOR_INIT;
|
|
}
|
|
#endif /* RV_MON_TYPE */
|
|
|
|
#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
|
|
/*
|
|
* Handle event for implicit monitor: da_get_monitor() will figure out
|
|
* the monitor.
|
|
*/
|
|
|
|
static inline void __da_handle_event(struct da_monitor *da_mon,
|
|
enum events event)
|
|
{
|
|
bool retval;
|
|
|
|
retval = da_event(da_mon, event);
|
|
if (!retval)
|
|
da_monitor_reset(da_mon);
|
|
}
|
|
|
|
/*
|
|
* da_handle_event - handle an event
|
|
*/
|
|
static inline void da_handle_event(enum events event)
|
|
{
|
|
struct da_monitor *da_mon = da_get_monitor();
|
|
bool retval;
|
|
|
|
retval = da_monitor_handling_event(da_mon);
|
|
if (!retval)
|
|
return;
|
|
|
|
__da_handle_event(da_mon, event);
|
|
}
|
|
|
|
/*
|
|
* da_handle_start_event - start monitoring or handle event
|
|
*
|
|
* This function is used to notify the monitor that the system is returning
|
|
* to the initial state, so the monitor can start monitoring in the next event.
|
|
* Thus:
|
|
*
|
|
* If the monitor already started, handle the event.
|
|
* If the monitor did not start yet, start the monitor but skip the event.
|
|
*/
|
|
static inline bool da_handle_start_event(enum events event)
|
|
{
|
|
struct da_monitor *da_mon;
|
|
|
|
if (!da_monitor_enabled())
|
|
return 0;
|
|
|
|
da_mon = da_get_monitor();
|
|
|
|
if (unlikely(!da_monitoring(da_mon))) {
|
|
da_monitor_start(da_mon);
|
|
return 0;
|
|
}
|
|
|
|
__da_handle_event(da_mon, event);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* da_handle_start_run_event - start monitoring and handle event
|
|
*
|
|
* This function is used to notify the monitor that the system is in the
|
|
* initial state, so the monitor can start monitoring and handling event.
|
|
*/
|
|
static inline bool da_handle_start_run_event(enum events event)
|
|
{
|
|
struct da_monitor *da_mon;
|
|
|
|
if (!da_monitor_enabled())
|
|
return 0;
|
|
|
|
da_mon = da_get_monitor();
|
|
|
|
if (unlikely(!da_monitoring(da_mon)))
|
|
da_monitor_start(da_mon);
|
|
|
|
__da_handle_event(da_mon, event);
|
|
|
|
return 1;
|
|
}
|
|
|
|
#elif RV_MON_TYPE == RV_MON_PER_TASK
|
|
/*
|
|
* Handle event for per task.
|
|
*/
|
|
|
|
static inline void __da_handle_event(struct da_monitor *da_mon,
|
|
struct task_struct *tsk, enum events event)
|
|
{
|
|
bool retval;
|
|
|
|
retval = da_event(da_mon, tsk, event);
|
|
if (!retval)
|
|
da_monitor_reset(da_mon);
|
|
}
|
|
|
|
/*
|
|
* da_handle_event - handle an event
|
|
*/
|
|
static inline void da_handle_event(struct task_struct *tsk, enum events event)
|
|
{
|
|
struct da_monitor *da_mon = da_get_monitor(tsk);
|
|
bool retval;
|
|
|
|
retval = da_monitor_handling_event(da_mon);
|
|
if (!retval)
|
|
return;
|
|
|
|
__da_handle_event(da_mon, tsk, event);
|
|
}
|
|
|
|
/*
|
|
* da_handle_start_event - start monitoring or handle event
|
|
*
|
|
* This function is used to notify the monitor that the system is returning
|
|
* to the initial state, so the monitor can start monitoring in the next event.
|
|
* Thus:
|
|
*
|
|
* If the monitor already started, handle the event.
|
|
* If the monitor did not start yet, start the monitor but skip the event.
|
|
*/
|
|
static inline bool da_handle_start_event(struct task_struct *tsk,
|
|
enum events event)
|
|
{
|
|
struct da_monitor *da_mon;
|
|
|
|
if (!da_monitor_enabled())
|
|
return 0;
|
|
|
|
da_mon = da_get_monitor(tsk);
|
|
|
|
if (unlikely(!da_monitoring(da_mon))) {
|
|
da_monitor_start(da_mon);
|
|
return 0;
|
|
}
|
|
|
|
__da_handle_event(da_mon, tsk, event);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* da_handle_start_run_event - start monitoring and handle event
|
|
*
|
|
* This function is used to notify the monitor that the system is in the
|
|
* initial state, so the monitor can start monitoring and handling event.
|
|
*/
|
|
static inline bool da_handle_start_run_event(struct task_struct *tsk,
|
|
enum events event)
|
|
{
|
|
struct da_monitor *da_mon;
|
|
|
|
if (!da_monitor_enabled())
|
|
return 0;
|
|
|
|
da_mon = da_get_monitor(tsk);
|
|
|
|
if (unlikely(!da_monitoring(da_mon)))
|
|
da_monitor_start(da_mon);
|
|
|
|
__da_handle_event(da_mon, tsk, event);
|
|
|
|
return 1;
|
|
}
|
|
#endif /* RV_MON_TYPE */
|
|
|
|
#endif
|