Commit 61438453 authored by Gabriele Monaco's avatar Gabriele Monaco Committed by Steven Rostedt (Google)
Browse files

rv: Add opid per-cpu monitor

Add a per-cpu monitor as part of the sched model:
* opid: operations with preemption and irq disabled
    Monitor to ensure wakeup and need_resched occur with irq and
    preemption disabled or in irq handlers.

Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Tomas Glozar <tglozar@redhat.com>
Cc: Juri Lelli <jlelli@redhat.com>
Cc: Clark Williams <williams@redhat.com>
Cc: John Kacur <jkacur@redhat.com>
Link: https://lore.kernel.org/20250728135022.255578-10-gmonaco@redhat.com


Signed-off-by: default avatarGabriele Monaco <gmonaco@redhat.com>
Acked-by: default avatarNam Cao <namcao@linutronix.de>
Tested-by: default avatarNam Cao <namcao@linutronix.de>
Signed-off-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
parent e8440a88
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -341,6 +341,61 @@ can be triggered also by preemption, but cannot occur after the task got to
                             |           | switch_yield
                             +-----------+ wakeup

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.
``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::

                 |                     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.

References
----------

+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ source "kernel/trace/rv/monitors/snep/Kconfig"
source "kernel/trace/rv/monitors/sts/Kconfig"
source "kernel/trace/rv/monitors/nrp/Kconfig"
source "kernel/trace/rv/monitors/sssw/Kconfig"
source "kernel/trace/rv/monitors/opid/Kconfig"
# Add new sched monitors here

source "kernel/trace/rv/monitors/rtapp/Kconfig"
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ obj-$(CONFIG_RV_MON_SLEEP) += monitors/sleep/sleep.o
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
# Add new monitors here
obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
+19 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
#
config RV_MON_OPID
	depends on RV
	depends on TRACE_IRQFLAGS
	depends on TRACE_PREEMPT_TOGGLE
	depends on RV_MON_SCHED
	default y if PREEMPT_RT
	select DA_MON_EVENTS_IMPLICIT
	bool "opid monitor"
	help
	  Monitor to ensure operations like wakeup and need resched occur with
	  interrupts and preemption disabled or during IRQs, where preemption
	  may not be disabled explicitly.

	  This monitor is unstable on !PREEMPT_RT, say N unless you are testing it.

	  For further information, see:
	    Documentation/trace/rv/monitor_sched.rst
+168 −0
Original line number Diff line number Diff line
// 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>
#include <rv/da_monitor.h>

#define MODULE_NAME "opid"

#include <trace/events/sched.h>
#include <trace/events/irq.h>
#include <trace/events/preemptirq.h>
#include <rv_trace.h>
#include <monitors/sched/sched.h>

#include "opid.h"

static struct rv_monitor rv_opid;
DECLARE_DA_MON_PER_CPU(opid, unsigned char);

#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/trace/irq_vectors.h>

static void handle_vector_irq_entry(void *data, int vector)
{
	da_handle_event_opid(irq_entry_opid);
}

static void attach_vector_irq(void)
{
	rv_attach_trace_probe("opid", local_timer_entry, handle_vector_irq_entry);
	if (IS_ENABLED(CONFIG_IRQ_WORK))
		rv_attach_trace_probe("opid", irq_work_entry, handle_vector_irq_entry);
	if (IS_ENABLED(CONFIG_SMP)) {
		rv_attach_trace_probe("opid", reschedule_entry, handle_vector_irq_entry);
		rv_attach_trace_probe("opid", call_function_entry, handle_vector_irq_entry);
		rv_attach_trace_probe("opid", call_function_single_entry, handle_vector_irq_entry);
	}
}

static void detach_vector_irq(void)
{
	rv_detach_trace_probe("opid", local_timer_entry, handle_vector_irq_entry);
	if (IS_ENABLED(CONFIG_IRQ_WORK))
		rv_detach_trace_probe("opid", irq_work_entry, handle_vector_irq_entry);
	if (IS_ENABLED(CONFIG_SMP)) {
		rv_detach_trace_probe("opid", reschedule_entry, handle_vector_irq_entry);
		rv_detach_trace_probe("opid", call_function_entry, handle_vector_irq_entry);
		rv_detach_trace_probe("opid", call_function_single_entry, handle_vector_irq_entry);
	}
}

#else
/* We assume irq_entry tracepoints are sufficient on other architectures */
static void attach_vector_irq(void) { }
static void detach_vector_irq(void) { }
#endif

static void handle_irq_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
	da_handle_event_opid(irq_disable_opid);
}

static void handle_irq_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
	da_handle_event_opid(irq_enable_opid);
}

static void handle_irq_entry(void *data, int irq, struct irqaction *action)
{
	da_handle_event_opid(irq_entry_opid);
}

static void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)
{
	da_handle_event_opid(preempt_disable_opid);
}

static void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
	da_handle_event_opid(preempt_enable_opid);
}

static void handle_sched_need_resched(void *data, struct task_struct *tsk, int cpu, int tif)
{
	/* The monitor's intitial state is not in_irq */
	if (this_cpu_read(hardirq_context))
		da_handle_event_opid(sched_need_resched_opid);
	else
		da_handle_start_event_opid(sched_need_resched_opid);
}

static void handle_sched_waking(void *data, struct task_struct *p)
{
	/* The monitor's intitial state is not in_irq */
	if (this_cpu_read(hardirq_context))
		da_handle_event_opid(sched_waking_opid);
	else
		da_handle_start_event_opid(sched_waking_opid);
}

static int enable_opid(void)
{
	int retval;

	retval = da_monitor_init_opid();
	if (retval)
		return retval;

	rv_attach_trace_probe("opid", irq_disable, handle_irq_disable);
	rv_attach_trace_probe("opid", irq_enable, handle_irq_enable);
	rv_attach_trace_probe("opid", irq_handler_entry, handle_irq_entry);
	rv_attach_trace_probe("opid", preempt_disable, handle_preempt_disable);
	rv_attach_trace_probe("opid", preempt_enable, handle_preempt_enable);
	rv_attach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
	rv_attach_trace_probe("opid", sched_waking, handle_sched_waking);
	attach_vector_irq();

	return 0;
}

static void disable_opid(void)
{
	rv_opid.enabled = 0;

	rv_detach_trace_probe("opid", irq_disable, handle_irq_disable);
	rv_detach_trace_probe("opid", irq_enable, handle_irq_enable);
	rv_detach_trace_probe("opid", irq_handler_entry, handle_irq_entry);
	rv_detach_trace_probe("opid", preempt_disable, handle_preempt_disable);
	rv_detach_trace_probe("opid", preempt_enable, handle_preempt_enable);
	rv_detach_trace_probe("opid", sched_set_need_resched_tp, handle_sched_need_resched);
	rv_detach_trace_probe("opid", sched_waking, handle_sched_waking);
	detach_vector_irq();

	da_monitor_destroy_opid();
}

/*
 * This is the monitor register section.
 */
static struct rv_monitor rv_opid = {
	.name = "opid",
	.description = "operations with preemption and irq disabled.",
	.enable = enable_opid,
	.disable = disable_opid,
	.reset = da_monitor_reset_all_opid,
	.enabled = 0,
};

static int __init register_opid(void)
{
	return rv_register_monitor(&rv_opid, &rv_sched);
}

static void __exit unregister_opid(void)
{
	rv_unregister_monitor(&rv_opid);
}

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.");
Loading