linux/kernel/trace/rv/monitors/opid/opid.c
Gabriele Monaco 2b406fdb33 rv: Convert the opid monitor to a hybrid automaton
The opid monitor validates that wakeup and need_resched events only
occur with interrupts and preemption disabled by following the
preemptirq tracepoints.
As reported in [1], those tracepoints might be inaccurate in some
situations (e.g. NMIs).

Since the monitor doesn't validate other ordering properties, remove the
dependency on preemptirq tracepoints and convert the monitor to a hybrid
automaton to validate the constraint during event handling.
This makes the monitor more robust by also removing the workaround for
interrupts missing the preemption tracepoints, which was working on
PREEMPT_RT only and allows the monitor to be built on kernels without
the preemptirqs tracepoints.

[1] - https://lore.kernel.org/lkml/20250625120823.60600-1-gmonaco@redhat.com

Reviewed-by: Nam Cao <namcao@linutronix.de>
Link: https://lore.kernel.org/r/20260330111010.153663-8-gmonaco@redhat.com
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
2026-03-31 16:47:17 +02:00

124 lines
3.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/ftrace.h>
#include <linux/tracepoint.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rv.h>
#include <rv/instrumentation.h>
#define MODULE_NAME "opid"
#include <trace/events/sched.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>
#define RV_MON_TYPE RV_MON_PER_CPU
#include "opid.h"
#include <rv/ha_monitor.h>
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_opid env, u64 time_ns)
{
if (env == irq_off_opid)
return irqs_disabled();
else if (env == preempt_off_opid) {
/*
* If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables
* preemption (adding one to the preempt_count). Since we are
* interested in the preempt_count at the time the tracepoint was
* hit, we consider 1 as still enabled.
*/
if (IS_ENABLED(CONFIG_PREEMPTION))
return (preempt_count() & PREEMPT_MASK) > 1;
return true;
}
return ENV_INVALID_VALUE;
}
static inline bool ha_verify_guards(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
bool res = true;
if (curr_state == any_opid && event == sched_need_resched_opid)
res = ha_get_env(ha_mon, irq_off_opid, time_ns) == 1ull;
else if (curr_state == any_opid && event == sched_waking_opid)
res = ha_get_env(ha_mon, irq_off_opid, time_ns) == 1ull &&
ha_get_env(ha_mon, preempt_off_opid, time_ns) == 1ull;
return res;
}
static bool ha_verify_constraint(struct ha_monitor *ha_mon,
enum states curr_state, enum events event,
enum states next_state, u64 time_ns)
{
if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
return false;
return true;
}
static void handle_sched_need_resched(void *data, struct task_struct *tsk, int cpu, int tif)
{
da_handle_start_run_event(sched_need_resched_opid);
}
static void handle_sched_waking(void *data, struct task_struct *p)
{
da_handle_start_run_event(sched_waking_opid);
}
static int enable_opid(void)
{
int retval;
retval = da_monitor_init();
if (retval)
return retval;
rv_attach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
rv_attach_trace_probe("opid", sched_waking, handle_sched_waking);
return 0;
}
static void disable_opid(void)
{
rv_this.enabled = 0;
rv_detach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
rv_detach_trace_probe("opid", sched_waking, handle_sched_waking);
da_monitor_destroy();
}
/*
* This is the monitor register section.
*/
static struct rv_monitor rv_this = {
.name = "opid",
.description = "operations with preemption and irq disabled.",
.enable = enable_opid,
.disable = disable_opid,
.reset = da_monitor_reset_all,
.enabled = 0,
};
static int __init register_opid(void)
{
return rv_register_monitor(&rv_this, &rv_sched);
}
static void __exit unregister_opid(void)
{
rv_unregister_monitor(&rv_this);
}
module_init(register_opid);
module_exit(unregister_opid);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("opid: operations with preemption and irq disabled.");