linux/include/rv/da_monitor.h
Mikhail Gavrilov 75f3cf0d15 rv: Fix multiple definition of __pcpu_unique_da_mon_this
The refactoring in commit 30984ccf31 ("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>
2026-02-20 13:12:00 +01:00

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