* Re: [PATCH] tools: Use fputc() calls in some functions
From: Andi Kleen @ 2026-07-23 13:47 UTC (permalink / raw)
To: Markus Elfring
Cc: acpica-devel, bpf, coresight, kvm, linux-acpi, linux-arm-kernel,
linux-gpio, linux-kselftest, linux-mm, linux-perf-users, linux-pm,
linux-trace-kernel, linuxppc-dev, mptcp, netdev,
platform-driver-x86, virtualization, Adrian Hunter, Alex Mastro,
Alex Williamson, Alexander Shishkin, Alexei Starovoitov,
Andrew Lunn, Andrew Morton, Andrii Nakryiko, Ankur Arora,
Arnaldo Carvalho de Melo, Bartosz Golaszewski, Bobby Eshleman,
Christian Bornträger, Christophe Leroy, Chun-Tse Shao,
Claudio Imbrenda, Costa Shulyupin, Crystal Wood, Daniel Borkmann,
Daniel Lezcano, David Hildenbrand, David Matlack, David S. Miller,
Donald Hunter, Eduard Zingerman, Emil Tsalapatis, Eric Dumazet,
Gabriele Monaco, Geliang Tang, Ian Rogers, Ingo Molnar,
Ivan Pravdin, Jakub Kicinski, James Clark, Janosch Frank,
Jason Xing, Jiri Olsa, Joe Damato, John Garry, Josh Poimboeuf,
Kaushlendra Kumar, Kent Gibson, Kumar Kartikeya Dwivedi,
Len Brown, Leo Yan, Lukasz Luba, Madhavan Srinivasan,
Mark Rutland, Martin KaFai Lau, Masami Hiramatsu, Mat Martineau,
Matthieu Baerts, Michael Ellerman, Mike Leach, Mina Almasry,
Mykyta Yatsenko, Nam Cao, Namhyung Kim, Nicholas Piggin,
Paolo Abeni, Paolo Bonzini, Pawel Chmielewski, Peter Zijlstra,
Quentin Monnet, Rafael J. Wysocki, Raghavendra Rao Ananta,
Saket Dumbre, Shuah Khan, Simon Horman, Song Liu,
Srinivas Pandruvada, Stanislav Fomichev, Stefano Garzarella,
Steven Rostedt, Suzuki K Poulose, Swapnil Sapkal,
Thomas Weißschuh, Tomas Glozar, Vipin Sharma,
Wander Lairson Costa, Will Deacon, Willem de Bruijn,
Willy Tarreau, Yonghong Song, Zhang Chujun, Zhang Rui, LKML,
kernel-janitors
In-Reply-To: <48f2d61f-d468-49b7-881e-f00777db073f@web.de>
On Thu, Jul 23, 2026 at 12:25:25PM +0200, Markus Elfring wrote:
> From: Markus Elfring <elfring@users.sourceforge.net>
> Date: Thu, 23 Jul 2026 11:13:30 +0200
> Subject: [PATCH] tools: Use fputc() calls in some functions
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> Single characters should occasionally be transferred into the output.
> Thus use the corresponding function “fputc” (or even “putchar”).
>
> The source code was transformed by using the Coccinelle software.
gcc already does this transformation automatically.
^ permalink raw reply
* [PATCH v3] ftrace: Add global mutex to serialize trace_parser access
From: Tengda Wu @ 2026-07-23 13:34 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mark Rutland, Mathieu Desnoyers, linux-trace-kernel, linux-kernel,
Tengda Wu
In ftrace, the trace_parser structure is allocated and initialized when
a trace file is opened, and is subsequently used across write and release
handlers to parse user input.
The affected handler paths and their specific functions are:
- Open paths: ftrace_regex_open(), ftrace_graph_open()
- Write paths: ftrace_regex_write(), ftrace_graph_write()
- Release paths: ftrace_regex_release(), ftrace_graph_release()
If userspace opens a trace file descriptor and shares it across multiple
threads, concurrent write calls will race on the parser's internal state,
specifically the 'idx', 'cont', and 'buffer' fields, leading to corrupted
input or undefined behavior.
Fix this by adding a global mutex, parser_lock, to serialize all access
to trace_parser across write and release paths, preventing concurrent
corruption of parser state.
Fixes: e704eff3ff51 ("ftrace: Have set_graph_function handle multiple functions in one write")
Fixes: 689fd8b65d66 ("tracing: trace parser support for function and graph")
Cc: stable@vger.kernel.org
Signed-off-by: Tengda Wu <wutengda@huaweicloud.com>
---
v3: Switch from per-parser lock to global parser_lock (v1 approach).
v2: https://lore.kernel.org/all/20260715081937.1469757-1-wutengda@huaweicloud.com/
v1: https://lore.kernel.org/all/20260713134640.708323-1-wutengda@huaweicloud.com/
kernel/trace/ftrace.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f93e34dd2328..ba27f31c6ff4 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1097,6 +1097,12 @@ struct ftrace_ops global_ops = {
FTRACE_OPS_FL_PID,
};
+/*
+ * parser_lock - Protects trace_parser state against concurrent operations.
+ * Held across trace_get_user() and subsequent buffer parsing to prevent races.
+ */
+DEFINE_MUTEX(parser_lock);
+
/*
* Used by the stack unwinder to know about dynamic ftrace trampolines.
*/
@@ -5842,6 +5848,8 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
/* iter->hash is a local copy, so we don't need regex_lock */
parser = &iter->parser;
+
+ guard(mutex)(&parser_lock);
read = trace_get_user(parser, ubuf, cnt, ppos);
if (read >= 0 && trace_parser_loaded(parser) &&
@@ -6984,12 +6992,14 @@ int ftrace_regex_release(struct inode *inode, struct file *file)
iter = file->private_data;
parser = &iter->parser;
+ mutex_lock(&parser_lock);
if (trace_parser_loaded(parser)) {
int enable = !(iter->flags & FTRACE_ITER_NOTRACE);
ftrace_process_regex(iter, parser->buffer,
parser->idx, enable);
}
+ mutex_unlock(&parser_lock);
trace_parser_put(parser);
@@ -7321,10 +7331,12 @@ ftrace_graph_release(struct inode *inode, struct file *file)
parser = &fgd->parser;
+ mutex_lock(&parser_lock);
if (trace_parser_loaded((parser))) {
ret = ftrace_graph_set_hash(fgd->new_hash,
parser->buffer);
}
+ mutex_unlock(&parser_lock);
trace_parser_put(parser);
@@ -7437,6 +7449,7 @@ ftrace_graph_write(struct file *file, const char __user *ubuf,
parser = &fgd->parser;
+ guard(mutex)(&parser_lock);
read = trace_get_user(parser, ubuf, cnt, ppos);
if (read >= 0 && trace_parser_loaded(parser) &&
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v4 8/8] selftests/verification: add tlob selftests
From: Gabriele Monaco @ 2026-07-23 13:29 UTC (permalink / raw)
To: wen.yang; +Cc: Nam Cao, linux-trace-kernel, linux-kernel
In-Reply-To: <4eb9a676efe90de8dfc1a9188d6ea81336e65e63.1783524627.git.wen.yang@linux.dev>
On Wed, 2026-07-08 at 23:38 +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
>
> Add seven ftrace-style test scripts for the tlob RV monitor under
> tools/testing/selftests/verification/test.d/tlob/. The tests cover
> uprobe binding management, budget violation detection, and per-state
> time accounting.
>
> Helper binaries tlob_target and tlob_sym are included in the same
> directory so the suite is self-contained. tlob_sym resolves ELF
> symbol offsets for uprobe registration; tlob_target provides busy-spin,
> sleep, and preempt workloads.
>
> ftracetest is updated to walk up the directory tree when searching for
> test.d/functions, so monitor subdirectories can be passed as the test
> directory without placing a dummy functions shim in each new directory.
>
> Signed-off-by: Wen Yang <wen.yang@linux.dev>
> ---
...
> +"$UPROBE_TARGET" 30000 & # busy mode: tlob_busy_work fires every 200 ms
> +busy_pid=$!
> +"$UPROBE_TARGET" 30000 sleep & # sleep mode: tlob_sleep_work fires every 200
> ms
> +sleep_pid=$!
> +sleep 0.05
> +
> +echo 1 > /sys/kernel/tracing/events/rv/error_env_tlob/enable
> +echo 1 > /sys/kernel/tracing/events/rv/detail_env_tlob/enable
> +echo 1 > /sys/kernel/tracing/tracing_on
> +echo 1 > monitors/tlob/enable
> +echo > /sys/kernel/tracing/trace
> +
> +# Binding A: 5 s budget on the busy probe - must not fire in 200 ms loops.
> +echo "p ${UPROBE_TARGET}:${busy_offset} ${busy_stop} threshold=5000000000" >
> "$TLOB_MONITOR"
> +# Binding B: 10 us budget on the sleep probe - fires on first invocation.
> +echo "p ${UPROBE_TARGET}:${sleep_offset} ${sleep_stop} threshold=10000" >
> "$TLOB_MONITOR"
> +
> +# Wait up to 2 s for error_env_tlob from binding B.
> +found=0; i=0
> +while [ "$i" -lt 20 ]; do
> + sleep 0.1
> + grep -q "error_env_tlob" /sys/kernel/tracing/trace && { found=1;
> break; }
> + i=$((i+1))
> +done
Be careful when starting tasks and making assertions (e.g. grep) before
killing them, in case those assertions fail, set -e would exit
immediately and we'll skip stopping the tasks.
I'd say whenever you start tlob_target (or anything else) you can do:
teardown() {
kill "$sleep_pid" 2>/dev/null || true; wait "$sleep_pid" 2>/dev/null || true
kill "$busy_pid" 2>/dev/null || true; wait "$busy_pid" 2>/dev/null || true
}
trap teardown EXIT
just to be safe. This will kill and wait again if you need teardown
before the end of the test, but that isn't an issue.
> +
> +echo "-${UPROBE_TARGET}:${busy_offset}" > "$TLOB_MONITOR" 2>/dev/null
> +echo "-${UPROBE_TARGET}:${sleep_offset}" > "$TLOB_MONITOR" 2>/dev/null
> +kill "$sleep_pid" 2>/dev/null || true; wait "$sleep_pid" 2>/dev/null || true
> +kill "$busy_pid" 2>/dev/null || true; wait "$busy_pid" 2>/dev/null || true
> +
> +echo 0 > monitors/tlob/enable
> +echo 0 > /sys/kernel/tracing/events/rv/error_env_tlob/enable
> +echo 0 > /sys/kernel/tracing/events/rv/detail_env_tlob/enable
> +
> +[ "$found" = "1" ]
> +# error_env_tlob payload: clock variable must be present.
> +# The event field can be "budget_exceeded" (hrtimer path) or the DA event
> +# name ("sleep", "preempt") depending on which fires first; don't constrain
> it.
> +grep "error_env_tlob" /sys/kernel/tracing/trace | head -n 1 | grep -q
> "clk_elapsed="
> +# detail_env_tlob must appear alongside the error.
> +grep -q "detail_env_tlob" /sys/kernel/tracing/trace
> +
> +echo > /sys/kernel/tracing/trace
> diff --git
> a/tools/testing/selftests/verification/test.d/tlob/uprobe_no_event.tc
> b/tools/testing/selftests/verification/test.d/tlob/uprobe_no_event.tc
> new file mode 100644
> index 000000000000..bb2eeef17019
> --- /dev/null
> +++ b/tools/testing/selftests/verification/test.d/tlob/uprobe_no_event.tc
> @@ -0,0 +1,19 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# description: Test tlob monitor no spurious events without active uprobe
> binding
> +# requires: tlob:monitor
> +
> +TLOB_MONITOR=monitors/tlob/monitor
This appears unused here.
Thanks,
Gabriele
^ permalink raw reply
* Re: [PATCH v3 2/6] rtla/osnoise: Record IPI count in osnoise top
From: Valentin Schneider @ 2026-07-23 13:28 UTC (permalink / raw)
To: Tomas Glozar
Cc: linux-kernel, linux-trace-kernel, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Costa Shulyupin,
Crystal Wood, John Kacur, Ivan Pravdin, Jonathan Corbet
In-Reply-To: <CAP4=nvSsYuibT7Oq-hAF9b=dZx==FcR1bK5d3zNmYcWgAZmCGQ@mail.gmail.com>
On 23/07/26 13:32, Tomas Glozar wrote:
> čt 23. 7. 2026 v 13:32 odesílatel Tomas Glozar <tglozar@redhat.com> napsal:
>> >
>> > This uses the newly added -i cmdline option.
>> >
>>
>> This is now named --irq, right?
>
> s/irq/ipi/
>
Woops, yes indeed, rebase fail!
>>
>> Tomas
^ permalink raw reply
* Re: [PATCH v4 0/8] rv/tlob: Add task latency over budget RV monitor
From: Gabriele Monaco @ 2026-07-23 11:53 UTC (permalink / raw)
To: wen.yang; +Cc: linux-trace-kernel, linux-kernel
In-Reply-To: <cover.1783524627.git.wen.yang@linux.dev>
On Wed, 2026-07-08 at 23:38 +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
Usually, within tracing, we try to make the commit titles consistent by
capitalising them after the tag (as you did only for the cover letter).
Also try not to introduce new tags, we have poor consistency, but I prefer only
to keep rv: when you modify kernel-side code (all your patches besides the
selftests one, which is correct).
Try to follow what you see with git log --oneline <modified files>
Overall:
rv/da: introduce DA_MON_ALLOCATION_STRATEGY
rv: add generic uprobe infrastructure for RV monitors
rv/tlob: add tlob model DOT file
...
becomes
rv: Introduce DA_MON_ALLOCATION_STRATEGY
rv: Add generic uprobe infrastructure for RV monitors
rv: Add tlob model DOT file
...
(note the uppercase A and I and the use of rv: , please apply these to other
commits too).
Thanks,
Gabriele
> This series introduces tlob (task latency over budget), a per-task
> hybrid automaton RV monitor that measures elapsed wall-clock time across
> a user-delimited code section and emits an error when the elapsed time
> exceeds a configurable budget.
>
> The monitor tracks three states (running, waiting, sleeping) driven by
> sched_switch and sched_wakeup tracepoints. A single clock invariant,
> clk_elapsed < BUDGET_NS(), is enforced by a per-task hrtimer
> (HRTIMER_MODE_REL_HARD). On expiry, error_env_tlob is emitted,
> followed by detail_env_tlob carrying a per-state time breakdown
> (running_ns, waiting_ns, sleeping_ns).
>
> Tasks are registered for monitoring by writing to the tracefs monitor
> file:
>
> echo "p /path/to/binary:OFFSET_START OFFSET_STOP threshold=NS" \
> > /sys/kernel/tracing/rv/monitors/tlob/monitor
>
> Two uprobes delimit the measured section without modifying the target
> binary. Multiple uprobe pairs can be active simultaneously.
>
> Series structure
> ----------------
> Patch 1: rv/da: introduce DA_MON_ALLOCATION_STRATEGY
> Compile-time allocation strategy selector for per-object DA storage.
> Three strategies: DA_ALLOC_AUTO (default kmalloc), DA_ALLOC_POOL
> (pre-allocated llist pool), DA_ALLOC_MANUAL (caller-managed).
>
> Patch 2: rv: add generic uprobe infrastructure for RV monitors
> Thin wrapper over uprobe_consumer for path resolution, registration,
> and safe synchronous teardown.
>
> Patch 3: rv/tlob: add tlob model DOT file
> Graphviz DOT specification of the tlob hybrid automaton.
>
> Patch 4: rv/ha: fix ha_invariant_passed_ns silent bypass
> Without this fix, ha_invariant_passed_ns() returns 0 on first call and
> leaves env_store at U64_MAX. Every subsequent state transition resets
> the hrtimer to the full budget instead of the remaining time, so the
> budget never expires for tasks that transition between states. This
> causes tlob's detail_sleeping and detail_waiting selftests to hang.
>
> This patch is a minimal fix in the current dual-representation framework.
> Nam's series [1] refactors ha_monitor.h to a single-representation
> model; once it lands, tlob will need to be updated to the new API and
> this patch will be superseded by Gabriele's planned framework-level
> initialisation improvement. We carry it here to keep the series
> self-contained and testable.
>
> [1]
> https://lore.kernel.org/lkml/08188c28f274da63a3f8549add3086a92aef45e5.1780908661.git.namcao@linutronix.de
>
> Patches 5: rv/ha: make da_monitor_reset_hook and EVENT_NONE_LBL
> #ifndef guards for da_monitor_reset_hook and EVENT_NONE_LBL.
>
> Patch 6: rv/tlob: add tlob hybrid automaton monitor
> Main tlob implementation.
>
> Patches 7-8: Tests
> KUnit tests for the uprobe-line parser; seven ftrace-style selftests.
>
> Changes since v3
> ----------------
> Patch 1 (rv/da):
> - Pool redesigned from spinlock+pointer-stack to lock-free llist
> (cmpxchg-based): pool release from RCU callback context is now safe
> without acquiring a lock
> - DA_MON_POOL_SIZE alone selects pool mode; no separate
> DA_MON_ALLOCATION_STRATEGY define required for monitors
> - All strategy dispatch uses plain C if() (no ifdeffery in functions)
> - da_monitor_destroy_pool() calls da_monitor_reset_all() and
> da_monitor_sync_hook() before the hash iteration, preventing hrtimer
> UAF when timer callbacks are still in flight
>
> Patch 2 (rv_uprobe):
> - struct rv_uprobe now embeds struct uprobe_consumer directly; no
> separate heap allocation per probe, no rv_uprobe_free()
> - path_put() moved after uprobe_register() to keep the inode
> referenced across the call as required by the uprobe API
>
> Patch 6 (tlob monitor):
> - da_get_target_by_id() wrapped in scoped_guard(rcu) in
> tlob_start_task(): hash_for_each_possible_rcu() requires an explicit
> RCU read-side CS on PREEMPT_RT where spinlock_t does not provide one
> - tlob_monitor_read() releases mutex before simple_read_from_buffer()
> to avoid holding the mutex across copy_to_user()
> - kmem_cache replaced with a pre-allocated llist pool for
> tlob_task_state, eliminating GFP_ATOMIC in the measurement window
> - EXPORT_SYMBOL_IF_KUNIT added for tlob_parse_uprobe_line() and
> tlob_parse_remove_line() (required when CONFIG_TLOB_KUNIT_TEST=m)
> - tlob_parse_remove_line() rejects negative offsets
> - Unnecessary includes removed; Kconfig entry placed after the
> deadline monitors marker
>
> Patch 7 (KUnit):
> - Tests now call tlob_parse_uprobe_line() and tlob_parse_remove_line()
> directly; no tracepoint enrollment, no uprobe or filesystem state
>
> Patch 8 (selftests):
> - ftracetest walk-up algorithm (Gabriele's suggestion) incorporated;
> the separate verificationtest-ktap fix patch is dropped and the
> series shrinks from 9 to 8 patches
> - All test scripts use "! cmd || false" throughout
>
> Testing
> -------
> Tested on x86_64 with CONFIG_PREEMPT_RT=y (kernel 7.1 + virtme-ng):
> - All KUnit tests pass
> - All 7 selftests pass (uprobe_bind, uprobe_violation, uprobe_no_event,
> uprobe_multi, uprobe_detail_{running,sleeping,waiting})
>
>
> Wen Yang (8):
> rv/da: introduce DA_MON_ALLOCATION_STRATEGY
> rv: add generic uprobe infrastructure for RV monitors
> rv/tlob: add tlob model DOT file
> rv/ha: fix ha_invariant_passed_ns silent bypass of invariant check
> rv/ha: make da_monitor_reset_hook and EVENT_NONE_LBL overridable
> rv/tlob: add tlob hybrid automaton monitor
> rv/tlob: add KUnit tests for the tlob monitor
> selftests/verification: add tlob selftests
>
> Documentation/trace/rv/index.rst | 1 +
> Documentation/trace/rv/monitor_tlob.rst | 177 ++++
> include/rv/da_monitor.h | 247 ++++-
> include/rv/ha_monitor.h | 24 +-
> include/rv/rv_uprobe.h | 93 ++
> kernel/trace/rv/Kconfig | 8 +
> kernel/trace/rv/Makefile | 3 +
> kernel/trace/rv/monitors/nomiss/nomiss.c | 6 +-
> kernel/trace/rv/monitors/tlob/.kunitconfig | 8 +
> kernel/trace/rv/monitors/tlob/Kconfig | 19 +
> kernel/trace/rv/monitors/tlob/tlob.c | 970 ++++++++++++++++++
> kernel/trace/rv/monitors/tlob/tlob.h | 153 +++
> kernel/trace/rv/monitors/tlob/tlob_kunit.c | 139 +++
> kernel/trace/rv/monitors/tlob/tlob_trace.h | 52 +
> kernel/trace/rv/rv_trace.h | 1 +
> kernel/trace/rv/rv_uprobe.c | 104 ++
> tools/testing/selftests/ftrace/ftracetest | 18 +-
> .../testing/selftests/verification/.gitignore | 2 +
> tools/testing/selftests/verification/Makefile | 19 +-
> .../verification/test.d/tlob/Makefile | 28 +
> .../test.d/tlob/run_tlob_tests.sh | 90 ++
> .../verification/test.d/tlob/tlob_sym.c | 209 ++++
> .../verification/test.d/tlob/tlob_target.c | 138 +++
> .../verification/test.d/tlob/uprobe_bind.tc | 37 +
> .../test.d/tlob/uprobe_detail_running.tc | 51 +
> .../test.d/tlob/uprobe_detail_sleeping.tc | 50 +
> .../test.d/tlob/uprobe_detail_waiting.tc | 66 ++
> .../verification/test.d/tlob/uprobe_multi.tc | 64 ++
> .../test.d/tlob/uprobe_no_event.tc | 19 +
> .../test.d/tlob/uprobe_violation.tc | 67 ++
> tools/verification/models/tlob.dot | 22 +
> 31 files changed, 2839 insertions(+), 46 deletions(-)
> create mode 100644 Documentation/trace/rv/monitor_tlob.rst
> create mode 100644 include/rv/rv_uprobe.h
> create mode 100644 kernel/trace/rv/monitors/tlob/.kunitconfig
> create mode 100644 kernel/trace/rv/monitors/tlob/Kconfig
> create mode 100644 kernel/trace/rv/monitors/tlob/tlob.c
> create mode 100644 kernel/trace/rv/monitors/tlob/tlob.h
> create mode 100644 kernel/trace/rv/monitors/tlob/tlob_kunit.c
> create mode 100644 kernel/trace/rv/monitors/tlob/tlob_trace.h
> create mode 100644 kernel/trace/rv/rv_uprobe.c
> create mode 100644 tools/testing/selftests/verification/test.d/tlob/Makefile
> create mode 100755
> tools/testing/selftests/verification/test.d/tlob/run_tlob_tests.sh
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/tlob_sym.c
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/tlob_target.c
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/uprobe_bind.tc
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/uprobe_detail_running.tc
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/uprobe_detail_sleeping.tc
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/uprobe_detail_waiting.tc
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/uprobe_multi.tc
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/uprobe_no_event.tc
> create mode 100644
> tools/testing/selftests/verification/test.d/tlob/uprobe_violation.tc
> create mode 100644 tools/verification/models/tlob.dot
^ permalink raw reply
* Re: [PATCH v3 2/6] rtla/osnoise: Record IPI count in osnoise top
From: Tomas Glozar @ 2026-07-23 11:32 UTC (permalink / raw)
To: Valentin Schneider
Cc: linux-kernel, linux-trace-kernel, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Costa Shulyupin,
Crystal Wood, John Kacur, Ivan Pravdin, Jonathan Corbet
In-Reply-To: <CAP4=nvRHyJr9vTm0oJe5Pp_nSa25De_zLB3P3hvUpofaKeVZrA@mail.gmail.com>
čt 23. 7. 2026 v 13:32 odesílatel Tomas Glozar <tglozar@redhat.com> napsal:
> >
> > This uses the newly added -i cmdline option.
> >
>
> This is now named --irq, right?
s/irq/ipi/
>
> Tomas
^ permalink raw reply
* Re: [PATCH v3 2/6] rtla/osnoise: Record IPI count in osnoise top
From: Tomas Glozar @ 2026-07-23 11:32 UTC (permalink / raw)
To: Valentin Schneider
Cc: linux-kernel, linux-trace-kernel, Steven Rostedt,
Masami Hiramatsu, Mathieu Desnoyers, Costa Shulyupin,
Crystal Wood, John Kacur, Ivan Pravdin, Jonathan Corbet
In-Reply-To: <20260715154553.2020891-3-vschneid@redhat.com>
st 15. 7. 2026 v 17:46 odesílatel Valentin Schneider
<vschneid@redhat.com> napsal:
>
> Leverage the ipi_send_cpu and ipi_send_cpumask trace events to record the
> count of IPIs sent to monitored CPUs. These interferences are already
> accounted by the IRQ count, but this split gives a better overall picture.
>
> This uses the newly added -i cmdline option.
>
This is now named --irq, right?
> Signed-off-by: Valentin Schneider <vschneid@redhat.com>
> ---
> tools/tracing/rtla/src/osnoise_top.c | 116 ++++++++++++++++++++++++++-
> 1 file changed, 115 insertions(+), 1 deletion(-)
>
> [truncated]
Tomas
^ permalink raw reply
* [PATCH] tools: Use fputc() calls in some functions
From: Markus Elfring @ 2026-07-23 10:25 UTC (permalink / raw)
To: acpica-devel, bpf, coresight, kvm, linux-acpi, linux-arm-kernel,
linux-gpio, linux-kselftest, linux-mm, linux-perf-users, linux-pm,
linux-trace-kernel, linuxppc-dev, mptcp, netdev,
platform-driver-x86, virtualization, Adrian Hunter, Alex Mastro,
Alex Williamson, Alexander Shishkin, Alexei Starovoitov,
Andi Kleen, Andrew Lunn, Andrew Morton, Andrii Nakryiko,
Ankur Arora, Arnaldo Carvalho de Melo, Bartosz Golaszewski,
Bobby Eshleman, Christian Bornträger, Christophe Leroy,
Chun-Tse Shao, Claudio Imbrenda, Costa Shulyupin, Crystal Wood,
Daniel Borkmann, Daniel Lezcano, David Hildenbrand, David Matlack,
David S. Miller, Donald Hunter, Eduard Zingerman, Emil Tsalapatis,
Eric Dumazet, Gabriele Monaco, Geliang Tang, Ian Rogers,
Ingo Molnar, Ivan Pravdin, Jakub Kicinski, James Clark,
Janosch Frank, Jason Xing, Jiri Olsa, Joe Damato, John Garry,
Josh Poimboeuf, Kaushlendra Kumar, Kent Gibson,
Kumar Kartikeya Dwivedi, Len Brown, Leo Yan, Lukasz Luba,
Madhavan Srinivasan, Mark Rutland, Martin KaFai Lau,
Masami Hiramatsu, Mat Martineau, Matthieu Baerts,
Michael Ellerman, Mike Leach, Mina Almasry, Mykyta Yatsenko,
Nam Cao, Namhyung Kim, Nicholas Piggin, Paolo Abeni,
Paolo Bonzini, Pawel Chmielewski, Peter Zijlstra, Quentin Monnet,
Rafael J. Wysocki, Raghavendra Rao Ananta, Saket Dumbre,
Shuah Khan, Simon Horman, Song Liu, Srinivas Pandruvada,
Stanislav Fomichev, Stefano Garzarella, Steven Rostedt,
Suzuki K Poulose, Swapnil Sapkal, Thomas Weißschuh,
Tomas Glozar, Vipin Sharma, Wander Lairson Costa, Will Deacon,
Willem de Bruijn, Willy Tarreau, Yonghong Song, Zhang Chujun,
Zhang Rui
Cc: LKML, kernel-janitors
From: Markus Elfring <elfring@users.sourceforge.net>
Date: Thu, 23 Jul 2026 11:13:30 +0200
Subject: [PATCH] tools: Use fputc() calls in some functions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Single characters should occasionally be transferred into the output.
Thus use the corresponding function “fputc” (or even “putchar”).
The source code was transformed by using the Coccinelle software.
See also:
Improving output for single characters (with SmPL)?
https://lore.kernel.org/kernel-janitors/202dfb21-f5a2-4550-8a1f-aa07d84db345@web.de/
Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
tools/bpf/bpftool/common.c | 4 ++--
tools/bpf/bpftool/main.c | 2 +-
tools/bpf/bpftool/netlink_dumper.h | 6 +++---
tools/bpf/bpftool/prog.c | 2 +-
tools/gpio/gpio-event-mon.c | 2 +-
tools/gpio/gpio-hammer.c | 4 ++--
tools/gpio/lsgpio.c | 4 ++--
tools/include/nolibc/err.h | 2 +-
tools/mm/page_owner_sort.c | 2 +-
tools/net/ynl/ynltool/main.c | 4 ++--
tools/objtool/builtin-check.c | 2 +-
tools/objtool/disas.c | 2 +-
tools/perf/builtin-c2c.c | 10 +++++-----
tools/perf/builtin-daemon.c | 4 ++--
tools/perf/builtin-lock.c | 14 +++++++-------
tools/perf/builtin-script.c | 8 ++++----
tools/perf/ui/gtk/util.c | 6 +++---
tools/perf/ui/stdio/hist.c | 8 ++++----
tools/perf/util/cs-etm.c | 2 +-
tools/perf/util/debug.c | 2 +-
| 6 +++---
tools/perf/util/intel-pt-decoder/intel-pt-log.c | 8 ++++----
tools/perf/util/libbfd.c | 2 +-
tools/perf/util/session.c | 4 ++--
tools/perf/util/stat-display.c | 12 ++++++------
tools/perf/util/values.c | 4 ++--
tools/power/acpi/tools/acpidump/apdump.c | 2 +-
tools/power/x86/intel-speed-select/isst-display.c | 6 +++---
tools/power/x86/turbostat/turbostat.c | 4 ++--
.../testing/selftests/bpf/benchs/bench_htab_mem.c | 2 +-
tools/testing/selftests/bpf/jit_disasm_helpers.c | 2 +-
tools/testing/selftests/bpf/test_progs.c | 12 ++++++------
tools/testing/selftests/bpf/veristat.c | 2 +-
tools/testing/selftests/drivers/net/hw/ncdevmem.c | 2 +-
tools/testing/selftests/kvm/s390/keyop.c | 2 +-
tools/testing/selftests/net/mptcp/pm_nl_ctl.c | 2 +-
tools/testing/selftests/net/psock_tpacket.c | 4 ++--
tools/testing/selftests/net/txtimestamp.c | 10 +++++-----
.../testing/selftests/powerpc/nx-gzip/gunz_test.c | 2 +-
.../selftests/vfio/lib/include/libvfio/assert.h | 2 +-
tools/testing/selftests/vfio/lib/libvfio.c | 10 +++++-----
tools/testing/vsock/vsock_diag_test.c | 4 ++--
tools/thermal/tmon/sysfs.c | 2 +-
tools/thermal/tmon/tmon.c | 2 +-
tools/tracing/latency/latency-collector.c | 2 +-
tools/tracing/rtla/src/common.c | 2 +-
tools/tracing/rtla/src/utils.c | 2 +-
tools/usb/ffs-test.c | 2 +-
tools/verification/rv/src/in_kernel.c | 2 +-
49 files changed, 104 insertions(+), 104 deletions(-)
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c
index ef366ccc9650..3133058cfe40 100644
--- a/tools/bpf/bpftool/common.c
+++ b/tools/bpf/bpftool/common.c
@@ -53,7 +53,7 @@ void p_err(const char *fmt, ...)
} else {
fprintf(stderr, "Error: ");
vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
va_end(ap);
}
@@ -67,7 +67,7 @@ void p_info(const char *fmt, ...)
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
va_end(ap);
}
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index c91e1a6e1a1e..aecfc778625f 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -273,7 +273,7 @@ void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
if (!i)
/* nothing */;
else if (!(i % 16))
- fprintf(f, "\n");
+ fputc(f, '\n');
else if (!(i % 8))
fprintf(f, " ");
else
diff --git a/tools/bpf/bpftool/netlink_dumper.h b/tools/bpf/bpftool/netlink_dumper.h
index 96318106fb49..bf0919b4f52a 100644
--- a/tools/bpf/bpftool/netlink_dumper.h
+++ b/tools/bpf/bpftool/netlink_dumper.h
@@ -25,7 +25,7 @@
if (json_output) \
jsonw_start_object(json_wtr); \
else \
- fprintf(stdout, "{"); \
+ putchar('{'); \
}
#define NET_END_OBJECT_NESTED \
@@ -33,7 +33,7 @@
if (json_output) \
jsonw_end_object(json_wtr); \
else \
- fprintf(stdout, "}"); \
+ putchar('}'); \
}
#define NET_END_OBJECT \
@@ -47,7 +47,7 @@
if (json_output) \
jsonw_end_object(json_wtr); \
else \
- fprintf(stdout, "\n"); \
+ putchar('\n'); \
}
#define NET_START_ARRAY(name, fmt_str) \
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index a9f730d407a9..d27cb9d49860 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -1266,7 +1266,7 @@ static void hex_print(void *data, unsigned int size, FILE *f)
fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
}
- fprintf(f, "\n");
+ fputc(f, '\n');
}
}
diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c
index b70813b0bf8e..e12e14780c0e 100644
--- a/tools/gpio/gpio-event-mon.c
+++ b/tools/gpio/gpio-event-mon.c
@@ -121,7 +121,7 @@ int monitor_device(const char *device_name,
default:
fprintf(stdout, "unknown event");
}
- fprintf(stdout, "\n");
+ putchar('\n');
i++;
if (i == loops)
diff --git a/tools/gpio/gpio-hammer.c b/tools/gpio/gpio-hammer.c
index ba0866eb3581..b76c94fee04d 100644
--- a/tools/gpio/gpio-hammer.c
+++ b/tools/gpio/gpio-hammer.c
@@ -87,7 +87,7 @@ int hammer_device(const char *device_name, unsigned int *lines, int num_lines,
if (j == sizeof(swirr) - 1)
j = 0;
- fprintf(stdout, "[");
+ putchar('[');
for (i = 0; i < num_lines; i++) {
fprintf(stdout, "%u: %d", lines[i],
gpiotools_test_bit(values.bits, i));
@@ -101,7 +101,7 @@ int hammer_device(const char *device_name, unsigned int *lines, int num_lines,
if (loops && iteration == loops)
break;
}
- fprintf(stdout, "\n");
+ putchar('\n');
ret = 0;
exit_close_error:
diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c
index 52a0be45410c..abb2c6fbc3d7 100644
--- a/tools/gpio/lsgpio.c
+++ b/tools/gpio/lsgpio.c
@@ -152,9 +152,9 @@ int list_device(const char *device_name)
if (linfo.flags) {
fprintf(stdout, " [");
print_attributes(&linfo);
- fprintf(stdout, "]");
+ putchar(']');
}
- fprintf(stdout, "\n");
+ putchar('\n');
}
diff --git a/tools/include/nolibc/err.h b/tools/include/nolibc/err.h
index e22ae87a7289..89c9a3d3bc77 100644
--- a/tools/include/nolibc/err.h
+++ b/tools/include/nolibc/err.h
@@ -27,7 +27,7 @@ void vwarnx(const char *fmt, va_list args)
{
fprintf(stderr, "%s: ", program_invocation_short_name);
vfprintf(stderr, fmt, args);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
static __attribute__((unused))
diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c
index 35d3d254941c..0293a838211e 100644
--- a/tools/mm/page_owner_sort.c
+++ b/tools/mm/page_owner_sort.c
@@ -917,7 +917,7 @@ int main(int argc, char **argv)
}
if (cull & CULL_STACKTRACE)
fprintf(fout, ":\n%s", list[i].stacktrace);
- fprintf(fout, "\n");
+ fputc(fout, '\n');
}
}
diff --git a/tools/net/ynl/ynltool/main.c b/tools/net/ynl/ynltool/main.c
index 5d0f428eed0a..245d7f58c8fb 100644
--- a/tools/net/ynl/ynltool/main.c
+++ b/tools/net/ynl/ynltool/main.c
@@ -156,7 +156,7 @@ void p_err(const char *fmt, ...)
} else {
fprintf(stderr, "Error: ");
vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
va_end(ap);
}
@@ -170,7 +170,7 @@ void p_info(const char *fmt, ...)
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
va_end(ap);
}
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 118c3de2f293..b6d27d326b8a 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -297,7 +297,7 @@ int make_backup(void)
fprintf(stderr, " %s", arg);
}
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
return 0;
}
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index e6a54a83605c..339d1d5690c4 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -537,7 +537,7 @@ void disas_print_insn(FILE *stream, struct disas_context *dctx,
return;
if (strcmp(format, "\n") == 0) {
- fprintf(stream, "\n");
+ fputc(stream, '\n');
return;
}
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index c9584dbedf77..fc9b6b18eded 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -2511,7 +2511,7 @@ static void print_cacheline(struct c2c_hists *c2c_hists,
hists__fprintf_headers(&c2c_hists->hists, out);
once = true;
} else {
- fprintf(out, "\n");
+ fputc(out, '\n');
}
fprintf(out, " ----------------------------------------------------------------------\n");
@@ -2590,15 +2590,15 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
setup_pager();
print_c2c__display_stats(out);
- fprintf(out, "\n");
+ fputc(out, '\n');
print_shared_cacheline_info(out);
- fprintf(out, "\n");
+ fputc(out, '\n');
print_c2c_info(out, session);
if (c2c.stats_only)
return;
- fprintf(out, "\n");
+ fputc(out, '\n');
fprintf(out, "=================================================\n");
fprintf(out, " Shared Data Cache Line Table \n");
fprintf(out, "=================================================\n");
@@ -2606,7 +2606,7 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
hists__fprintf(&c2c.hists.hists, true, 0, 0, 0, stdout, true);
- fprintf(out, "\n");
+ fputc(out, '\n');
fprintf(out, "=================================================\n");
fprintf(out, " Shared Cache Line Distribution Pareto \n");
fprintf(out, "=================================================\n");
diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c
index c4632577d129..7cb20b3a03e4 100644
--- a/tools/perf/builtin-daemon.c
+++ b/tools/perf/builtin-daemon.c
@@ -691,7 +691,7 @@ static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out)
/* session up time */
csv_sep, (uint64_t)((curr - daemon->start) / 60));
- fprintf(out, "\n");
+ fputc(out, '\n');
} else {
fprintf(out, "[%d:daemon] base: %s\n", getpid(), daemon->base);
if (cmd->list.verbose) {
@@ -730,7 +730,7 @@ static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out)
/* session up time */
csv_sep, (uint64_t)((curr - session->start) / 60));
- fprintf(out, "\n");
+ fputc(out, '\n');
} else {
fprintf(out, "[%d:%s] perf record %s\n",
session->pid, session->name, session->run);
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index d5c0d55cb82d..3706aff25883 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -1319,9 +1319,9 @@ static void print_result(void)
list_for_each_entry(key, &lock_keys, list) {
key->print(key, st);
- fprintf(lock_output, " ");
+ fputc(lock_output, ' ');
}
- fprintf(lock_output, "\n");
+ fputc(lock_output, '\n');
if (++printed >= print_nr_entries)
break;
@@ -1598,7 +1598,7 @@ static void print_header_csv(const char *sep)
fprintf(lock_output, "%s%s %s", "type", sep, "caller");
if (verbose > 0)
fprintf(lock_output, "%s %s", sep, "stacktrace");
- fprintf(lock_output, "\n");
+ fputc(lock_output, '\n');
break;
case LOCK_AGGR_ADDR:
fprintf(lock_output, "%s%s %s%s %s\n", "address", sep, "symbol", sep, "type");
@@ -1629,7 +1629,7 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat
list_for_each_entry(key, &lock_keys, list) {
key->print(key, st);
- fprintf(lock_output, " ");
+ fputc(lock_output, ' ');
}
switch (aggr_mode) {
@@ -1687,7 +1687,7 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
case LOCK_AGGR_CALLER:
fprintf(lock_output, "%s%s %s", get_type_flags_name(st->flags), sep, st->name);
if (verbose <= 0)
- fprintf(lock_output, "\n");
+ fputc(lock_output, '\n');
break;
case LOCK_AGGR_TASK:
pid = st->addr;
@@ -1721,7 +1721,7 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s
get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf);
}
- fprintf(lock_output, "\n");
+ fputc(lock_output, '\n');
}
}
@@ -1781,7 +1781,7 @@ static void print_footer_csv(int total, int bad, struct lock_contention_fails *f
for (i = 0; i < BROKEN_MAX; i++)
fprintf(lock_output, "%s bad_%s=%d", sep, name[i], bad_hist[i]);
}
- fprintf(lock_output, "\n");
+ fputc(lock_output, '\n');
}
static void print_footer(int total, int bad, struct lock_contention_fails *fails)
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index f91d8b1fbd01..2b202018ba59 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -2563,7 +2563,7 @@ static void process_event(struct perf_script *script,
perf_sample__fprintf_ipc(sample, evsel, fp);
- fprintf(fp, "\n");
+ fputc(fp, '\n');
if (PRINT_FIELD(SRCCODE)) {
if (map__fprintf_srccode(al->map, al->addr, stdout,
@@ -2817,7 +2817,7 @@ static int process_deferred_sample_event(const struct perf_tool *tool,
cursor, symbol_conf.bt_stop_list, fp);
}
- fprintf(fp, "\n");
+ fputc(fp, '\n');
if (verbose > 0)
fflush(fp);
@@ -3273,11 +3273,11 @@ static int list_available_languages_cb(struct scripting_ops *ops, const char *sp
static void list_available_languages(void)
{
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
fprintf(stderr, "Scripting language extensions (used in "
"perf script -s [spec:]script.[spec]):\n\n");
script_spec__for_each(&list_available_languages_cb);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
/* Find script file relative to current directory or exec path */
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c
index c47f5c387838..037ebc47f7dc 100644
--- a/tools/perf/ui/gtk/util.c
+++ b/tools/perf/ui/gtk/util.c
@@ -37,7 +37,7 @@ static int perf_gtk__error(const char *format, va_list args)
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Error:\n");
vfprintf(stderr, format, args);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
return -1;
}
@@ -62,7 +62,7 @@ static int perf_gtk__warning_info_bar(const char *format, va_list args)
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
return -1;
}
@@ -83,7 +83,7 @@ static int perf_gtk__warning_statusbar(const char *format, va_list args)
vasprintf(&msg, format, args) < 0) {
fprintf(stderr, "Warning:\n");
vfprintf(stderr, format, args);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
return -1;
}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 8c4c8925df2c..044d2aa3b312 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -691,7 +691,7 @@ static int hists__fprintf_hierarchy_headers(struct hists *hists,
}
next_line:
- fprintf(fp, "\n");
+ fputc(fp, '\n');
}
fprintf(fp, "# ");
@@ -783,7 +783,7 @@ hists__fprintf_standard_headers(struct hists *hists,
if (line)
fprintf(fp, "# ");
fprintf_line(hists, hpp, line, fp);
- fprintf(fp, "\n");
+ fputc(fp, '\n');
}
if (sep)
@@ -806,10 +806,10 @@ hists__fprintf_standard_headers(struct hists *hists,
width = fmt->width(fmt, hpp, hists);
for (i = 0; i < width; i++)
- fprintf(fp, ".");
+ fputc(fp, '.');
}
- fprintf(fp, "\n");
+ fputc(fp, '\n');
fprintf(fp, "#\n");
return hpp_list->nr_header_lines + 2;
}
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 95e3ec1171ac..d4534b489252 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -912,7 +912,7 @@ static void cs_etm__dump_event(struct cs_etm_queue *etmq,
const char *color = PERF_COLOR_BLUE;
size_t buffer_used = 0;
- fprintf(stdout, "\n");
+ putchar('\n');
color_fprintf(stdout, color,
". ... CoreSight %s Trace data: size %#zx bytes\n",
cs_etm_decoder__get_name(etmq->decoder), buffer->size);
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 6b5ffe81f141..5429984ab49c 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -350,7 +350,7 @@ void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size)
fprintf(file, " #%zd %p ", i, stackdump[i]);
map__fprintf_srcline(al.map, al.addr, "", file);
- fprintf(file, "\n");
+ fputc(file, '\n');
addr_location__exit(&al);
}
thread__put(thread);
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index e90e541f546b..e3a2a6e52ef0 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -2454,7 +2454,7 @@ static void __print_pmu_caps(FILE *fp, int nr_caps, char **caps, char *pmu_name)
delimiter = ", ";
}
- fprintf(fp, "\n");
+ fputc(fp, '\n');
}
static void print_cpu_pmu_caps(struct feat_fd *ff, FILE *fp)
@@ -2513,7 +2513,7 @@ static void print_pmu_mappings(struct feat_fd *ff, FILE *fp)
pmu_num--;
}
- fprintf(fp, "\n");
+ fputc(fp, '\n');
if (!pmu_num)
return;
@@ -4349,7 +4349,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
fprintf(fp, "%s ", feat_ops[bit].name);
}
- fprintf(fp, "\n");
+ fputc(fp, '\n');
return 0;
}
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-log.c b/tools/perf/util/intel-pt-decoder/intel-pt-log.c
index ef55d6232cf0..0a35493b854a 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-log.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-log.c
@@ -70,14 +70,14 @@ static void intel_pt_print_data(const unsigned char *buf, int len, uint64_t pos,
int i;
for (i = 0; i < indent; i++)
- fprintf(f, " ");
+ fputc(f, ' ');
fprintf(f, " %08" PRIx64 ": ", pos);
for (i = 0; i < len; i++)
fprintf(f, " %02x", buf[i]);
for (; i < 16; i++)
fprintf(f, " ");
- fprintf(f, " ");
+ fputc(f, ' ');
}
static void intel_pt_print_no_data(uint64_t pos, int indent)
@@ -85,12 +85,12 @@ static void intel_pt_print_no_data(uint64_t pos, int indent)
int i;
for (i = 0; i < indent; i++)
- fprintf(f, " ");
+ fputc(f, ' ');
fprintf(f, " %08" PRIx64 ": ", pos);
for (i = 0; i < 16; i++)
fprintf(f, " ");
- fprintf(f, " ");
+ fputc(f, ' ');
}
static ssize_t log_buf__write(void *cookie, const char *buf, size_t size)
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index d8241c7caac5..efbfdad8b330 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -603,7 +603,7 @@ int symbol__disassemble_bpf_libbfd(struct symbol *sym __maybe_unused,
} else
srcline = NULL;
- fprintf(s, "\n");
+ fputc(s, '\n');
prev_buf_size = buf_size;
fflush(s);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 3237870a1a34..ef2b3226187c 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1257,7 +1257,7 @@ int perf_event__process_finished_round(const struct perf_tool *tool __maybe_unus
struct ordered_events *oe)
{
if (dump_trace)
- fprintf(stdout, "\n");
+ putchar('\n');
return ordered_events__flush(oe, OE_FLUSH__ROUND);
}
@@ -4015,7 +4015,7 @@ int perf_event__process_id_index(const struct perf_tool *tool __maybe_unused,
fprintf(stdout, " machine_pid: %"PRI_ld64, e2->machine_pid);
fprintf(stdout, " vcpu: %"PRI_lu64"\n", e2->vcpu);
} else {
- fprintf(stdout, "\n");
+ putchar('\n');
}
}
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index f94f1324d24a..b90e3be95549 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -529,7 +529,7 @@ static void print_metric_json(struct perf_stat_config *config __maybe_unused,
}
}
if (!config->metric_only)
- fprintf(out, "}");
+ fputc(out, '}');
}
static void new_line_json(struct perf_stat_config *config, void *ctx)
@@ -1316,7 +1316,7 @@ static void print_header_std(struct perf_stat_config *config,
FILE *output = config->output;
int i;
- fprintf(output, "\n");
+ fputc(output, '\n');
fprintf(output, " Performance counter stats for ");
if (_target->bpf_str)
fprintf(output, "\'BPF program(s) %s", _target->bpf_str);
@@ -1333,7 +1333,7 @@ static void print_header_std(struct perf_stat_config *config,
else
fprintf(output, "thread id \'%s", _target->tid);
- fprintf(output, "\'");
+ fputc(output, '\'');
if (config->run_count > 1)
fprintf(output, " (%d runs)", config->run_count);
fprintf(output, ":\n\n");
@@ -1406,9 +1406,9 @@ static void print_table(struct perf_stat_config *config, FILE *output, double av
fprintf(output, " %17.9f (%+.9f) ", run, run - avg);
for (h = 0; h < n; h++)
- fprintf(output, "#");
+ fputc(output, '#');
- fprintf(output, "\n");
+ fputc(output, '\n');
}
fprintf(output, "\n%*s# Final result:\n", indent, "");
@@ -1428,7 +1428,7 @@ static void print_footer(struct perf_stat_config *config)
return;
if (!config->null_run)
- fprintf(output, "\n");
+ fputc(output, '\n');
if (config->run_count == 1) {
fprintf(output, " %17.9f seconds time elapsed", avg);
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c
index 6eaddfcf833e..2c5340a928dd 100644
--- a/tools/perf/util/values.c
+++ b/tools/perf/util/values.c
@@ -217,7 +217,7 @@ static void perf_read_values__display_pretty(FILE *fp,
fprintf(fp, "# %*s %*s", pidwidth, "PID", tidwidth, "TID");
for (j = 0; j < values->num_counters; j++)
fprintf(fp, " %*s", counterwidth[j], evsel__name(values->counters[j]));
- fprintf(fp, "\n");
+ fputc(fp, '\n');
for (i = 0; i < values->threads; i++) {
fprintf(fp, " %*d %*d", pidwidth, values->pid[i],
@@ -225,7 +225,7 @@ static void perf_read_values__display_pretty(FILE *fp,
for (j = 0; j < values->num_counters; j++)
fprintf(fp, " %*" PRIu64,
counterwidth[j], values->value[i][j]);
- fprintf(fp, "\n");
+ fputc(fp, '\n');
}
free(counterwidth);
}
diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c
index 72ad7915b04f..600d5b6e04a8 100644
--- a/tools/power/acpi/tools/acpidump/apdump.c
+++ b/tools/power/acpi/tools/acpidump/apdump.c
@@ -171,7 +171,7 @@ ap_dump_table_buffer(struct acpi_table_header *table,
acpi_ut_dump_buffer_to_file(gbl_output_file,
ACPI_CAST_PTR(u8, table), table_length,
DB_BYTE_DISPLAY, 0);
- fprintf(gbl_output_file, "\n");
+ fputc(gbl_output_file, '\n');
return (0);
}
diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c
index e4884eb02837..5d492780aad9 100644
--- a/tools/power/x86/intel-speed-select/isst-display.c
+++ b/tools/power/x86/intel-speed-select/isst-display.c
@@ -122,7 +122,7 @@ static void format_and_print(FILE *outf, int level, char *header, char *value)
if (level == 0) {
if (header)
- fprintf(outf, "{");
+ fputc(outf, '{');
else
fprintf(outf, "\n}\n");
@@ -138,7 +138,7 @@ static void format_and_print(FILE *outf, int level, char *header, char *value)
if (value) {
if (last_level != level)
- fprintf(outf, "\n");
+ fputc(outf, '\n');
fprintf(outf, "%s\"%s\": ", delimiters, header);
fprintf(outf, "\"%s\"", value);
@@ -156,7 +156,7 @@ static void format_and_print(FILE *outf, int level, char *header, char *value)
fprintf(outf, "\n%s}", delimiters);
}
if (abs(last_level - level) < 3)
- fprintf(outf, "\n");
+ fputc(outf, '\n');
if (header)
fprintf(outf, "%s\"%s\": {", delimiters,
header);
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index 4ad7cb1df5c5..bfa38199275f 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -9022,7 +9022,7 @@ void dump_cpuid_hypervisor(void)
dump_word_chars(ebx);
dump_word_chars(ecx);
dump_word_chars(edx);
- fprintf(outf, "\n");
+ fputc(outf, '\n');
}
void process_cpuid()
@@ -9692,7 +9692,7 @@ void topology_probe(bool startup)
fprintf(outf, " siblings");
for (ht_id = 0; ht_id <= MAX_HT_ID; ++ht_id)
fprintf(outf, " %d", cpus[i].ht_sibling_cpu_id[ht_id]);
- fprintf(outf, "\n");
+ fputc(outf, '\n');
}
}
diff --git a/tools/testing/selftests/bpf/benchs/bench_htab_mem.c b/tools/testing/selftests/bpf/benchs/bench_htab_mem.c
index 1ee217d97434..64b97a41af45 100644
--- a/tools/testing/selftests/bpf/benchs/bench_htab_mem.c
+++ b/tools/testing/selftests/bpf/benchs/bench_htab_mem.c
@@ -148,7 +148,7 @@ static const struct htab_mem_use_case *htab_mem_find_use_case_or_exit(const char
fprintf(stderr, "available use case:");
for (i = 0; i < ARRAY_SIZE(use_cases); i++)
fprintf(stderr, " %s", use_cases[i].name);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
exit(1);
}
diff --git a/tools/testing/selftests/bpf/jit_disasm_helpers.c b/tools/testing/selftests/bpf/jit_disasm_helpers.c
index 3558fe10e28c..58ea2a2ec13e 100644
--- a/tools/testing/selftests/bpf/jit_disasm_helpers.c
+++ b/tools/testing/selftests/bpf/jit_disasm_helpers.c
@@ -228,7 +228,7 @@ int get_jited_program_text(int fd, char *text, size_t text_sz)
for (pc = 0, i = 0; i < jited_funcs; ++i) {
fprintf(text_out, "func #%d:\n", i);
disasm_one_func(text_out, image + pc, func_lens[i]);
- fprintf(text_out, "\n");
+ fputc(text_out, '\n');
pc += func_lens[i];
}
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c
index 7ba82974ee78..b520be1bf7aa 100644
--- a/tools/testing/selftests/bpf/test_progs.c
+++ b/tools/testing/selftests/bpf/test_progs.c
@@ -383,7 +383,7 @@ static void print_test_result(const struct prog_test_def *test, const struct tes
else
fprintf(env.stdout_saved, "OK (SKIP: %d/%d)", skipped_cnt, subtests_cnt);
- fprintf(env.stdout_saved, "\n");
+ fputc(env.stdout_saved, '\n');
}
static void print_test_log(char *log_buf, size_t log_cnt)
@@ -391,7 +391,7 @@ static void print_test_log(char *log_buf, size_t log_cnt)
log_buf[log_cnt] = '\0';
fprintf(env.stdout_saved, "%s", log_buf);
if (log_buf[log_cnt - 1] != '\n')
- fprintf(env.stdout_saved, "\n");
+ fputc(env.stdout_saved, '\n');
}
static void print_subtest_name(int test_num, int subtest_num,
@@ -409,7 +409,7 @@ static void print_subtest_name(int test_num, int subtest_num,
if (result)
fprintf(env.stdout_saved, ":%s", result);
- fprintf(env.stdout_saved, "\n");
+ fputc(env.stdout_saved, '\n');
}
static void jsonw_write_log_message(json_writer_t *w, char *log_buf, size_t log_cnt)
@@ -1333,14 +1333,14 @@ void hexdump(const char *prefix, const void *buf, size_t len)
for (int i = 0; i < len; i++) {
if (!(i % 16)) {
if (i)
- fprintf(stdout, "\n");
+ putchar('\n');
fprintf(stdout, "%s", prefix);
}
if (i && !(i % 8) && (i % 16))
- fprintf(stdout, "\t");
+ putchar('\t');
fprintf(stdout, "%02X ", ((uint8_t *)(buf))[i]);
}
- fprintf(stdout, "\n");
+ putchar('\n');
}
static void sigint_handler(int signum)
diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c
index c9c257784ee3..61ea29bb35d7 100644
--- a/tools/testing/selftests/bpf/veristat.c
+++ b/tools/testing/selftests/bpf/veristat.c
@@ -1628,7 +1628,7 @@ static void dump(__u32 prog_id, enum dump_mode mode, const char *file_name, cons
printf("DUMP (%s) %s/%s:\n", mode == DUMP_JITED ? "JITED" : "XLATED", file_name, prog_name);
while (fgets(buf, sizeof(buf), fp))
fputs(buf, stdout);
- fprintf(stdout, "\n");
+ putchar('\n');
if (ferror(fp))
fprintf(stderr, "Failed to dump BPF prog with error: %d\n", errno);
diff --git a/tools/testing/selftests/drivers/net/hw/ncdevmem.c b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
index d96e8a3b5a65..7bdcc4114dba 100644
--- a/tools/testing/selftests/drivers/net/hw/ncdevmem.c
+++ b/tools/testing/selftests/drivers/net/hw/ncdevmem.c
@@ -135,7 +135,7 @@ static void pr_err(const char *fmt, ...)
if (errno != 0)
fprintf(stderr, ": %s", strerror(errno));
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
static struct memory_buffer *udmabuf_alloc(size_t size)
diff --git a/tools/testing/selftests/kvm/s390/keyop.c b/tools/testing/selftests/kvm/s390/keyop.c
index c7805e87d12c..120834f8ba0d 100644
--- a/tools/testing/selftests/kvm/s390/keyop.c
+++ b/tools/testing/selftests/kvm/s390/keyop.c
@@ -117,7 +117,7 @@ static void dump_sk(const unsigned char skeys[], const char *descr)
fprintf(stderr, "# %3d: ", i);
for (j = 0; j < 32; j++)
fprintf(stderr, "%02x ", skeys[i + j]);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
}
diff --git a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
index 78180da1efcc..40467e86bfd4 100644
--- a/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
+++ b/tools/testing/selftests/net/mptcp/pm_nl_ctl.c
@@ -208,7 +208,7 @@ static int capture_events(int fd, int event_group)
}
if (server_side)
fprintf(stderr, ",server_side:1");
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
} while (1);
return 0;
diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c
index 7caf3135448d..724da9222ad9 100644
--- a/tools/testing/selftests/net/psock_tpacket.c
+++ b/tools/testing/selftests/net/psock_tpacket.c
@@ -116,7 +116,7 @@ static int pfsocket(int ver)
static void status_bar_update(void)
{
if (total_packets % 10 == 0) {
- fprintf(stderr, ".");
+ fputc(stderr, '.');
fflush(stderr);
}
}
@@ -825,7 +825,7 @@ static int test_tpacket(int version, int type)
unmap_ring(sock, &ring);
close(sock);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
return 0;
}
diff --git a/tools/testing/selftests/net/txtimestamp.c b/tools/testing/selftests/net/txtimestamp.c
index 170be192f5c7..86c8d4945d73 100644
--- a/tools/testing/selftests/net/txtimestamp.c
+++ b/tools/testing/selftests/net/txtimestamp.c
@@ -200,10 +200,10 @@ static void __print_timestamp(const char *name, struct timespec *cur,
ts_delta = timespec_to_ns64(cur) - timespec_to_ns64(&ts_usr);
fprintf(stderr, " (USR +");
__print_ts_delta_formatted(ts_delta);
- fprintf(stderr, ")");
+ fputc(stderr, ')');
}
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
static void record_timestamp_usr(void)
@@ -254,7 +254,7 @@ static void print_timing_event(char *name, struct timing_event *te)
__print_ts_delta_formatted(te->min);
fprintf(stderr, ", max=");
__print_ts_delta_formatted(te->max);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
/* TODO: convert to check_and_print payload once API is stable */
@@ -271,7 +271,7 @@ static void print_payload(char *data, int len)
fprintf(stderr, "payload: ");
for (i = 0; i < len; i++)
fprintf(stderr, "%02hhx ", data[i]);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
@@ -933,7 +933,7 @@ int main(int argc, char **argv)
fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]);
fprintf(stderr, "payload: %u\n", cfg_payload_len);
fprintf(stderr, "server port: %u\n", dest_port);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
if (do_ipv4) {
if (cfg_do_listen)
diff --git a/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
index 7c23d3dd7d6d..412defe98271 100644
--- a/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
+++ b/tools/testing/selftests/powerpc/nx-gzip/gunz_test.c
@@ -363,7 +363,7 @@ int decompress_file(int argc, char **argv, void *devhandle)
goto err3;
fprintf(stderr, "%02x ", tmp[i]);
if (i == 5)
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
fprintf(stderr, "gzHeader MTIME, XFL, OS ignored\n");
diff --git a/tools/testing/selftests/vfio/lib/include/libvfio/assert.h b/tools/testing/selftests/vfio/lib/include/libvfio/assert.h
index 9fff88f6e4e1..682267bb71e3 100644
--- a/tools/testing/selftests/vfio/lib/include/libvfio/assert.h
+++ b/tools/testing/selftests/vfio/lib/include/libvfio/assert.h
@@ -11,7 +11,7 @@
#define VFIO_LOG_AND_EXIT(...) do { \
fprintf(stderr, " " __VA_ARGS__); \
- fprintf(stderr, "\n"); \
+ fputc(stderr, '\n'); \
exit(KSFT_FAIL); \
} while (0)
diff --git a/tools/testing/selftests/vfio/lib/libvfio.c b/tools/testing/selftests/vfio/lib/libvfio.c
index 3a3d1ed635c1..80a312f149a6 100644
--- a/tools/testing/selftests/vfio/lib/libvfio.c
+++ b/tools/testing/selftests/vfio/lib/libvfio.c
@@ -60,16 +60,16 @@ char **vfio_selftests_get_bdfs(int *argc, char *argv[], int *nr_bdfs)
}
fprintf(stderr, "Unable to determine which device(s) to use, skipping test.\n");
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
fprintf(stderr, "To pass the device address via environment variable:\n");
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
fprintf(stderr, " export VFIO_SELFTESTS_BDF=\"segment:bus:device.function\"\n");
fprintf(stderr, " %s [options]\n", argv[0]);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
fprintf(stderr, "To pass the device address(es) via argv:\n");
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
fprintf(stderr, " %s [options] segment:bus:device.function ...\n", argv[0]);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
exit(KSFT_SKIP);
}
diff --git a/tools/testing/vsock/vsock_diag_test.c b/tools/testing/vsock/vsock_diag_test.c
index 081e045f4696..cc903cdb45d9 100644
--- a/tools/testing/vsock/vsock_diag_test.c
+++ b/tools/testing/vsock/vsock_diag_test.c
@@ -86,7 +86,7 @@ static void print_vsock_addr(FILE *fp, unsigned int cid, unsigned int port)
fprintf(fp, "%u:", cid);
if (port == VMADDR_PORT_ANY)
- fprintf(fp, "*");
+ fputc(fp, '*');
else
fprintf(fp, "%u", port);
}
@@ -94,7 +94,7 @@ static void print_vsock_addr(FILE *fp, unsigned int cid, unsigned int port)
static void print_vsock_stat(FILE *fp, struct vsock_stat *st)
{
print_vsock_addr(fp, st->msg.vdiag_src_cid, st->msg.vdiag_src_port);
- fprintf(fp, " ");
+ fputc(fp, ' ');
print_vsock_addr(fp, st->msg.vdiag_dst_cid, st->msg.vdiag_dst_port);
fprintf(fp, " %s %s %s %u\n",
sock_type_str(st->msg.vdiag_type),
diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c
index cb1108bc9249..ebdb95819a76 100644
--- a/tools/thermal/tmon/sysfs.c
+++ b/tools/thermal/tmon/sysfs.c
@@ -522,7 +522,7 @@ int update_thermal_data()
}
if (tmon_log) {
- fprintf(tmon_log, "\n");
+ fputc(tmon_log, '\n');
fflush(tmon_log);
}
diff --git a/tools/thermal/tmon/tmon.c b/tools/thermal/tmon/tmon.c
index 7eb3216a27f4..9a972f73a2c0 100644
--- a/tools/thermal/tmon/tmon.c
+++ b/tools/thermal/tmon/tmon.c
@@ -199,7 +199,7 @@ static void prepare_logging(void)
fprintf(tmon_log, "%s%d ", ptdata.cdi[i].type,
ptdata.cdi[i].instance);
- fprintf(tmon_log, "\n");
+ fputc(tmon_log, '\n');
}
static struct option opts[] = {
diff --git a/tools/tracing/latency/latency-collector.c b/tools/tracing/latency/latency-collector.c
index ef97916e3873..3dc6fb9df926 100644
--- a/tools/tracing/latency/latency-collector.c
+++ b/tools/tracing/latency/latency-collector.c
@@ -1999,7 +1999,7 @@ static void scan_arguments(int argc, char *argv[])
"been enabled. Random sleep is intended for the following tracers:\n");
for (i = 0; random_tracers[i]; i++)
fprintf(stderr, "%s\n", random_tracers[i]);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
}
diff --git a/tools/tracing/rtla/src/common.c b/tools/tracing/rtla/src/common.c
index 8c7f5e75b2ec..e81e676d51d2 100644
--- a/tools/tracing/rtla/src/common.c
+++ b/tools/tracing/rtla/src/common.c
@@ -449,7 +449,7 @@ void common_usage(const char *tool, const char *mode,
fprintf(stderr, "%s [-h] ", mode);
print_msg_array(start_msgs);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
print_msg_array(common_options);
print_msg_array(opt_msgs);
diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c
index cb187e7d48d1..3532da2f2296 100644
--- a/tools/tracing/rtla/src/utils.c
+++ b/tools/tracing/rtla/src/utils.c
@@ -67,7 +67,7 @@ void fatal(const char *fmt, ...)
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
exit(ERROR);
}
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c
index 22b938fbdfb7..e993736ba0d6 100644
--- a/tools/usb/ffs-test.c
+++ b/tools/usb/ffs-test.c
@@ -562,7 +562,7 @@ empty_out_buf(struct thread *ignore, const void *buf, size_t nbytes)
fprintf(stderr, "%4zd:", len);
fprintf(stderr, " %02x", *p);
if (31 == (len % 32))
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
fflush(stderr);
errno = EILSEQ;
diff --git a/tools/verification/rv/src/in_kernel.c b/tools/verification/rv/src/in_kernel.c
index e6dea4040f8f..26132171f600 100644
--- a/tools/verification/rv/src/in_kernel.c
+++ b/tools/verification/rv/src/in_kernel.c
@@ -667,7 +667,7 @@ static void ikm_usage_print_reactors(void)
end = strstr(start, "\n");
}
- fprintf(stderr, "\n");
+ fputc(stderr, '\n');
}
/*
* ikm_usage - print usage
--
2.55.0
^ permalink raw reply related
* Re: [PATCH v1 v1 2/7] ntfs3: add namei tracepoints
From: liubaolin @ 2026-07-23 9:30 UTC (permalink / raw)
To: Steven Rostedt
Cc: almaz.alexandrovich, mhiramat, mathieu.desnoyers, linux-kernel,
ntfs3, linux-trace-kernel, liubaolin12138, Baolin Liu,
liubaolin12138
In-Reply-To: <20260721170952.26f79352@gandalf.local.home>
Dear Steve,
dentry->d_name.len is the qstr length and does not include the
trailing NUL, so it corresponds to strlen(dentry->d_name.name), rather
than strlen(dentry->d_name.name) + 1.
If you think the extra name_len field here is unnecessary, I can submit
a v2 patch to drop it. Looking forward to your feedback.
Best regards,
Baolin
在 2026/7/22 05:09, Steven Rostedt 写道:
> On Fri, 17 Jul 2026 11:22:43 +0800
> Baolin Liu <liubaolin12138@163.com> wrote:
>
>> +TRACE_EVENT(ntfs3_lookup,
>> + TP_PROTO(struct inode *dir, struct dentry *dentry),
>> + TP_ARGS(dir, dentry),
>> + TP_STRUCT__entry(
>> + __field(dev_t, dev)
>> + __field(unsigned long, parent_ino)
>> + __string(name, dentry->d_name.name)
>> + __field(unsigned int, name_len)
>> + ),
>> + TP_fast_assign(
>> + __entry->dev = dir->i_sb->s_dev;
>> + __entry->parent_ino = dir->i_ino;
>> + __assign_str(name);
>> + __entry->name_len = dentry->d_name.len;
>
> Is dentry->d_name.len not equal to strlen(dentry->d_name.name) + 1 ?
>
> -- Steve
>
>
>> + ),
>> + TP_printk("dev=(%d,%d) parent=%lu name=%s len=%u",
>> + MAJOR(__entry->dev), MINOR(__entry->dev),
>> + __entry->parent_ino, __get_str(name),
>> + __entry->name_len)
>> +);
^ permalink raw reply
* [PATCH RESEND 3/3] mm: trace: name protection key encoding bits
From: Meijing Zhao @ 2026-07-23 9:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mathieu Desnoyers, Andrew Morton, linux-trace-kernel, linux-mm,
linux-kernel, Meijing Zhao
In-Reply-To: <20260723092905.761037-1-zhaomeijing100@gmail.com>
From: Meijing Zhao <zhaomeijing@lixiang.com>
Protection keys are encoded in architecture-specific HIGH_ARCH_* VMA
flag bits. show_vma_flags(), which is used by VMA tracepoints and %pGv,
does not name those bits, leaving them as raw hexadecimal values.
Name the protection-key encoding bits pkey_bit0 through pkey_bit4 under
CONFIG_ARCH_HAS_PKEYS. The names make clear that these are bits of one
protection-key value rather than independent protection keys. For
example, protection key 3 is represented as:
pkey_bit0|pkey_bit1
Honor CONFIG_ARCH_PKEY_BITS when exposing bit 3 and bit 4 so only bits
provided by the architecture are included.
Signed-off-by: Meijing Zhao <zhaomeijing@lixiang.com>
---
include/trace/events/mmflags.h | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index 2d76daacb70c..c8b8a980f8c7 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -204,6 +204,24 @@ IF_HAVE_PG_ARCH_3(arch_3)
# define IF_HAVE_VM_DROPPABLE(flag, name)
#endif
+#ifdef CONFIG_ARCH_HAS_PKEYS
+# define IF_HAVE_VM_PKEY(flag, name) {flag, name},
+#if CONFIG_ARCH_PKEY_BITS > 3
+# define IF_HAVE_VM_PKEY3(flag, name) {flag, name},
+#else
+# define IF_HAVE_VM_PKEY3(flag, name)
+#endif
+#if CONFIG_ARCH_PKEY_BITS > 4
+# define IF_HAVE_VM_PKEY4(flag, name) {flag, name},
+#else
+# define IF_HAVE_VM_PKEY4(flag, name)
+#endif
+#else
+# define IF_HAVE_VM_PKEY(flag, name)
+# define IF_HAVE_VM_PKEY3(flag, name)
+# define IF_HAVE_VM_PKEY4(flag, name)
+#endif
+
#ifdef CONFIG_ARM64_MTE
# define IF_HAVE_VM_MTE(flag, name) {flag, name},
#else
@@ -251,6 +269,11 @@ IF_HAVE_VM_SOFTDIRTY(VM_SOFTDIRTY, "softdirty" ) \
{VM_HUGEPAGE, "hugepage" }, \
{VM_NOHUGEPAGE, "nohugepage" }, \
IF_HAVE_VM_DROPPABLE(VM_DROPPABLE, "droppable" ) \
+IF_HAVE_VM_PKEY(VM_PKEY_BIT0, "pkey_bit0") \
+IF_HAVE_VM_PKEY(VM_PKEY_BIT1, "pkey_bit1") \
+IF_HAVE_VM_PKEY(VM_PKEY_BIT2, "pkey_bit2") \
+IF_HAVE_VM_PKEY3(VM_PKEY_BIT3, "pkey_bit3") \
+IF_HAVE_VM_PKEY4(VM_PKEY_BIT4, "pkey_bit4") \
IF_HAVE_VM_MTE(VM_MTE, "mte") \
IF_HAVE_VM_MTE(VM_MTE_ALLOWED, "mte_allowed") \
IF_HAVE_VM_SHADOW_STACK(VM_SHADOW_STACK, "shadow_stack") \
--
2.25.1
^ permalink raw reply related
* [PATCH RESEND 2/3] mm: trace: decode MTE and shadow stack VMA flags
From: Meijing Zhao @ 2026-07-23 9:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mathieu Desnoyers, Andrew Morton, linux-trace-kernel, linux-mm,
linux-kernel, Meijing Zhao
In-Reply-To: <20260723092905.761037-1-zhaomeijing100@gmail.com>
From: Meijing Zhao <zhaomeijing@lixiang.com>
show_vma_flags(), which is used by VMA tracepoints and %pGv, leaves
architecture-specific HIGH_ARCH_* bits unnamed. Arm64 MTE flags and
user shadow stack flags are therefore printed as raw hexadecimal values.
These bit positions are shared between architectures. For example, bit
37 represents VM_MTE_ALLOWED on arm64 but VM_SHADOW_STACK on x86, so a
shared HIGH_ARCH_* bit cannot be given an unconditional name.
Add conditionally compiled names for VM_MTE, VM_MTE_ALLOWED and
VM_SHADOW_STACK. Keep the configuration guards aligned with the
definitions of these aliases so each shared bit position is decoded
according to the target architecture.
For example, an arm64 VMA containing VM_MTE_ALLOWED is now printed as:
...|account|mte_allowed|...
instead of:
...|account|0x2000000000
Signed-off-by: Meijing Zhao <zhaomeijing@lixiang.com>
---
include/trace/events/mmflags.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index add5ad6843e8..2d76daacb70c 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -204,6 +204,19 @@ IF_HAVE_PG_ARCH_3(arch_3)
# define IF_HAVE_VM_DROPPABLE(flag, name)
#endif
+#ifdef CONFIG_ARM64_MTE
+# define IF_HAVE_VM_MTE(flag, name) {flag, name},
+#else
+# define IF_HAVE_VM_MTE(flag, name)
+#endif
+
+#if defined(CONFIG_X86_USER_SHADOW_STACK) || defined(CONFIG_RISCV_USER_CFI) || \
+ defined(CONFIG_ARM64_GCS)
+# define IF_HAVE_VM_SHADOW_STACK(flag, name) {flag, name},
+#else
+# define IF_HAVE_VM_SHADOW_STACK(flag, name)
+#endif
+
#define __def_vmaflag_names \
{VM_READ, "read" }, \
{VM_WRITE, "write" }, \
@@ -238,6 +251,9 @@ IF_HAVE_VM_SOFTDIRTY(VM_SOFTDIRTY, "softdirty" ) \
{VM_HUGEPAGE, "hugepage" }, \
{VM_NOHUGEPAGE, "nohugepage" }, \
IF_HAVE_VM_DROPPABLE(VM_DROPPABLE, "droppable" ) \
+IF_HAVE_VM_MTE(VM_MTE, "mte") \
+IF_HAVE_VM_MTE(VM_MTE_ALLOWED, "mte_allowed") \
+IF_HAVE_VM_SHADOW_STACK(VM_SHADOW_STACK, "shadow_stack") \
{VM_MERGEABLE, "mergeable" } \
#define show_vma_flags(flags) \
--
2.25.1
^ permalink raw reply related
* [PATCH RESEND 1/3] mm: trace: decode arm64 and sparc64 VM_ARCH_1 flags
From: Meijing Zhao @ 2026-07-23 9:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mathieu Desnoyers, Andrew Morton, linux-trace-kernel, linux-mm,
linux-kernel, Meijing Zhao
In-Reply-To: <20260723092905.761037-1-zhaomeijing100@gmail.com>
From: Meijing Zhao <zhaomeijing@lixiang.com>
The VM_ARCH_1 bit has architecture-specific meanings. show_vma_flags(),
which is used by VMA tracepoints and %pGv, already reports the powerpc,
parisc and no-MMU meanings, but falls back to the generic "arch_1" name
on arm64 and sparc64.
Report VM_ARM64_BTI as "bti" and VM_SPARC_ADI as "adi" so trace output
and %pGv dumps expose the actual architecture-specific state. These
names also match the mnemonics used by /proc/<pid>/smaps.
Signed-off-by: Meijing Zhao <zhaomeijing@lixiang.com>
---
include/trace/events/mmflags.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index a6e5a44c9b42..add5ad6843e8 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -176,6 +176,10 @@ IF_HAVE_PG_ARCH_3(arch_3)
#define __VM_ARCH_SPECIFIC_1 {VM_SAO, "sao" }
#elif defined(CONFIG_PARISC)
#define __VM_ARCH_SPECIFIC_1 {VM_GROWSUP, "growsup" }
+#elif defined(CONFIG_SPARC64)
+#define __VM_ARCH_SPECIFIC_1 {VM_SPARC_ADI, "adi" }
+#elif defined(CONFIG_ARM64)
+#define __VM_ARCH_SPECIFIC_1 {VM_ARM64_BTI, "bti" }
#elif !defined(CONFIG_MMU)
#define __VM_ARCH_SPECIFIC_1 {VM_MAPPED_COPY,"mappedcopy" }
#else
--
2.25.1
^ permalink raw reply related
* [PATCH RESEND 0/3] mm: trace: decode architecture-specific VMA flags
From: Meijing Zhao @ 2026-07-23 9:29 UTC (permalink / raw)
To: Steven Rostedt, Masami Hiramatsu
Cc: Mathieu Desnoyers, Andrew Morton, linux-trace-kernel, linux-mm,
linux-kernel, Meijing Zhao
From: Meijing Zhao <zhaomeijing@lixiang.com>
show_vma_flags(), which is used by VMA tracepoints and %pGv, names
generic VM_* bits but does not fully decode architecture-specific flag
positions. As a result, trace output and VMA dumps can show a generic
"arch_1" name or a raw hexadecimal value instead of the meaning assigned
by the target architecture.
This series adds symbolic names for the arm64 and sparc64 meanings of
VM_ARCH_1, arm64 MTE flags, user shadow stack flags, and protection-key
encoding bits under their corresponding configuration guards.
The original submission was rewritten by the mail gateway as a
multipart HTML message and had a disclaimer appended, causing the vger
mailing lists to reject it. This resend uses a mail transport that
preserves plain-text messages. There are no code changes.
Meijing Zhao (3):
mm: trace: decode arm64 and sparc64 VM_ARCH_1 flags
mm: trace: decode MTE and shadow stack VMA flags
mm: trace: name protection key encoding bits
include/trace/events/mmflags.h | 43 ++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
--
2.25.1
^ permalink raw reply
* [PATCH v5 17/17] selftests/verification: Add selftests for deadline and stall monitors
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco,
Shuah Khan, linux-kselftest
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
Add selftests to verify deadline monitors don't fail under expected
conditions and the stall monitor report violations only when expected.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
Notes:
V4:
* Restore original stall threshold after selftest
.../verification/test.d/rv_deadline.tc | 23 +++++++++++++
.../selftests/verification/test.d/rv_stall.tc | 33 +++++++++++++++++++
2 files changed, 56 insertions(+)
create mode 100644 tools/testing/selftests/verification/test.d/rv_deadline.tc
create mode 100644 tools/testing/selftests/verification/test.d/rv_stall.tc
diff --git a/tools/testing/selftests/verification/test.d/rv_deadline.tc b/tools/testing/selftests/verification/test.d/rv_deadline.tc
new file mode 100644
index 000000000000..fc95267dbb82
--- /dev/null
+++ b/tools/testing/selftests/verification/test.d/rv_deadline.tc
@@ -0,0 +1,23 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# description: Test deadline monitors trigger no reaction
+# requires: available_reactors deadline:monitor printk:reactor stress-ng:program
+
+load() { # returns true if there was a reaction
+ local lines_before
+ lines_before=$(dmesg | wc -l)
+ stress-ng --cpu 2 --sched deadline --sched-period 100000000 \
+ --sched-deadline 100000000 --sched-runtime 20000000 -t 5 &
+ stress-ng --cpu 2 --sched rr --sched-prio 50 --cyclic 1 \
+ --cyclic-policy rr --cyclic-prio 50 -t 5 &
+ wait
+ dmesg | tail -n +$((lines_before + 1)) | grep -q "rv: monitor [a-z]\+ does not allow event"
+}
+
+echo 1 > monitors/deadline/enable
+echo printk > monitors/deadline/reactors
+
+! load || false
+
+echo nop > monitors/deadline/reactors
+echo 0 > monitors/deadline/enable
diff --git a/tools/testing/selftests/verification/test.d/rv_stall.tc b/tools/testing/selftests/verification/test.d/rv_stall.tc
new file mode 100644
index 000000000000..515a10263ca1
--- /dev/null
+++ b/tools/testing/selftests/verification/test.d/rv_stall.tc
@@ -0,0 +1,33 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# description: Test stall monitor
+# requires: available_reactors stall:monitor printk:reactor stress-ng:program
+
+THRESHOLD=/sys/module/stall/parameters/threshold_jiffies
+ORIG_THRESHOLD=$(cat $THRESHOLD)
+trap 'echo $ORIG_THRESHOLD > $THRESHOLD' EXIT
+
+load() { # returns true if there was a reaction
+ local lines_before cpu
+ cpu=$(($(nproc) - 1))
+ lines_before=$(dmesg | wc -l)
+ stress-ng --cpu 1 --taskset "$cpu" --sched rr --sched-prio 1 -t 3 &
+ stress-ng --cpu 5 --taskset "$cpu" -t 3 &
+ wait
+ dmesg | tail -n +$((lines_before + 1)) | grep -q "rv: monitor stall does not allow event"
+}
+
+echo 5000 > $THRESHOLD
+echo 1 > monitors/stall/enable
+echo printk > monitors/stall/reactors
+
+! load || false
+
+echo 0 > monitors/stall/enable
+echo 70 > $THRESHOLD
+echo 1 > monitors/stall/enable
+
+load
+
+echo nop > monitors/stall/reactors
+echo 0 > monitors/stall/enable
--
2.55.0
^ permalink raw reply related
* [PATCH v5 16/17] selftests/verification: Rearrange the wwnr_printk test
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco,
Shuah Khan, linux-kselftest
Cc: Wen Yang, Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
The wwnr_printk test expects no reactions in some situations, after
fixing the bash assertion, the test is failing because expecting no
reaction after a previous step had reactions is flaky without making
sure all buffers are flushed.
Wait for reactions to be over when expected by polling dmesg for an
interval without any rv message.
Also simplify the load function to stop loads as soon as a reaction
occurs, this limits the number of lines to flush and makes tests overall
faster and more stable.
Reviewed-by: Wen Yang <wen.yang@linux.dev>
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
Notes:
V5:
* Move wait for flush to avoid wwnr/printk selftest to hang
V4:
* Wait for dmesg to flush all messages after reactions
.../verification/test.d/rv_wwnr_printk.tc | 28 +++++++++++++++++--
1 file changed, 25 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc b/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
index 96de95edb530..17e1edfb3902 100644
--- a/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
+++ b/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
@@ -4,11 +4,29 @@
# requires: available_reactors wwnr:monitor printk:reactor stress-ng:program
load() { # returns true if there was a reaction
- local lines_before num
+ local lines_before num load_pid ret
num=$((($(nproc) + 1) / 2))
lines_before=$(dmesg | wc -l)
- stress-ng --cpu-sched "$num" --timer "$num" -t 5 -q
- dmesg | tail -n $((lines_before + 1)) | grep -q "rv: monitor wwnr does not allow event"
+ stress-ng --cpu-sched "$num" --timer "$num" -t 5 -q &
+ load_pid=$!
+ timeout 5 dmesg -w | tail -n +$((lines_before + 1)) | \
+ grep -m 1 -q "rv: monitor wwnr does not allow event"
+ ret=$?
+ kill "$load_pid" || true
+ wait "$load_pid" || true
+ return $ret
+}
+
+# loads may flood the ringbuffer, wait for all pending printks (timeout at 2 minutes)
+wait_dmesg_flush() {
+ local last_before last_after
+ for _ in $(seq 400); do
+ last_before=$last_after
+ last_after=$(dmesg | grep "rv:" | tail -n 1 || true)
+ [ "$last_before" = "$last_after" ] && return 0
+ sleep .3
+ done
+ return 1
}
echo 1 > monitors/wwnr/enable
@@ -17,12 +35,16 @@ echo printk > monitors/wwnr/reactors
load
echo 0 > monitoring_on
+wait_dmesg_flush
+
! load || false
echo 1 > monitoring_on
load
echo 0 > reacting_on
+wait_dmesg_flush
+
! load || false
echo 1 > reacting_on
--
2.55.0
^ permalink raw reply related
* [PATCH v5 15/17] selftests/verification: Fix wrong errexit assumption
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco,
Shuah Khan, linux-kselftest
Cc: Wen Yang, Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
RV selftest rely on bash errexit (set -e) to terminate with error, when
a step is expected to return false, the following syntax is used:
! cmd
This however prevents the test from exiting when cmd is false (desired)
but doesn't exit if cmd is true, since commands prefixed with ! are
explicitly excluded from errexit.
Use the syntax
! cmd || false
Which ends up checking the exit value of ! cmd and supplies a false
command for errexit to evaluate.
Reviewed-by: Wen Yang <wen.yang@linux.dev>
Acked-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
.../verification/test.d/rv_monitor_enable_disable.tc | 10 +++++-----
.../verification/test.d/rv_monitor_reactor.tc | 4 ++--
.../selftests/verification/test.d/rv_wwnr_printk.tc | 4 ++--
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/verification/test.d/rv_monitor_enable_disable.tc b/tools/testing/selftests/verification/test.d/rv_monitor_enable_disable.tc
index f29236defb5a..61e2c8b54d9a 100644
--- a/tools/testing/selftests/verification/test.d/rv_monitor_enable_disable.tc
+++ b/tools/testing/selftests/verification/test.d/rv_monitor_enable_disable.tc
@@ -10,7 +10,7 @@ test_simple_monitor() {
grep -q "$monitor$" enabled_monitors
echo 0 > "monitors/$prefix$monitor/enable"
- ! grep -q "$monitor$" enabled_monitors
+ ! grep -q "$monitor$" enabled_monitors || false
echo "$monitor" >> enabled_monitors
grep -q 1 "monitors/$prefix$monitor/enable"
@@ -34,12 +34,12 @@ test_container_monitor() {
test -n "$nested"
echo 0 > "monitors/$monitor/enable"
- ! grep -q "^$monitor$" enabled_monitors
+ ! grep -q "^$monitor$" enabled_monitors || false
for nested_dir in "monitors/$monitor"/*; do
[ -d "$nested_dir" ] || continue
nested=$(basename "$nested_dir")
- ! grep -q "^$monitor:$nested$" enabled_monitors
+ ! grep -q "^$monitor:$nested$" enabled_monitors || false
done
echo "$monitor" >> enabled_monitors
@@ -71,5 +71,5 @@ for monitor_dir in monitors/*; do
fi
done
-! echo non_existent_monitor > enabled_monitors
-! grep -q "^non_existent_monitor$" enabled_monitors
+! echo non_existent_monitor > enabled_monitors || false
+! grep -q "^non_existent_monitor$" enabled_monitors || false
diff --git a/tools/testing/selftests/verification/test.d/rv_monitor_reactor.tc b/tools/testing/selftests/verification/test.d/rv_monitor_reactor.tc
index 2958bf849338..516a20971390 100644
--- a/tools/testing/selftests/verification/test.d/rv_monitor_reactor.tc
+++ b/tools/testing/selftests/verification/test.d/rv_monitor_reactor.tc
@@ -64,5 +64,5 @@ done
monitor=$(ls /sys/kernel/tracing/rv/monitors -1 | head -n 1)
test -f "monitors/$monitor/reactors"
-! echo non_existent_reactor > "monitors/$monitor/reactors"
-! grep -q "\\[non_existent_reactor\\]" "monitors/$monitor/reactors"
+! echo non_existent_reactor > "monitors/$monitor/reactors" || false
+! grep -q "\\[non_existent_reactor\\]" "monitors/$monitor/reactors" || false
diff --git a/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc b/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
index 5a59432b1d93..96de95edb530 100644
--- a/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
+++ b/tools/testing/selftests/verification/test.d/rv_wwnr_printk.tc
@@ -17,13 +17,13 @@ echo printk > monitors/wwnr/reactors
load
echo 0 > monitoring_on
-! load
+! load || false
echo 1 > monitoring_on
load
echo 0 > reacting_on
-! load
+! load || false
echo 1 > reacting_on
echo nop > monitors/wwnr/reactors
--
2.55.0
^ permalink raw reply related
* [PATCH v5 14/17] rv: Add KUnit tests for some LTL monitors
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco,
Masami Hiramatsu
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
Validate the functionality of LTL monitors by injecting events in a
controlled environment (KUnit) and expecting reactions, just like it is
done in DA monitors.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
include/rv/ltl_monitor.h | 8 +++
.../trace/rv/monitors/pagefault/pagefault.c | 12 ++++
.../rv/monitors/pagefault/pagefault_kunit.c | 34 +++++++++++
.../rv/monitors/pagefault/pagefault_kunit.h | 24 ++++++++
kernel/trace/rv/monitors/sleep/sleep.c | 19 ++++++
kernel/trace/rv/monitors/sleep/sleep_kunit.c | 58 +++++++++++++++++++
kernel/trace/rv/monitors/sleep/sleep_kunit.h | 30 ++++++++++
kernel/trace/rv/rv_monitors_test.c | 4 ++
8 files changed, 189 insertions(+)
create mode 100644 kernel/trace/rv/monitors/pagefault/pagefault_kunit.c
create mode 100644 kernel/trace/rv/monitors/pagefault/pagefault_kunit.h
create mode 100644 kernel/trace/rv/monitors/sleep/sleep_kunit.c
create mode 100644 kernel/trace/rv/monitors/sleep/sleep_kunit.h
diff --git a/include/rv/ltl_monitor.h b/include/rv/ltl_monitor.h
index d7dc01db4dd9..e9fd8265a3da 100644
--- a/include/rv/ltl_monitor.h
+++ b/include/rv/ltl_monitor.h
@@ -172,3 +172,11 @@ static void __maybe_unused ltl_atom_pulse(struct task_struct *task, enum ltl_ato
ltl_atom_set(mon, atom, !value);
ltl_validate(task, mon);
}
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .is_per_task = true, \
+ .task_slot = <l_monitor_slot, \
+}
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault.c b/kernel/trace/rv/monitors/pagefault/pagefault.c
index e52500fd2de0..c599fc19fc88 100644
--- a/kernel/trace/rv/monitors/pagefault/pagefault.c
+++ b/kernel/trace/rv/monitors/pagefault/pagefault.c
@@ -86,3 +86,15 @@ module_exit(unregister_pagefault);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nam Cao <namcao@linutronix.de>");
MODULE_DESCRIPTION("pagefault: Monitor that RT tasks do not raise page faults");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "pagefault_kunit.h"
+
+const struct rv_pagefault_ops rv_pagefault_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_page_fault = handle_page_fault,
+ .handle_task_newtask = handle_task_newtask,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_pagefault_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault_kunit.c b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.c
new file mode 100644
index 000000000000..06369960b008
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <linux/sched/deadline.h>
+#include <linux/sched/rt.h>
+#include "pagefault_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_PAGEFAULT)
+
+static void rv_test_pagefault(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_pagefault_ops.mon);
+
+ /* Initial pagefault when non-RT to start the model without failure */
+ target->policy = SCHED_NORMAL;
+ target->prio = MAX_RT_PRIO + 20;
+ rv_pagefault_ops.handle_task_newtask(NULL, target, 0);
+ rv_mock_current(target);
+ rv_pagefault_ops.handle_page_fault(NULL, 0, NULL, 0);
+
+ /* RT task has a page fault */
+ target->policy = SCHED_FIFO;
+ target->prio = MAX_RT_PRIO - 1;
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_pagefault_ops.handle_page_fault(NULL, 0, NULL, 0);
+}
+
+#else
+#define rv_test_pagefault rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault_kunit.h b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.h
new file mode 100644
index 000000000000..2f9652f08b3f
--- /dev/null
+++ b/kernel/trace/rv/monitors/pagefault/pagefault_kunit.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __PAGEFAULT_KUNIT_H
+#define __PAGEFAULT_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_pagefault_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_page_fault)(void *data, unsigned long address, struct pt_regs *regs,
+ unsigned long error_code);
+ void (*handle_task_newtask)(void *data, struct task_struct *task, u64 flags);
+} rv_pagefault_ops;
+#endif
+
+#endif /* __PAGEFAULT_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/sleep/sleep.c b/kernel/trace/rv/monitors/sleep/sleep.c
index 71d2005ce520..f84a61a21825 100644
--- a/kernel/trace/rv/monitors/sleep/sleep.c
+++ b/kernel/trace/rv/monitors/sleep/sleep.c
@@ -247,3 +247,22 @@ module_exit(unregister_sleep);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nam Cao <namcao@linutronix.de>");
MODULE_DESCRIPTION("sleep: Monitor that RT tasks do not undesirably sleep");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sleep_kunit.h"
+
+const struct rv_sleep_ops rv_sleep_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_waking = handle_sched_waking,
+ .handle_sched_wakeup = handle_sched_wakeup,
+ .handle_sched_set_state = handle_sched_set_state,
+ .handle_contention_begin = handle_contention_begin,
+ .handle_contention_end = handle_contention_end,
+ .handle_kthread_stop = handle_kthread_stop,
+ .handle_sys_enter = handle_sys_enter,
+ .handle_sys_exit = handle_sys_exit,
+ .handle_task_newtask = handle_task_newtask,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sleep_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sleep/sleep_kunit.c b/kernel/trace/rv/monitors/sleep/sleep_kunit.c
new file mode 100644
index 000000000000..ccbb3d188528
--- /dev/null
+++ b/kernel/trace/rv/monitors/sleep/sleep_kunit.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/syscalls.h>
+#include <trace/events/sched.h>
+#include <uapi/linux/futex.h>
+#include "sleep_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_SLEEP)
+
+static void rv_test_sleep(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+ unsigned long args[6] = {0};
+ struct pt_regs regs = {0};
+
+ prepare_test(test, &rv_sleep_ops.mon);
+ target->policy = SCHED_FIFO;
+ target->prio = MAX_RT_PRIO - 2;
+ other->policy = SCHED_FIFO;
+ other->prio = MAX_RT_PRIO - 1;
+ rv_sleep_ops.handle_task_newtask(NULL, target, 0);
+
+ /* RT task sleeps on a non RT-friendly nanosleep */
+ rv_mock_current(target);
+ args[0] = CLOCK_REALTIME;
+ syscall_set_arguments(target, ®s, args);
+#ifdef __NR_clock_nanosleep
+ rv_sleep_ops.handle_sys_enter(NULL, ®s, __NR_clock_nanosleep);
+#elif defined(__NR_clock_nanosleep_time64)
+ rv_sleep_ops.handle_sys_enter(NULL, ®s, __NR_clock_nanosleep_time64);
+#endif
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sleep_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_sleep_ops.handle_sys_exit(NULL, NULL, 0);
+
+ /* RT task woken up by lower priority task */
+ args[1] = FUTEX_WAIT;
+ syscall_set_arguments(target, ®s, args);
+ rv_mock_current(target);
+#ifdef __NR_futex
+ rv_sleep_ops.handle_sys_enter(NULL, ®s, __NR_futex);
+#elif defined(__NR_futex_time64)
+ rv_sleep_ops.handle_sys_enter(NULL, ®s, __NR_futex_time64);
+#endif
+ rv_sleep_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_mock_current(other);
+ rv_sleep_ops.handle_sched_waking(NULL, target);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sleep_ops.handle_sched_wakeup(NULL, target);
+}
+
+#else
+#define rv_test_sleep rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sleep/sleep_kunit.h b/kernel/trace/rv/monitors/sleep/sleep_kunit.h
new file mode 100644
index 000000000000..2cd61e31a6af
--- /dev/null
+++ b/kernel/trace/rv/monitors/sleep/sleep_kunit.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __SLEEP_KUNIT_H
+#define __SLEEP_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sleep_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_waking)(void *data, struct task_struct *task);
+ void (*handle_sched_wakeup)(void *data, struct task_struct *task);
+ void (*handle_sched_set_state)(void *data, struct task_struct *task, int state);
+ void (*handle_contention_begin)(void *data, void *lock, unsigned int flags);
+ void (*handle_contention_end)(void *data, void *lock, int ret);
+ void (*handle_kthread_stop)(void *data, struct task_struct *task);
+ void (*handle_sys_enter)(void *data, struct pt_regs *regs, long id);
+ void (*handle_sys_exit)(void *data, struct pt_regs *regs, long ret);
+ void (*handle_task_newtask)(void *data, struct task_struct *task, u64 flags);
+} rv_sleep_ops;
+#endif
+
+#endif /* __SLEEP_KUNIT_H */
diff --git a/kernel/trace/rv/rv_monitors_test.c b/kernel/trace/rv/rv_monitors_test.c
index 8026176eee90..bbbf7d120ec0 100644
--- a/kernel/trace/rv/rv_monitors_test.c
+++ b/kernel/trace/rv/rv_monitors_test.c
@@ -148,6 +148,8 @@ static void rv_test_dummy(struct kunit *test)
#include "monitors/sts/sts_kunit.c"
#include "monitors/opid/opid_kunit.c"
#include "monitors/nomiss/nomiss_kunit.c"
+#include "monitors/pagefault/pagefault_kunit.c"
+#include "monitors/sleep/sleep_kunit.c"
static struct kunit_case rv_mon_test_cases[] = {
KUNIT_CASE(rv_test_dummy),
@@ -156,6 +158,8 @@ static struct kunit_case rv_mon_test_cases[] = {
KUNIT_CASE(rv_test_sts),
KUNIT_CASE(rv_test_opid),
KUNIT_CASE(rv_test_nomiss),
+ KUNIT_CASE(rv_test_pagefault),
+ KUNIT_CASE(rv_test_sleep),
{}
};
--
2.55.0
^ permalink raw reply related
* [PATCH v5 13/17] rv: Add KUnit mock for current
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco,
Masami Hiramatsu
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
Some monitors do not only rely on tracepoint arguments but also on the
currently executing task.
This makes it more challenging to mock events in KUnit.
Define wrapper functions around current, the functionality is mocked
only during KUnit, an additional function call is avoided using a static
branch unless any (even unrelated) KUnit test is running.
Rely on a global mock_current variable that is set only by the RV KUnit
tests and cleared on teardown. Unrelated KUnit tests that happen to
trigger RV handlers would see it null and use current.
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
Notes:
V5:
* Drop static stub from current to avoid issues with unrelated KUnit tests
include/rv/da_monitor.h | 1 +
include/rv/kunit.h | 14 +++++++++-
include/rv/ltl_monitor.h | 1 +
kernel/trace/rv/Kconfig | 3 +++
.../trace/rv/monitors/pagefault/pagefault.c | 2 +-
kernel/trace/rv/monitors/sleep/sleep.c | 24 ++++++++---------
kernel/trace/rv/rv.c | 26 +++++++++++++++++++
kernel/trace/rv/rv_monitors_test.c | 1 +
8 files changed, 58 insertions(+), 14 deletions(-)
diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h
index 773564720ba1..9f7ba443d777 100644
--- a/include/rv/da_monitor.h
+++ b/include/rv/da_monitor.h
@@ -16,6 +16,7 @@
#include <rv/automata.h>
#include <linux/rv.h>
+#include <rv/kunit.h>
#include <linux/stringify.h>
#include <linux/bug.h>
#include <linux/sched.h>
diff --git a/include/rv/kunit.h b/include/rv/kunit.h
index ff98b5137285..31e0b93c40ea 100644
--- a/include/rv/kunit.h
+++ b/include/rv/kunit.h
@@ -2,7 +2,10 @@
/*
* Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
*
- * Declaration of utilities to run KUnit tests.
+ * Declaration of wrappers to allow mocking core functionality, like current,
+ * and other testing utilities.
+ * Necessary only when mocking may be needed. If the RV KUnit test is
+ * enabled, the wrappers incur an additional function call overhead.
*/
#ifndef _RV_KUNIT_H
@@ -57,5 +60,14 @@ void prepare_test(struct kunit *test, const struct rv_kunit_mon *mon);
void teardown_test(void *arg);
struct task_struct *rv_kunit_alloc_mock_task(struct kunit *test);
+void rv_mock_current(struct task_struct *tsk);
+struct task_struct *rv_get_mock_current(void);
+
+#define rv_get_current() (unlikely(kunit_get_current_test()) ? rv_get_mock_current() : current)
+
+#else /* !CONFIG_RV_MONITORS_KUNIT_TEST */
+
+#define rv_get_current() current
+
#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
#endif /* _RV_KUNIT_H */
diff --git a/include/rv/ltl_monitor.h b/include/rv/ltl_monitor.h
index 56e83edcf0c4..d7dc01db4dd9 100644
--- a/include/rv/ltl_monitor.h
+++ b/include/rv/ltl_monitor.h
@@ -9,6 +9,7 @@
#include <linux/stringify.h>
#include <linux/seq_buf.h>
#include <rv/instrumentation.h>
+#include <rv/kunit.h>
#include <trace/events/task.h>
#include <trace/events/sched.h>
diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
index 34c1feb35a9b..da905de6c4e8 100644
--- a/kernel/trace/rv/Kconfig
+++ b/kernel/trace/rv/Kconfig
@@ -121,4 +121,7 @@ config RV_MONITORS_KUNIT_TEST
These tests verify that monitors correctly detect violations by
triggering fake events and validating the expected reactions.
+ Enabling this may slightly increase overhead of some monitors if any
+ unrelated KUnit test is running.
+
If unsure, say N.
diff --git a/kernel/trace/rv/monitors/pagefault/pagefault.c b/kernel/trace/rv/monitors/pagefault/pagefault.c
index 5e1a2a606783..e52500fd2de0 100644
--- a/kernel/trace/rv/monitors/pagefault/pagefault.c
+++ b/kernel/trace/rv/monitors/pagefault/pagefault.c
@@ -38,7 +38,7 @@ static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bo
static void handle_page_fault(void *data, unsigned long address, struct pt_regs *regs,
unsigned long error_code)
{
- ltl_atom_pulse(current, LTL_PAGEFAULT, true);
+ ltl_atom_pulse(rv_get_current(), LTL_PAGEFAULT, true);
}
static int enable_pagefault(void)
diff --git a/kernel/trace/rv/monitors/sleep/sleep.c b/kernel/trace/rv/monitors/sleep/sleep.c
index 12328ce663f5..71d2005ce520 100644
--- a/kernel/trace/rv/monitors/sleep/sleep.c
+++ b/kernel/trace/rv/monitors/sleep/sleep.c
@@ -102,7 +102,7 @@ static void handle_sched_waking(void *data, struct task_struct *task)
if (this_cpu_read(hardirq_context)) {
ltl_atom_pulse(task, LTL_WOKEN_BY_HARDIRQ, true);
} else if (in_task()) {
- if (current->prio <= task->prio)
+ if (rv_get_current()->prio <= task->prio)
ltl_atom_pulse(task, LTL_WOKEN_BY_EQUAL_OR_HIGHER_PRIO, true);
} else if (in_nmi()) {
ltl_atom_pulse(task, LTL_WOKEN_BY_NMI, true);
@@ -112,12 +112,12 @@ static void handle_sched_waking(void *data, struct task_struct *task)
static void handle_contention_begin(void *data, void *lock, unsigned int flags)
{
if (flags & LCB_F_RT)
- ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, true);
+ ltl_atom_update(rv_get_current(), LTL_BLOCK_ON_RT_MUTEX, true);
}
static void handle_contention_end(void *data, void *lock, int ret)
{
- ltl_atom_update(current, LTL_BLOCK_ON_RT_MUTEX, false);
+ ltl_atom_update(rv_get_current(), LTL_BLOCK_ON_RT_MUTEX, false);
}
static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
@@ -126,7 +126,7 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
unsigned long args[6];
int op, cmd;
- mon = ltl_get_monitor(current);
+ mon = ltl_get_monitor(rv_get_current());
switch (id) {
#ifdef __NR_clock_nanosleep
@@ -135,11 +135,11 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
#ifdef __NR_clock_nanosleep_time64
case __NR_clock_nanosleep_time64:
#endif
- syscall_get_arguments(current, regs, args);
+ syscall_get_arguments(rv_get_current(), regs, args);
ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_MONOTONIC, args[0] == CLOCK_MONOTONIC);
ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, args[0] == CLOCK_TAI);
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, args[1] == TIMER_ABSTIME);
- ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, true);
+ ltl_atom_update(rv_get_current(), LTL_CLOCK_NANOSLEEP, true);
break;
#ifdef __NR_futex
@@ -148,25 +148,25 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
#ifdef __NR_futex_time64
case __NR_futex_time64:
#endif
- syscall_get_arguments(current, regs, args);
+ syscall_get_arguments(rv_get_current(), regs, args);
op = args[1];
cmd = op & FUTEX_CMD_MASK;
switch (cmd) {
case FUTEX_LOCK_PI:
case FUTEX_LOCK_PI2:
- ltl_atom_update(current, LTL_FUTEX_LOCK_PI, true);
+ ltl_atom_update(rv_get_current(), LTL_FUTEX_LOCK_PI, true);
break;
case FUTEX_WAIT:
case FUTEX_WAIT_BITSET:
case FUTEX_WAIT_REQUEUE_PI:
- ltl_atom_update(current, LTL_FUTEX_WAIT, true);
+ ltl_atom_update(rv_get_current(), LTL_FUTEX_WAIT, true);
break;
}
break;
#ifdef __NR_epoll_wait
case __NR_epoll_wait:
- ltl_atom_update(current, LTL_EPOLL_WAIT, true);
+ ltl_atom_update(rv_get_current(), LTL_EPOLL_WAIT, true);
break;
#endif
}
@@ -174,7 +174,7 @@ static void handle_sys_enter(void *data, struct pt_regs *regs, long id)
static void handle_sys_exit(void *data, struct pt_regs *regs, long ret)
{
- struct ltl_monitor *mon = ltl_get_monitor(current);
+ struct ltl_monitor *mon = ltl_get_monitor(rv_get_current());
ltl_atom_set(mon, LTL_FUTEX_LOCK_PI, false);
ltl_atom_set(mon, LTL_FUTEX_WAIT, false);
@@ -182,7 +182,7 @@ static void handle_sys_exit(void *data, struct pt_regs *regs, long ret)
ltl_atom_set(mon, LTL_NANOSLEEP_CLOCK_TAI, false);
ltl_atom_set(mon, LTL_NANOSLEEP_TIMER_ABSTIME, false);
ltl_atom_set(mon, LTL_EPOLL_WAIT, false);
- ltl_atom_update(current, LTL_CLOCK_NANOSLEEP, false);
+ ltl_atom_update(rv_get_current(), LTL_CLOCK_NANOSLEEP, false);
}
static void handle_kthread_stop(void *data, struct task_struct *task)
diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c
index cfe950fef3b4..4c7143961e6e 100644
--- a/kernel/trace/rv/rv.c
+++ b/kernel/trace/rv/rv.c
@@ -893,4 +893,30 @@ void rv_clear_testing(struct kunit_suite *suite)
mutex_unlock(&rv_interface_lock);
}
EXPORT_SYMBOL_IF_KUNIT(rv_clear_testing);
+
+/*
+ * rv_get_mock_current() is called only if we are running from a KUnit test.
+ * This can occur from a legitimate RV test or any unrelated test running when
+ * a real RV monitor is active and triggering events.
+ * We assume the former case is the only one where mock_current is not NULL and
+ * can occur only sequentially (KUnit doesn't run tests in parallel).
+ * We cannot rely on the test's context because there is no way to safely
+ * understand from which test we are running and KUnit utilities require
+ * locking, which is unsafe from NMI or scheduling context.
+ * Note that it is not possible for a real RV monitor to run when the RV KUnit
+ * tests are running (see rv_set_testing()).
+ */
+static struct task_struct *mock_current;
+
+void rv_mock_current(struct task_struct *tsk)
+{
+ mock_current = tsk;
+}
+EXPORT_SYMBOL_IF_KUNIT(rv_mock_current);
+
+struct task_struct *rv_get_mock_current(void)
+{
+ return mock_current ?: current;
+}
+EXPORT_SYMBOL_GPL(rv_get_mock_current);
#endif
diff --git a/kernel/trace/rv/rv_monitors_test.c b/kernel/trace/rv/rv_monitors_test.c
index 82c798f1875a..8026176eee90 100644
--- a/kernel/trace/rv/rv_monitors_test.c
+++ b/kernel/trace/rv/rv_monitors_test.c
@@ -54,6 +54,7 @@ void teardown_test(void *arg)
mon->rv_this->react = NULL;
active_ctx = NULL;
+ rv_mock_current(NULL);
if (mon->is_per_task)
*mon->task_slot = RV_PER_TASK_MONITOR_INIT;
--
2.55.0
^ permalink raw reply related
* [PATCH v5 12/17] rv: Add KUnit tests for some DA/HA monitors
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco,
Masami Hiramatsu
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
Validate the functionality of DA monitors by injecting events in a
controlled environment (KUnit) and expecting reactions.
Events handlers are exported directly from the monitor source files
without using system events and with dummy arguments (e.g. no real
tasks). If the provided sequence of events incurs a violation, the test
expects the stub version of rv_react() to be called.
This testing method can validate the entire monitor implementation since
it sits between the monitor and the system (in place of the
tracepoints). All sorts of system and timing events can be emulated
without affecting the running kernel.
Handlers and monitor functions are exported as part of a struct to
simplify the process of running KUnit tests from kernel modules.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
Notes:
V4:
* Use deadline_thresh in nomiss KUnit test
* Special init/destroy for per-task monitors to avoid touching real
tasks and starting tracepoints
* Add dummy test to check reactions work as expected
* Use actual reactor instead of stub to support timer reactions
include/rv/da_monitor.h | 22 +++
include/rv/ha_monitor.h | 21 +++
include/rv/kunit.h | 61 ++++++
kernel/trace/rv/Kconfig | 11 ++
kernel/trace/rv/Makefile | 1 +
kernel/trace/rv/monitors/nomiss/nomiss.c | 18 ++
.../trace/rv/monitors/nomiss/nomiss_kunit.c | 38 ++++
.../trace/rv/monitors/nomiss/nomiss_kunit.h | 35 ++++
kernel/trace/rv/monitors/opid/opid.c | 12 ++
kernel/trace/rv/monitors/opid/opid_kunit.c | 33 ++++
kernel/trace/rv/monitors/opid/opid_kunit.h | 23 +++
kernel/trace/rv/monitors/sco/sco.c | 13 ++
kernel/trace/rv/monitors/sco/sco_kunit.c | 29 +++
kernel/trace/rv/monitors/sco/sco_kunit.h | 24 +++
kernel/trace/rv/monitors/sssw/sssw.c | 14 ++
kernel/trace/rv/monitors/sssw/sssw_kunit.c | 33 ++++
kernel/trace/rv/monitors/sssw/sssw_kunit.h | 30 +++
kernel/trace/rv/monitors/sts/sts.c | 19 ++
kernel/trace/rv/monitors/sts/sts_kunit.c | 39 ++++
kernel/trace/rv/monitors/sts/sts_kunit.h | 33 ++++
kernel/trace/rv/rv.c | 40 ++++
kernel/trace/rv/rv_monitors_test.c | 174 ++++++++++++++++++
22 files changed, 723 insertions(+)
create mode 100644 include/rv/kunit.h
create mode 100644 kernel/trace/rv/monitors/nomiss/nomiss_kunit.c
create mode 100644 kernel/trace/rv/monitors/nomiss/nomiss_kunit.h
create mode 100644 kernel/trace/rv/monitors/opid/opid_kunit.c
create mode 100644 kernel/trace/rv/monitors/opid/opid_kunit.h
create mode 100644 kernel/trace/rv/monitors/sco/sco_kunit.c
create mode 100644 kernel/trace/rv/monitors/sco/sco_kunit.h
create mode 100644 kernel/trace/rv/monitors/sssw/sssw_kunit.c
create mode 100644 kernel/trace/rv/monitors/sssw/sssw_kunit.h
create mode 100644 kernel/trace/rv/monitors/sts/sts_kunit.c
create mode 100644 kernel/trace/rv/monitors/sts/sts_kunit.h
create mode 100644 kernel/trace/rv/rv_monitors_test.c
diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h
index 34b8fba9ecd4..773564720ba1 100644
--- a/include/rv/da_monitor.h
+++ b/include/rv/da_monitor.h
@@ -311,6 +311,11 @@ static inline struct da_monitor *da_get_monitor(struct task_struct *tsk)
return &tsk->rv[task_mon_slot].da_mon;
}
+static inline void da_reset(struct task_struct *tsk)
+{
+ da_monitor_reset(da_get_monitor(tsk));
+}
+
/*
* da_get_target - return the task associated to the monitor
*/
@@ -908,4 +913,21 @@ static inline void da_reset(da_id_type id, monitor_target target)
}
#endif /* RV_MON_TYPE */
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#if RV_MON_TYPE == RV_MON_PER_TASK
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .is_per_task = true, \
+ .task_slot = &task_mon_slot, \
+ .task_reset = da_reset, \
+}
+#else
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .monitor_init = da_monitor_init, \
+ .monitor_destroy = da_monitor_destroy, \
+}
+#endif /* RV_MON_TYPE */
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
+
#endif
diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h
index 28d3c74cabfc..d4a490bf2726 100644
--- a/include/rv/ha_monitor.h
+++ b/include/rv/ha_monitor.h
@@ -558,4 +558,25 @@ static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon) { }
#endif
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#ifdef RV_MON_OPS_INIT
+#undef RV_MON_OPS_INIT
+#endif
+
+#if RV_MON_TYPE == RV_MON_PER_TASK
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .is_per_task = true, \
+ .task_slot = &task_mon_slot, \
+ .task_reset = da_reset, \
+}
+#else
+#define RV_MON_OPS_INIT() { \
+ .rv_this = &rv_this, \
+ .monitor_init = ha_monitor_init, \
+ .monitor_destroy = ha_monitor_destroy, \
+}
+#endif /* RV_MON_TYPE */
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
+
#endif
diff --git a/include/rv/kunit.h b/include/rv/kunit.h
new file mode 100644
index 000000000000..ff98b5137285
--- /dev/null
+++ b/include/rv/kunit.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
+ *
+ * Declaration of utilities to run KUnit tests.
+ */
+
+#ifndef _RV_KUNIT_H
+#define _RV_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+#include <linux/delay.h>
+
+int rv_set_testing(struct kunit_suite *suite);
+void rv_clear_testing(struct kunit_suite *suite);
+
+#define RV_KUNIT_MAX_MOCK_TASKS 8
+
+struct rv_kunit_ctx {
+ int reactions, expected;
+ int mock_task_count;
+ struct task_struct *mock_tasks[RV_KUNIT_MAX_MOCK_TASKS];
+};
+
+#define RV_KUNIT_EXPECT_REACTION(test, ctx) \
+ do { \
+ KUNIT_EXPECT_EQ(test, ctx->reactions, ++ctx->expected); \
+ if (ctx->reactions != ctx->expected) \
+ ctx->expected = ctx->reactions; \
+ } while (0)
+
+#define RV_KUNIT_EXPECT_NO_REACTION(test, ctx) \
+ do { \
+ KUNIT_EXPECT_EQ(test, ctx->reactions, ctx->expected); \
+ if (ctx->reactions != ctx->expected) \
+ ctx->expected = ctx->reactions; \
+ } while (0)
+
+#define RV_KUNIT_EXPECT_REACTION_HERE(test, ctx) \
+ for (int __done = ({ RV_KUNIT_EXPECT_NO_REACTION(test, ctx); 0; }); \
+ !__done; \
+ __done = ({ RV_KUNIT_EXPECT_REACTION(test, ctx); 1; }))
+
+struct rv_kunit_mon {
+ struct rv_monitor *rv_this;
+ int (*monitor_init)(void);
+ void (*monitor_destroy)(void);
+ bool is_per_task;
+ int *task_slot;
+ void (*task_reset)(struct task_struct *task);
+};
+
+void prepare_test(struct kunit *test, const struct rv_kunit_mon *mon);
+void teardown_test(void *arg);
+struct task_struct *rv_kunit_alloc_mock_task(struct kunit *test);
+
+#endif /* CONFIG_RV_MONITORS_KUNIT_TEST */
+#endif /* _RV_KUNIT_H */
diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
index 3884b14df375..34c1feb35a9b 100644
--- a/kernel/trace/rv/Kconfig
+++ b/kernel/trace/rv/Kconfig
@@ -111,3 +111,14 @@ config RV_REACT_PANIC
help
Enables the panic reactor. The panic reactor emits a printk()
message if an exception is found and panic()s the system.
+
+config RV_MONITORS_KUNIT_TEST
+ tristate "KUnit tests for RV monitors" if !KUNIT_ALL_TESTS
+ depends on KUNIT && RV && RV_REACTORS
+ default KUNIT_ALL_TESTS
+ help
+ Enable KUnit tests for the RV (Runtime Verification) monitors.
+ These tests verify that monitors correctly detect violations by
+ triggering fake events and validating the expected reactions.
+
+ If unsure, say N.
diff --git a/kernel/trace/rv/Makefile b/kernel/trace/rv/Makefile
index 94498da35b37..a3502b7fe7f2 100644
--- a/kernel/trace/rv/Makefile
+++ b/kernel/trace/rv/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_RV_MON_NOMISS) += monitors/nomiss/nomiss.o
obj-$(CONFIG_RV_REACTORS) += rv_reactors.o
obj-$(CONFIG_RV_REACT_PRINTK) += reactor_printk.o
obj-$(CONFIG_RV_REACT_PANIC) += reactor_panic.o
+obj-$(CONFIG_RV_MONITORS_KUNIT_TEST) += rv_monitors_test.o
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.c b/kernel/trace/rv/monitors/nomiss/nomiss.c
index 8ead8783c29f..beb75965dc8d 100644
--- a/kernel/trace/rv/monitors/nomiss/nomiss.c
+++ b/kernel/trace/rv/monitors/nomiss/nomiss.c
@@ -291,3 +291,21 @@ module_exit(unregister_nomiss);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("nomiss: dl entities run to completion before their deadline.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "nomiss_kunit.h"
+
+const struct rv_nomiss_ops rv_nomiss_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .deadline_thresh = &deadline_thresh,
+ .handle_dl_replenish = handle_dl_replenish,
+ .handle_dl_throttle = handle_dl_throttle,
+ .handle_dl_server_stop = handle_dl_server_stop,
+ .handle_sched_switch = handle_sched_switch,
+ .handle_sched_wakeup = handle_sched_wakeup,
+ .handle_sys_enter = handle_sys_enter,
+ .handle_newtask = handle_newtask,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_nomiss_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss_kunit.c b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.c
new file mode 100644
index 000000000000..1f64249dfcce
--- /dev/null
+++ b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "nomiss_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_NOMISS)
+
+static void rv_test_nomiss(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_nomiss_ops.mon);
+
+ target->pid = 99;
+ target->policy = SCHED_DEADLINE;
+ target->dl.runtime = 10000;
+ target->dl.dl_deadline = 20000;
+
+ rv_nomiss_ops.handle_newtask(NULL, target, 0);
+
+ /* Task gets preempted and can't terminate before deadline */
+ rv_nomiss_ops.handle_sched_switch(NULL, 0, other, target, TASK_RUNNING);
+ rv_nomiss_ops.handle_dl_replenish(NULL, &target->dl, 0, DL_TASK);
+ udelay(10);
+ rv_nomiss_ops.handle_sched_switch(NULL, 0, target, other, TASK_RUNNING);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx) {
+ udelay(15 + *rv_nomiss_ops.deadline_thresh / 1000);
+ rv_nomiss_ops.handle_sched_switch(NULL, 0, other, target, TASK_RUNNING);
+ }
+}
+
+#else
+#define rv_test_nomiss rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss_kunit.h b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.h
new file mode 100644
index 000000000000..2be779c5dbaa
--- /dev/null
+++ b/kernel/trace/rv/monitors/nomiss/nomiss_kunit.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __NOMISS_KUNIT_H
+#define __NOMISS_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_nomiss_ops {
+ struct rv_kunit_mon mon;
+ const u64 *deadline_thresh;
+ void (*handle_dl_replenish)(void *data, struct sched_dl_entity *dl_se,
+ int cpu, u8 type);
+ void (*handle_dl_throttle)(void *data, struct sched_dl_entity *dl_se,
+ int cpu, u8 type);
+ void (*handle_dl_server_stop)(void *data, struct sched_dl_entity *dl_se,
+ int cpu, u8 type);
+ void (*handle_sched_switch)(void *data, bool preempt,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned int prev_state);
+ void (*handle_sched_wakeup)(void *data, struct task_struct *tsk);
+ void (*handle_sys_enter)(void *data, struct pt_regs *regs, long id);
+ void (*handle_newtask)(void *data, struct task_struct *task, u64 flags);
+} rv_nomiss_ops;
+#endif
+
+#endif /* __NOMISS_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/opid/opid.c b/kernel/trace/rv/monitors/opid/opid.c
index 3b6a85e815b8..9ae619f176fa 100644
--- a/kernel/trace/rv/monitors/opid/opid.c
+++ b/kernel/trace/rv/monitors/opid/opid.c
@@ -115,3 +115,15 @@ module_exit(unregister_opid);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("opid: operations with preemption and irq disabled.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "opid_kunit.h"
+
+const struct rv_opid_ops rv_opid_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_need_resched = handle_sched_need_resched,
+ .handle_sched_waking = handle_sched_waking,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_opid_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/opid/opid_kunit.c b/kernel/trace/rv/monitors/opid/opid_kunit.c
new file mode 100644
index 000000000000..3cb087a74241
--- /dev/null
+++ b/kernel/trace/rv/monitors/opid/opid_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "opid_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_OPID)
+
+static void rv_test_opid(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_opid_ops.mon);
+
+ /* Ensure we keep the same per-cpu monitor */
+ guard(migrate)();
+ KUNIT_EXPECT_TRUE(test, preemptible());
+
+ /* Wakeup with preemption and interrupts enabled */
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_opid_ops.handle_sched_waking(NULL, NULL);
+
+ /* Need resched with interrupts enabled */
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx) {
+ scoped_guard(preempt)
+ rv_opid_ops.handle_sched_need_resched(NULL, NULL, 0, TIF_NEED_RESCHED);
+ }
+}
+
+#else
+#define rv_test_opid rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/opid/opid_kunit.h b/kernel/trace/rv/monitors/opid/opid_kunit.h
new file mode 100644
index 000000000000..4969c6175957
--- /dev/null
+++ b/kernel/trace/rv/monitors/opid/opid_kunit.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __OPID_KUNIT_H
+#define __OPID_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_opid_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_need_resched)(void *data, struct task_struct *tsk, int cpu, int tif);
+ void (*handle_sched_waking)(void *data, struct task_struct *p);
+} rv_opid_ops;
+#endif
+
+#endif /* __OPID_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/sco/sco.c b/kernel/trace/rv/monitors/sco/sco.c
index 5a3bd5e16e62..1ef1b96e859d 100644
--- a/kernel/trace/rv/monitors/sco/sco.c
+++ b/kernel/trace/rv/monitors/sco/sco.c
@@ -83,3 +83,16 @@ module_exit(unregister_sco);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sco: scheduling context operations.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sco_kunit.h"
+
+const struct rv_sco_ops rv_sco_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_set_state = handle_sched_set_state,
+ .handle_schedule_entry = handle_schedule_entry,
+ .handle_schedule_exit = handle_schedule_exit,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sco_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sco/sco_kunit.c b/kernel/trace/rv/monitors/sco/sco_kunit.c
new file mode 100644
index 000000000000..5e59bcbfcf0b
--- /dev/null
+++ b/kernel/trace/rv/monitors/sco/sco_kunit.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "sco_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_SCO)
+
+static void rv_test_sco(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_sco_ops.mon);
+
+ /* Ensure we keep the same per-cpu monitor */
+ guard(migrate)();
+
+ /* Set state while scheduling */
+ rv_sco_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_sco_ops.handle_schedule_entry(NULL, false);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sco_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+}
+
+#else
+#define rv_test_sco rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sco/sco_kunit.h b/kernel/trace/rv/monitors/sco/sco_kunit.h
new file mode 100644
index 000000000000..567757df6b1d
--- /dev/null
+++ b/kernel/trace/rv/monitors/sco/sco_kunit.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __SCO_KUNIT_H
+#define __SCO_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sco_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_set_state)(void *data, struct task_struct *tsk, int state);
+ void (*handle_schedule_entry)(void *data, bool preempt);
+ void (*handle_schedule_exit)(void *data, bool is_switch);
+} rv_sco_ops;
+#endif
+
+#endif /* __SCO_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/sssw/sssw.c b/kernel/trace/rv/monitors/sssw/sssw.c
index a91321c890cd..fbfde32dc136 100644
--- a/kernel/trace/rv/monitors/sssw/sssw.c
+++ b/kernel/trace/rv/monitors/sssw/sssw.c
@@ -112,3 +112,17 @@ module_exit(unregister_sssw);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sssw: set state sleep and wakeup.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sssw_kunit.h"
+
+const struct rv_sssw_ops rv_sssw_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_sched_set_state = handle_sched_set_state,
+ .handle_sched_switch = handle_sched_switch,
+ .handle_sched_wakeup = handle_sched_wakeup,
+ .handle_signal_deliver = handle_signal_deliver,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sssw_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sssw/sssw_kunit.c b/kernel/trace/rv/monitors/sssw/sssw_kunit.c
new file mode 100644
index 000000000000..a95faf859c60
--- /dev/null
+++ b/kernel/trace/rv/monitors/sssw/sssw_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "sssw_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_SSSW)
+
+static void rv_test_sssw(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_sssw_ops.mon);
+
+ /* Suspend without setting to sleepable */
+ rv_sssw_ops.handle_sched_set_state(NULL, target, TASK_RUNNING);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sssw_ops.handle_sched_switch(NULL, 0, target, other, TASK_INTERRUPTIBLE);
+
+ /* Switch in after suspension without wakeup */
+ rv_sssw_ops.handle_sched_wakeup(NULL, target);
+ rv_sssw_ops.handle_sched_set_state(NULL, target, TASK_INTERRUPTIBLE);
+ rv_sssw_ops.handle_sched_switch(NULL, 0, target, other, TASK_INTERRUPTIBLE);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sssw_ops.handle_sched_switch(NULL, 0, other, target, TASK_RUNNING);
+}
+
+#else
+#define rv_test_sssw rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sssw/sssw_kunit.h b/kernel/trace/rv/monitors/sssw/sssw_kunit.h
new file mode 100644
index 000000000000..6513daa7afba
--- /dev/null
+++ b/kernel/trace/rv/monitors/sssw/sssw_kunit.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __SSSW_KUNIT_H
+#define __SSSW_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sssw_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_sched_set_state)(void *data, struct task_struct *tsk, int state);
+ void (*handle_sched_switch)(void *data, bool preempt,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned int prev_state);
+ void (*handle_sched_wakeup)(void *data, struct task_struct *p);
+ void (*handle_signal_deliver)(void *data, int sig,
+ struct kernel_siginfo *info,
+ struct k_sigaction *ka);
+} rv_sssw_ops;
+#endif
+
+#endif /* __SSSW_KUNIT_H */
diff --git a/kernel/trace/rv/monitors/sts/sts.c b/kernel/trace/rv/monitors/sts/sts.c
index ce031cbf202a..2a044cf925b1 100644
--- a/kernel/trace/rv/monitors/sts/sts.c
+++ b/kernel/trace/rv/monitors/sts/sts.c
@@ -152,3 +152,22 @@ module_exit(unregister_sts);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
MODULE_DESCRIPTION("sts: schedule implies task switch.");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "sts_kunit.h"
+
+const struct rv_sts_ops rv_sts_ops = {
+ .mon = RV_MON_OPS_INIT(),
+#ifdef CONFIG_X86_LOCAL_APIC
+ .handle_vector_irq_entry = handle_vector_irq_entry,
+#endif
+ .handle_irq_disable = handle_irq_disable,
+ .handle_irq_enable = handle_irq_enable,
+ .handle_irq_entry = handle_irq_entry,
+ .handle_sched_switch = handle_sched_switch,
+ .handle_schedule_entry = handle_schedule_entry,
+ .handle_schedule_exit = handle_schedule_exit,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_sts_ops);
+#endif
diff --git a/kernel/trace/rv/monitors/sts/sts_kunit.c b/kernel/trace/rv/monitors/sts/sts_kunit.c
new file mode 100644
index 000000000000..a07316fff091
--- /dev/null
+++ b/kernel/trace/rv/monitors/sts/sts_kunit.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+#include <trace/events/sched.h>
+#include "sts_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_STS)
+
+static void rv_test_sts(struct kunit *test)
+{
+ struct task_struct *target = rv_kunit_alloc_mock_task(test);
+ struct task_struct *other = rv_kunit_alloc_mock_task(test);
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ prepare_test(test, &rv_sts_ops.mon);
+ /* Per-CPU monitor, make sure we don't change CPU mid-test */
+ guard(migrate)();
+
+ /* Switch without disabling interrupts */
+ rv_sts_ops.handle_schedule_exit(NULL, false);
+ rv_sts_ops.handle_schedule_entry(NULL, false);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sts_ops.handle_sched_switch(NULL, 0, target, other, TASK_RUNNING);
+
+ rv_sts_ops.handle_schedule_exit(NULL, false);
+
+ /* Schedule from interrupt context */
+ rv_sts_ops.handle_schedule_entry(NULL, false);
+ rv_sts_ops.handle_irq_disable(NULL, 0, 0);
+ rv_sts_ops.handle_irq_entry(NULL, 0, NULL);
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_sts_ops.handle_sched_switch(NULL, 0, target, other, TASK_RUNNING);
+ rv_sts_ops.handle_irq_enable(NULL, 0, 0);
+}
+
+#else
+#define rv_test_sts rv_test_stub
+#endif
diff --git a/kernel/trace/rv/monitors/sts/sts_kunit.h b/kernel/trace/rv/monitors/sts/sts_kunit.h
new file mode 100644
index 000000000000..dede4e098c1f
--- /dev/null
+++ b/kernel/trace/rv/monitors/sts/sts_kunit.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __STS_KUNIT_H
+#define __STS_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_sts_ops {
+ struct rv_kunit_mon mon;
+#ifdef CONFIG_X86_LOCAL_APIC
+ void (*handle_vector_irq_entry)(void *data, int vector);
+#endif
+ void (*handle_irq_disable)(void *data, unsigned long ip, unsigned long parent_ip);
+ void (*handle_irq_enable)(void *data, unsigned long ip, unsigned long parent_ip);
+ void (*handle_irq_entry)(void *data, int irq, struct irqaction *action);
+ void (*handle_sched_switch)(void *data, bool preempt,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned int prev_state);
+ void (*handle_schedule_entry)(void *data, bool preempt);
+ void (*handle_schedule_exit)(void *data, bool is_switch);
+} rv_sts_ops;
+#endif
+
+#endif /* __STS_KUNIT_H */
diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c
index 9d58c730821d..cfe950fef3b4 100644
--- a/kernel/trace/rv/rv.c
+++ b/kernel/trace/rv/rv.c
@@ -854,3 +854,43 @@ int __init rv_init_interface(void)
return 0;
}
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <rv/kunit.h>
+#include <kunit/visibility.h>
+
+/*
+ * rv_set_testing - ensure mutual exclusion between KUnit tests and real monitors
+ *
+ * KUnit tests for RV monitors rely on stubs that are incompatible with
+ * the execution of real monitors. Ensure mutual exclusion by acquiring
+ * the rv_interface_lock for the duration of the suite.
+ *
+ * Returns 0 on success, -EBUSY if any real monitor is already enabled.
+ */
+int rv_set_testing(struct kunit_suite *suite)
+{
+ struct rv_monitor *mon;
+
+ mutex_lock(&rv_interface_lock);
+
+ list_for_each_entry(mon, &rv_monitors_list, list) {
+ if (mon->enabled) {
+ mutex_unlock(&rv_interface_lock);
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(rv_set_testing);
+
+/*
+ * rv_clear_testing - allow real monitors to run again after KUnit tests
+ */
+void rv_clear_testing(struct kunit_suite *suite)
+{
+ mutex_unlock(&rv_interface_lock);
+}
+EXPORT_SYMBOL_IF_KUNIT(rv_clear_testing);
+#endif
diff --git a/kernel/trace/rv/rv_monitors_test.c b/kernel/trace/rv/rv_monitors_test.c
new file mode 100644
index 000000000000..82c798f1875a
--- /dev/null
+++ b/kernel/trace/rv/rv_monitors_test.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
+ *
+ * RV monitor kunit tests:
+ * Tests the RV monitors by triggering fake events to verify monitor
+ * behavior and reactions. Tests start from the first defined event and
+ * trigger events in order to verify error detection.
+ */
+#include <rv/kunit.h>
+#include <kunit/test-bug.h>
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include "rv.h"
+
+/*
+ * An easy way to pass the context is to use kunit_get_current_test()->priv,
+ * but this doesn't always work (e.g. a reactor running from another context
+ * like softirq). Store the current value here whenever a test is running.
+ */
+static struct rv_kunit_ctx *active_ctx;
+
+__printf(1, 0)
+static void rv_kunit_mock_react(const char *msg, va_list args)
+{
+ if (active_ctx)
+ ++active_ctx->reactions;
+}
+
+/*
+ * teardown_test - Disable the monitor for a kunit test
+ *
+ * Since per-task monitors are special, make sure we reset all the ones we
+ * started manually here, if required.
+ */
+void teardown_test(void *arg)
+{
+ const struct rv_kunit_mon *mon = arg;
+ struct kunit *test = kunit_get_current_test();
+
+ if (test) {
+ struct rv_kunit_ctx *ctx = test->priv;
+
+ RV_KUNIT_EXPECT_NO_REACTION(test, ctx);
+
+ if (mon->is_per_task && mon->task_reset) {
+ for (int i = 0; i < ctx->mock_task_count; i++)
+ mon->task_reset(ctx->mock_tasks[i]);
+ synchronize_rcu();
+ }
+ }
+
+ mon->rv_this->enabled = 0;
+
+ mon->rv_this->react = NULL;
+ active_ctx = NULL;
+
+ if (mon->is_per_task)
+ *mon->task_slot = RV_PER_TASK_MONITOR_INIT;
+ else
+ mon->monitor_destroy();
+}
+
+/*
+ * prepare_test - Enable the monitor for a kunit test
+ *
+ * Do the bare minimum to set up the monitor, per-task monitors are special as
+ * "real" initialisation/destruction iterates over real tasks, and may register
+ * handlers. All we need is to select the right slot in the task_struct.
+ */
+void prepare_test(struct kunit *test, const struct rv_kunit_mon *mon)
+{
+ KUNIT_ASSERT_FALSE(test, mon->rv_this->enabled);
+
+ active_ctx = test->priv;
+ mon->rv_this->react = rv_kunit_mock_react;
+
+ if (mon->is_per_task)
+ *mon->task_slot = 0;
+ else
+ KUNIT_ASSERT_EQ(test, mon->monitor_init(), 0);
+
+ mon->rv_this->enabled = 1;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test, teardown_test, (void *)mon));
+}
+
+struct task_struct *rv_kunit_alloc_mock_task(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ struct task_struct *tsk;
+
+ KUNIT_ASSERT_LT(test, ctx->mock_task_count, RV_KUNIT_MAX_MOCK_TASKS);
+
+ tsk = kunit_kzalloc(test, sizeof(struct task_struct), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, tsk);
+
+ if (!IS_ENABLED(CONFIG_THREAD_INFO_IN_TASK)) {
+ tsk->stack = kunit_kzalloc(test, sizeof(struct thread_info), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, tsk->stack);
+ }
+
+ ctx->mock_tasks[ctx->mock_task_count++] = tsk;
+ return tsk;
+}
+
+static int rv_mon_test_init(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ test->priv = ctx;
+
+ return 0;
+}
+
+static void __maybe_unused rv_test_stub(struct kunit *test)
+{
+ kunit_skip(test, "Monitor not enabled\n");
+}
+
+/*
+ * rv_test_dummy - test reactions work as expected
+ */
+static void rv_test_dummy(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ static struct rv_monitor dummy_monitor = {
+ .name = "dummy",
+ .react = rv_kunit_mock_react,
+ };
+
+ active_ctx = ctx;
+
+ RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ rv_react(&dummy_monitor, "dummy");
+ RV_KUNIT_EXPECT_NO_REACTION(test, ctx);
+
+ active_ctx = NULL;
+}
+
+#include "monitors/sco/sco_kunit.c"
+#include "monitors/sssw/sssw_kunit.c"
+#include "monitors/sts/sts_kunit.c"
+#include "monitors/opid/opid_kunit.c"
+#include "monitors/nomiss/nomiss_kunit.c"
+
+static struct kunit_case rv_mon_test_cases[] = {
+ KUNIT_CASE(rv_test_dummy),
+ KUNIT_CASE(rv_test_sco),
+ KUNIT_CASE(rv_test_sssw),
+ KUNIT_CASE(rv_test_sts),
+ KUNIT_CASE(rv_test_opid),
+ KUNIT_CASE(rv_test_nomiss),
+ {}
+};
+
+static struct kunit_suite rv_mon_test_suite = {
+ .name = "rv_mon",
+ .suite_init = rv_set_testing,
+ .suite_exit = rv_clear_testing,
+ .init = rv_mon_test_init,
+ .test_cases = rv_mon_test_cases,
+};
+
+kunit_test_suites(&rv_mon_test_suite);
+
+MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
+MODULE_DESCRIPTION("RV monitor kunit tests: test monitors by triggering reactions");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
--
2.55.0
^ permalink raw reply related
* [PATCH v5 11/17] rv: Export task monitor slot and react symbols
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco,
Masami Hiramatsu
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
Export rv_get_task_monitor_slot, rv_put_task_monitor_slot, and rv_react
to GPL modules so they can be accessed by KUnit and future monitors
built as kernel modules.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
kernel/trace/rv/rv.c | 2 ++
kernel/trace/rv/rv_reactors.c | 1 +
2 files changed, 3 insertions(+)
diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c
index ee4e68102f17..9d58c730821d 100644
--- a/kernel/trace/rv/rv.c
+++ b/kernel/trace/rv/rv.c
@@ -189,6 +189,7 @@ int rv_get_task_monitor_slot(void)
return -EINVAL;
}
+EXPORT_SYMBOL_GPL(rv_get_task_monitor_slot);
void rv_put_task_monitor_slot(int slot)
{
@@ -205,6 +206,7 @@ void rv_put_task_monitor_slot(int slot)
task_monitor_count--;
task_monitor_slots[slot] = false;
}
+EXPORT_SYMBOL_GPL(rv_put_task_monitor_slot);
/*
* Monitors with a parent are nested,
diff --git a/kernel/trace/rv/rv_reactors.c b/kernel/trace/rv/rv_reactors.c
index 460af07f7aba..2f5fc8d18dea 100644
--- a/kernel/trace/rv/rv_reactors.c
+++ b/kernel/trace/rv/rv_reactors.c
@@ -479,3 +479,4 @@ void rv_react(struct rv_monitor *monitor, const char *msg, ...)
va_end(args);
}
+EXPORT_SYMBOL_GPL(rv_react);
--
2.55.0
^ permalink raw reply related
* [PATCH v5 10/17] verification/rvgen: Add selftests for rvgen kunit
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
The rvgen kunit command patches monitor files and adds necessary
definitions for kunit tests.
Add a test case validating its behaviour on dummy generated files and
comparing it against reference files, like it's done for rvgen monitor.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
.../rvgen/tests/golden/test_bak_kunit/Kconfig | 9 +
.../golden/test_bak_kunit/test_bak_kunit.c | 107 +++++++
.../golden/test_bak_kunit/test_bak_kunit.h | 108 ++++++++
.../test_bak_kunit/test_bak_kunit_kunit.c | 33 +++
.../test_bak_kunit/test_bak_kunit_kunit.c.bak | 1 +
.../test_bak_kunit/test_bak_kunit_kunit.h | 22 ++
.../test_bak_kunit/test_bak_kunit_trace.h | 14 +
.../rvgen/tests/golden/test_da_kunit/Kconfig | 9 +
.../golden/test_da_kunit/test_da_kunit.c | 107 +++++++
.../golden/test_da_kunit/test_da_kunit.h | 47 ++++
.../test_da_kunit/test_da_kunit_kunit.c | 33 +++
.../test_da_kunit/test_da_kunit_kunit.h | 23 ++
.../test_da_kunit/test_da_kunit_trace.h | 15 +
.../rvgen/tests/golden/test_ha_kunit/Kconfig | 9 +
.../golden/test_ha_kunit/test_ha_kunit.c | 260 ++++++++++++++++++
.../golden/test_ha_kunit/test_ha_kunit.h | 88 ++++++
.../test_ha_kunit/test_ha_kunit_kunit.c | 33 +++
.../test_ha_kunit/test_ha_kunit_kunit.h | 24 ++
.../test_ha_kunit/test_ha_kunit_trace.h | 19 ++
.../rvgen/tests/golden/test_ltl_kunit/Kconfig | 9 +
.../golden/test_ltl_kunit/test_ltl_kunit.c | 107 +++++++
.../golden/test_ltl_kunit/test_ltl_kunit.h | 108 ++++++++
.../test_ltl_kunit/test_ltl_kunit_kunit.c | 33 +++
.../test_ltl_kunit/test_ltl_kunit_kunit.h | 22 ++
.../test_ltl_kunit/test_ltl_kunit_trace.h | 14 +
tools/verification/rvgen/tests/rvgen_kunit.t | 41 +++
26 files changed, 1295 insertions(+)
create mode 100644 tools/verification/rvgen/tests/golden/test_bak_kunit/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c.bak
create mode 100644 tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/test_da_kunit/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ha_kunit/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl_kunit/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.c
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_trace.h
create mode 100644 tools/verification/rvgen/tests/rvgen_kunit.t
diff --git a/tools/verification/rvgen/tests/golden/test_bak_kunit/Kconfig b/tools/verification/rvgen/tests/golden/test_bak_kunit/Kconfig
new file mode 100644
index 000000000000..175a416f8b18
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_bak_kunit/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_TEST_BAK_KUNIT
+ depends on RV
+ # XXX: add dependencies if there
+ select LTL_MON_EVENTS_ID
+ bool "test_bak_kunit monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.c b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.c
new file mode 100644
index 000000000000..16579c1c6910
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.c
@@ -0,0 +1,107 @@
+// 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 "test_bak_kunit"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#include "test_bak_kunit.h"
+#include <rv/ltl_monitor.h>
+
+static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon)
+{
+ /*
+ * This is called everytime the Buchi automaton is triggered.
+ *
+ * This function could be used to fetch the atomic propositions which
+ * are expensive to trace. It is possible only if the atomic proposition
+ * does not need to be updated at precise time.
+ *
+ * It is recommended to use tracepoints and ltl_atom_update() instead.
+ */
+}
+
+static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
+{
+ /*
+ * This should initialize as many atomic propositions as possible.
+ *
+ * @task_creation indicates whether the task is being created. This is
+ * false if the task is already running before the monitor is enabled.
+ */
+ ltl_atom_set(mon, LTL_EVENT_A, true/false);
+ ltl_atom_set(mon, LTL_EVENT_B, true/false);
+}
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ */
+static void handle_example_event(void *data, /* XXX: fill header */)
+{
+ ltl_atom_update(task, LTL_EVENT_A, true/false);
+}
+
+static int enable_test_bak_kunit(void)
+{
+ int retval;
+
+ retval = ltl_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("test_bak_kunit", /* XXX: tracepoint */, handle_example_event);
+
+ return 0;
+}
+
+static void disable_test_bak_kunit(void)
+{
+ rv_detach_trace_probe("test_bak_kunit", /* XXX: tracepoint */, handle_example_event);
+
+ ltl_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "test_bak_kunit",
+ .description = "auto-generated",
+ .enable = enable_test_bak_kunit,
+ .disable = disable_test_bak_kunit,
+};
+
+static int __init register_test_bak_kunit(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_test_bak_kunit(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_test_bak_kunit);
+module_exit(unregister_test_bak_kunit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_bak_kunit: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.h b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.h
new file mode 100644
index 000000000000..2bfe4e37cea7
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * C implementation of Buchi automaton, automatically generated by
+ * tools/verification/rvgen from the linear temporal logic specification.
+ * For further information, see kernel documentation:
+ * Documentation/trace/rv/linear_temporal_logic.rst
+ */
+
+#include <linux/rv.h>
+
+#define MONITOR_NAME test_bak_kunit
+
+enum ltl_atom {
+ LTL_EVENT_A,
+ LTL_EVENT_B,
+ LTL_NUM_ATOM
+};
+static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);
+
+static const char *ltl_atom_str(enum ltl_atom atom)
+{
+ static const char *const names[] = {
+ "ev_a",
+ "ev_b",
+ };
+
+ return names[atom];
+}
+
+enum ltl_buchi_state {
+ S0,
+ S1,
+ S2,
+ S3,
+ S4,
+ RV_NUM_BA_STATES
+};
+static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);
+
+static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ if (val1)
+ __set_bit(S0, mon->states);
+ if (true)
+ __set_bit(S1, mon->states);
+ if (event_b)
+ __set_bit(S4, mon->states);
+}
+
+static void
+ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ switch (state) {
+ case S0:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S1:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S2:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S3:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S4:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ }
+}
diff --git a/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c
new file mode 100644
index 000000000000..e2b9354034cc
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+/*
+ * XXX: include required headers, e.g.,
+ * #include <linux/sched.h>
+ */
+#include "test_bak_kunit_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_TEST_BAK_KUNIT)
+
+static void rv_test_test_bak_kunit(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ /*
+ * If you need to create task_structs with rv_kunit_alloc_mock_task()
+ * do it BEFORE preparing the test.
+ */
+
+ prepare_test(test, &rv_test_bak_kunit_ops.mon);
+
+ /*
+ * XXX: write the test here
+ * e.g.
+ * RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ * rv_test_bak_kunit_ops.handle_event(args);
+ */
+}
+
+#else
+#define rv_test_test_bak_kunit rv_test_stub
+#endif
diff --git a/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c.bak b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c.bak
new file mode 100644
index 000000000000..f747925bf542
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.c.bak
@@ -0,0 +1 @@
+DUMMY
diff --git a/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.h b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.h
new file mode 100644
index 000000000000..585c4803be23
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_kunit.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __TEST_BAK_KUNIT_KUNIT_H
+#define __TEST_BAK_KUNIT_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_test_bak_kunit_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_example_event)(void *data, /* XXX: fill header */);
+} rv_test_bak_kunit_ops;
+#endif
+
+#endif /* __TEST_BAK_KUNIT_KUNIT_H */
diff --git a/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_trace.h b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_trace.h
new file mode 100644
index 000000000000..b984208838c4
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_bak_kunit/test_bak_kunit_trace.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_TEST_BAK_KUNIT
+DEFINE_EVENT(event_ltl_monitor_id, event_test_bak_kunit,
+ TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next),
+ TP_ARGS(task, states, atoms, next));
+DEFINE_EVENT(error_ltl_monitor_id, error_test_bak_kunit,
+ TP_PROTO(struct task_struct *task),
+ TP_ARGS(task));
+#endif /* CONFIG_RV_MON_TEST_BAK_KUNIT */
diff --git a/tools/verification/rvgen/tests/golden/test_da_kunit/Kconfig b/tools/verification/rvgen/tests/golden/test_da_kunit/Kconfig
new file mode 100644
index 000000000000..6d664ba5624d
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da_kunit/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_TEST_DA_KUNIT
+ depends on RV
+ # XXX: add dependencies if there
+ select DA_MON_EVENTS_IMPLICIT
+ bool "test_da_kunit monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.c b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.c
new file mode 100644
index 000000000000..effd26548b07
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.c
@@ -0,0 +1,107 @@
+// 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 "test_da_kunit"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_PER_CPU
+#include "test_da_kunit.h"
+#include <rv/da_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+static void handle_event_1(void *data, /* XXX: fill header */)
+{
+ da_handle_event(event_1_test_da_kunit);
+}
+
+static void handle_event_2(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event always leads to the initial state */
+ da_handle_start_event(event_2_test_da_kunit);
+}
+
+static int enable_test_da_kunit(void)
+{
+ int retval;
+
+ retval = da_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("test_da_kunit", /* XXX: tracepoint */, handle_event_1);
+ rv_attach_trace_probe("test_da_kunit", /* XXX: tracepoint */, handle_event_2);
+
+ return 0;
+}
+
+static void disable_test_da_kunit(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("test_da_kunit", /* XXX: tracepoint */, handle_event_1);
+ rv_detach_trace_probe("test_da_kunit", /* XXX: tracepoint */, handle_event_2);
+
+ da_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "test_da_kunit",
+ .description = "auto-generated",
+ .enable = enable_test_da_kunit,
+ .disable = disable_test_da_kunit,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_test_da_kunit(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_test_da_kunit(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_test_da_kunit);
+module_exit(unregister_test_da_kunit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_da_kunit: auto-generated");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "test_da_kunit_kunit.h"
+
+const struct rv_test_da_kunit_ops rv_test_da_kunit_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_event_1 = handle_event_1,
+ .handle_event_2 = handle_event_2,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_test_da_kunit_ops);
+#endif
diff --git a/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.h b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.h
new file mode 100644
index 000000000000..290a9454caa4
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of test_da_kunit automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME test_da_kunit
+
+enum states_test_da_kunit {
+ state_a_test_da_kunit,
+ state_b_test_da_kunit,
+ state_max_test_da_kunit,
+};
+
+#define INVALID_STATE state_max_test_da_kunit
+
+enum events_test_da_kunit {
+ event_1_test_da_kunit,
+ event_2_test_da_kunit,
+ event_max_test_da_kunit,
+};
+
+struct automaton_test_da_kunit {
+ char *state_names[state_max_test_da_kunit];
+ char *event_names[event_max_test_da_kunit];
+ unsigned char function[state_max_test_da_kunit][event_max_test_da_kunit];
+ unsigned char initial_state;
+ bool final_states[state_max_test_da_kunit];
+};
+
+static const struct automaton_test_da_kunit automaton_test_da_kunit = {
+ .state_names = {
+ "state_a",
+ "state_b",
+ },
+ .event_names = {
+ "event_1",
+ "event_2",
+ },
+ .function = {
+ { state_b_test_da_kunit, state_a_test_da_kunit },
+ { INVALID_STATE, state_a_test_da_kunit },
+ },
+ .initial_state = state_a_test_da_kunit,
+ .final_states = { 1, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.c b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.c
new file mode 100644
index 000000000000..17826a5c47c8
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+/*
+ * XXX: include required headers, e.g.,
+ * #include <linux/sched.h>
+ */
+#include "test_da_kunit_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_TEST_DA_KUNIT)
+
+static void rv_test_test_da_kunit(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ /*
+ * If you need to create task_structs with rv_kunit_alloc_mock_task()
+ * do it BEFORE preparing the test.
+ */
+
+ prepare_test(test, &rv_test_da_kunit_ops.mon);
+
+ /*
+ * XXX: write the test here
+ * e.g.
+ * RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ * rv_test_da_kunit_ops.handle_event(args);
+ */
+}
+
+#else
+#define rv_test_test_da_kunit rv_test_stub
+#endif
diff --git a/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.h b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.h
new file mode 100644
index 000000000000..0094215ff4fb
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_kunit.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __TEST_DA_KUNIT_KUNIT_H
+#define __TEST_DA_KUNIT_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_test_da_kunit_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_event_1)(void *data, /* XXX: fill header */);
+ void (*handle_event_2)(void *data, /* XXX: fill header */);
+} rv_test_da_kunit_ops;
+#endif
+
+#endif /* __TEST_DA_KUNIT_KUNIT_H */
diff --git a/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_trace.h b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_trace.h
new file mode 100644
index 000000000000..16804a79e834
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da_kunit/test_da_kunit_trace.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_TEST_DA_KUNIT
+DEFINE_EVENT(event_da_monitor, event_test_da_kunit,
+ TP_PROTO(char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor, error_test_da_kunit,
+ TP_PROTO(char *state, char *event),
+ TP_ARGS(state, event));
+#endif /* CONFIG_RV_MON_TEST_DA_KUNIT */
diff --git a/tools/verification/rvgen/tests/golden/test_ha_kunit/Kconfig b/tools/verification/rvgen/tests/golden/test_ha_kunit/Kconfig
new file mode 100644
index 000000000000..6c48770ace1a
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha_kunit/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_TEST_HA_KUNIT
+ depends on RV
+ # XXX: add dependencies if there
+ select HA_MON_EVENTS_ID
+ bool "test_ha_kunit monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.c b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.c
new file mode 100644
index 000000000000..99d6244c2539
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.c
@@ -0,0 +1,260 @@
+// 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 "test_ha_kunit"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_PER_TASK
+/* XXX: If the monitor has several instances, consider HA_TIMER_WHEEL */
+#define HA_TIMER_TYPE HA_TIMER_HRTIMER
+#include "test_ha_kunit.h"
+#include <rv/ha_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+#define BAR_NS(ha_mon) /* XXX: what is BAR_NS(ha_mon)? */
+
+#define FOO_NS /* XXX: what is FOO_NS? */
+
+static inline u64 bar_ns(struct ha_monitor *ha_mon)
+{
+ return /* XXX: what is bar_ns(ha_mon)? */;
+}
+
+static u64 foo_ns = /* XXX: default value */;
+module_param(foo_ns, ullong, 0644);
+
+/*
+ * These functions define how to read and reset the environment variable.
+ *
+ * Common environment variables like ns-based and jiffy-based clocks have
+ * pre-define getters and resetters you can use. The parser can infer the type
+ * of the environment variable if you supply a measure unit in the constraint.
+ * If you define your own functions, make sure to add appropriate memory
+ * barriers if required.
+ * Some environment variables don't require a storage as they read a system
+ * state (e.g. preemption count). Those variables are never reset, so we don't
+ * define a reset function on monitors only relying on this type of variables.
+ */
+static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_test_ha_kunit env, u64 time_ns)
+{
+ if (env == clk_test_ha_kunit)
+ return ha_get_clk_ns(ha_mon, env, time_ns);
+ else if (env == env1_test_ha_kunit)
+ return /* XXX: how do I read env1? */
+ else if (env == env2_test_ha_kunit)
+ return /* XXX: how do I read env2? */
+ return ENV_INVALID_VALUE;
+}
+
+static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_test_ha_kunit env, u64 time_ns)
+{
+ if (env == clk_test_ha_kunit)
+ ha_reset_clk_ns(ha_mon, env, time_ns);
+}
+
+/*
+ * These functions are used to validate state transitions.
+ *
+ * They are generated by parsing the model, there is usually no need to change them.
+ * If the monitor requires a timer, there are functions responsible to arm it when
+ * the next state has a constraint, cancel it in any other case and to check
+ * that it didn't expire before the callback run. Transitions to the same state
+ * without a reset never affect timers.
+ * Due to the different representations between invariants and guards, there is
+ * a function to convert it in case invariants or guards are reachable from
+ * another invariant without reset. Those are not present if not required in
+ * the model. This is all automatic but is worth checking because it may show
+ * errors in the model (e.g. missing resets).
+ */
+static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (curr_state == S0_test_ha_kunit)
+ return ha_check_invariant_ns(ha_mon, clk_test_ha_kunit, time_ns);
+ else if (curr_state == S2_test_ha_kunit)
+ return ha_check_invariant_ns(ha_mon, clk_test_ha_kunit, time_ns);
+ return true;
+}
+
+static inline void ha_convert_inv_guard(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (curr_state == next_state)
+ return;
+ if (curr_state == S2_test_ha_kunit)
+ ha_inv_to_guard(ha_mon, clk_test_ha_kunit, BAR_NS(ha_mon), time_ns);
+}
+
+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 == S0_test_ha_kunit && event == event0_test_ha_kunit)
+ ha_reset_env(ha_mon, clk_test_ha_kunit, time_ns);
+ else if (curr_state == S0_test_ha_kunit && event == event1_test_ha_kunit)
+ ha_reset_env(ha_mon, clk_test_ha_kunit, time_ns);
+ else if (curr_state == S1_test_ha_kunit && event == event0_test_ha_kunit)
+ ha_reset_env(ha_mon, clk_test_ha_kunit, time_ns);
+ else if (curr_state == S1_test_ha_kunit && event == event2_test_ha_kunit) {
+ res = ha_get_env(ha_mon, env1_test_ha_kunit, time_ns) == 0ull;
+ ha_reset_env(ha_mon, clk_test_ha_kunit, time_ns);
+ } else if (curr_state == S2_test_ha_kunit && event == event1_test_ha_kunit)
+ res = ha_monitor_env_invalid(ha_mon, clk_test_ha_kunit) ||
+ ha_get_env(ha_mon, clk_test_ha_kunit, time_ns) < foo_ns;
+ else if (curr_state == S3_test_ha_kunit && event == event0_test_ha_kunit)
+ res = ha_monitor_env_invalid(ha_mon, clk_test_ha_kunit) ||
+ (ha_get_env(ha_mon, clk_test_ha_kunit, time_ns) < FOO_NS &&
+ ha_get_env(ha_mon, env2_test_ha_kunit, time_ns) == 0ull);
+ else if (curr_state == S3_test_ha_kunit && event == event1_test_ha_kunit) {
+ res = ha_monitor_env_invalid(ha_mon, clk_test_ha_kunit) ||
+ (ha_get_env(ha_mon, clk_test_ha_kunit, time_ns) < 5000ull &&
+ ha_get_env(ha_mon, env1_test_ha_kunit, time_ns) == 1ull);
+ ha_reset_env(ha_mon, clk_test_ha_kunit, time_ns);
+ }
+ return res;
+}
+
+static inline void ha_setup_invariants(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (next_state == curr_state && event != event0_test_ha_kunit)
+ return;
+ if (next_state == S0_test_ha_kunit)
+ ha_start_timer_ns(ha_mon, clk_test_ha_kunit, bar_ns(ha_mon), time_ns);
+ else if (next_state == S2_test_ha_kunit)
+ ha_start_timer_ns(ha_mon, clk_test_ha_kunit, BAR_NS(ha_mon), time_ns);
+ else if (curr_state == S0_test_ha_kunit)
+ ha_cancel_timer(ha_mon);
+ else if (curr_state == S2_test_ha_kunit)
+ ha_cancel_timer(ha_mon);
+}
+
+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_invariants(ha_mon, curr_state, event, next_state, time_ns))
+ return false;
+
+ ha_convert_inv_guard(ha_mon, curr_state, event, next_state, time_ns);
+
+ if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
+ return false;
+
+ ha_setup_invariants(ha_mon, curr_state, event, next_state, time_ns);
+
+ return true;
+}
+
+static void handle_event0(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event always leads to the initial state */
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_start_event(p, event0_test_ha_kunit);
+}
+
+static void handle_event1(void *data, /* XXX: fill header */)
+{
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_event(p, event1_test_ha_kunit);
+}
+
+static void handle_event2(void *data, /* XXX: fill header */)
+{
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_event(p, event2_test_ha_kunit);
+}
+
+static int enable_test_ha_kunit(void)
+{
+ int retval;
+
+ retval = ha_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("test_ha_kunit", /* XXX: tracepoint */, handle_event0);
+ rv_attach_trace_probe("test_ha_kunit", /* XXX: tracepoint */, handle_event1);
+ rv_attach_trace_probe("test_ha_kunit", /* XXX: tracepoint */, handle_event2);
+
+ return 0;
+}
+
+static void disable_test_ha_kunit(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("test_ha_kunit", /* XXX: tracepoint */, handle_event0);
+ rv_detach_trace_probe("test_ha_kunit", /* XXX: tracepoint */, handle_event1);
+ rv_detach_trace_probe("test_ha_kunit", /* XXX: tracepoint */, handle_event2);
+
+ ha_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "test_ha_kunit",
+ .description = "auto-generated",
+ .enable = enable_test_ha_kunit,
+ .disable = disable_test_ha_kunit,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_test_ha_kunit(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_test_ha_kunit(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_test_ha_kunit);
+module_exit(unregister_test_ha_kunit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_ha_kunit: auto-generated");
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "test_ha_kunit_kunit.h"
+
+const struct rv_test_ha_kunit_ops rv_test_ha_kunit_ops = {
+ .mon = RV_MON_OPS_INIT(),
+ .handle_event0 = handle_event0,
+ .handle_event1 = handle_event1,
+ .handle_event2 = handle_event2,
+};
+EXPORT_SYMBOL_IF_KUNIT(rv_test_ha_kunit_ops);
+#endif
diff --git a/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.h b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.h
new file mode 100644
index 000000000000..5c428f818bdf
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of test_ha_kunit automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME test_ha_kunit
+
+enum states_test_ha_kunit {
+ S0_test_ha_kunit,
+ S1_test_ha_kunit,
+ S2_test_ha_kunit,
+ S3_test_ha_kunit,
+ state_max_test_ha_kunit,
+};
+
+#define INVALID_STATE state_max_test_ha_kunit
+
+enum events_test_ha_kunit {
+ event0_test_ha_kunit,
+ event1_test_ha_kunit,
+ event2_test_ha_kunit,
+ event_max_test_ha_kunit,
+};
+
+enum envs_test_ha_kunit {
+ clk_test_ha_kunit,
+ env1_test_ha_kunit,
+ env2_test_ha_kunit,
+ env_max_test_ha_kunit,
+ env_max_stored_test_ha_kunit = env1_test_ha_kunit,
+};
+
+_Static_assert(env_max_stored_test_ha_kunit <= MAX_HA_ENV_LEN, "Not enough slots");
+#define HA_CLK_NS
+
+struct automaton_test_ha_kunit {
+ char *state_names[state_max_test_ha_kunit];
+ char *event_names[event_max_test_ha_kunit];
+ char *env_names[env_max_test_ha_kunit];
+ unsigned char function[state_max_test_ha_kunit][event_max_test_ha_kunit];
+ unsigned char initial_state;
+ bool final_states[state_max_test_ha_kunit];
+};
+
+static const struct automaton_test_ha_kunit automaton_test_ha_kunit = {
+ .state_names = {
+ "S0",
+ "S1",
+ "S2",
+ "S3",
+ },
+ .event_names = {
+ "event0",
+ "event1",
+ "event2",
+ },
+ .env_names = {
+ "clk",
+ "env1",
+ "env2",
+ },
+ .function = {
+ {
+ S0_test_ha_kunit,
+ S1_test_ha_kunit,
+ INVALID_STATE,
+ },
+ {
+ S0_test_ha_kunit,
+ INVALID_STATE,
+ S2_test_ha_kunit,
+ },
+ {
+ INVALID_STATE,
+ S2_test_ha_kunit,
+ S3_test_ha_kunit,
+ },
+ {
+ S0_test_ha_kunit,
+ S1_test_ha_kunit,
+ INVALID_STATE,
+ },
+ },
+ .initial_state = S0_test_ha_kunit,
+ .final_states = { 1, 0, 0, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.c b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.c
new file mode 100644
index 000000000000..6214a4aa6d25
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+/*
+ * XXX: include required headers, e.g.,
+ * #include <linux/sched.h>
+ */
+#include "test_ha_kunit_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_TEST_HA_KUNIT)
+
+static void rv_test_test_ha_kunit(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ /*
+ * If you need to create task_structs with rv_kunit_alloc_mock_task()
+ * do it BEFORE preparing the test.
+ */
+
+ prepare_test(test, &rv_test_ha_kunit_ops.mon);
+
+ /*
+ * XXX: write the test here
+ * e.g.
+ * RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ * rv_test_ha_kunit_ops.handle_event(args);
+ */
+}
+
+#else
+#define rv_test_test_ha_kunit rv_test_stub
+#endif
diff --git a/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.h b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.h
new file mode 100644
index 000000000000..0b2030cb644a
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_kunit.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __TEST_HA_KUNIT_KUNIT_H
+#define __TEST_HA_KUNIT_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_test_ha_kunit_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_event0)(void *data, /* XXX: fill header */);
+ void (*handle_event1)(void *data, /* XXX: fill header */);
+ void (*handle_event2)(void *data, /* XXX: fill header */);
+} rv_test_ha_kunit_ops;
+#endif
+
+#endif /* __TEST_HA_KUNIT_KUNIT_H */
diff --git a/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_trace.h b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_trace.h
new file mode 100644
index 000000000000..6c13ee0068d3
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha_kunit/test_ha_kunit_trace.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_TEST_HA_KUNIT
+DEFINE_EVENT(event_da_monitor_id, event_test_ha_kunit,
+ TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(id, state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor_id, error_test_ha_kunit,
+ TP_PROTO(int id, char *state, char *event),
+ TP_ARGS(id, state, event));
+
+DEFINE_EVENT(error_env_da_monitor_id, error_env_test_ha_kunit,
+ TP_PROTO(int id, char *state, char *event, char *env),
+ TP_ARGS(id, state, event, env));
+#endif /* CONFIG_RV_MON_TEST_HA_KUNIT */
diff --git a/tools/verification/rvgen/tests/golden/test_ltl_kunit/Kconfig b/tools/verification/rvgen/tests/golden/test_ltl_kunit/Kconfig
new file mode 100644
index 000000000000..3e334c344261
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl_kunit/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_TEST_LTL_KUNIT
+ depends on RV
+ # XXX: add dependencies if there
+ select LTL_MON_EVENTS_ID
+ bool "test_ltl_kunit monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.c b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.c
new file mode 100644
index 000000000000..c1d58ce435a8
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.c
@@ -0,0 +1,107 @@
+// 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 "test_ltl_kunit"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#include "test_ltl_kunit.h"
+#include <rv/ltl_monitor.h>
+
+static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon)
+{
+ /*
+ * This is called everytime the Buchi automaton is triggered.
+ *
+ * This function could be used to fetch the atomic propositions which
+ * are expensive to trace. It is possible only if the atomic proposition
+ * does not need to be updated at precise time.
+ *
+ * It is recommended to use tracepoints and ltl_atom_update() instead.
+ */
+}
+
+static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
+{
+ /*
+ * This should initialize as many atomic propositions as possible.
+ *
+ * @task_creation indicates whether the task is being created. This is
+ * false if the task is already running before the monitor is enabled.
+ */
+ ltl_atom_set(mon, LTL_EVENT_A, true/false);
+ ltl_atom_set(mon, LTL_EVENT_B, true/false);
+}
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ */
+static void handle_example_event(void *data, /* XXX: fill header */)
+{
+ ltl_atom_update(task, LTL_EVENT_A, true/false);
+}
+
+static int enable_test_ltl_kunit(void)
+{
+ int retval;
+
+ retval = ltl_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("test_ltl_kunit", /* XXX: tracepoint */, handle_example_event);
+
+ return 0;
+}
+
+static void disable_test_ltl_kunit(void)
+{
+ rv_detach_trace_probe("test_ltl_kunit", /* XXX: tracepoint */, handle_example_event);
+
+ ltl_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "test_ltl_kunit",
+ .description = "auto-generated",
+ .enable = enable_test_ltl_kunit,
+ .disable = disable_test_ltl_kunit,
+};
+
+static int __init register_test_ltl_kunit(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_test_ltl_kunit(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_test_ltl_kunit);
+module_exit(unregister_test_ltl_kunit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_ltl_kunit: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.h b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.h
new file mode 100644
index 000000000000..acc503b56e87
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * C implementation of Buchi automaton, automatically generated by
+ * tools/verification/rvgen from the linear temporal logic specification.
+ * For further information, see kernel documentation:
+ * Documentation/trace/rv/linear_temporal_logic.rst
+ */
+
+#include <linux/rv.h>
+
+#define MONITOR_NAME test_ltl_kunit
+
+enum ltl_atom {
+ LTL_EVENT_A,
+ LTL_EVENT_B,
+ LTL_NUM_ATOM
+};
+static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);
+
+static const char *ltl_atom_str(enum ltl_atom atom)
+{
+ static const char *const names[] = {
+ "ev_a",
+ "ev_b",
+ };
+
+ return names[atom];
+}
+
+enum ltl_buchi_state {
+ S0,
+ S1,
+ S2,
+ S3,
+ S4,
+ RV_NUM_BA_STATES
+};
+static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);
+
+static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ if (val1)
+ __set_bit(S0, mon->states);
+ if (true)
+ __set_bit(S1, mon->states);
+ if (event_b)
+ __set_bit(S4, mon->states);
+}
+
+static void
+ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ switch (state) {
+ case S0:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S1:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S2:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S3:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S4:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ }
+}
diff --git a/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.c b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.c
new file mode 100644
index 000000000000..37dab5dfdebc
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+/*
+ * XXX: include required headers, e.g.,
+ * #include <linux/sched.h>
+ */
+#include "test_ltl_kunit_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_TEST_LTL_KUNIT)
+
+static void rv_test_test_ltl_kunit(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ /*
+ * If you need to create task_structs with rv_kunit_alloc_mock_task()
+ * do it BEFORE preparing the test.
+ */
+
+ prepare_test(test, &rv_test_ltl_kunit_ops.mon);
+
+ /*
+ * XXX: write the test here
+ * e.g.
+ * RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ * rv_test_ltl_kunit_ops.handle_event(args);
+ */
+}
+
+#else
+#define rv_test_test_ltl_kunit rv_test_stub
+#endif
diff --git a/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.h b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.h
new file mode 100644
index 000000000000..b2ca34be327f
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_kunit.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __TEST_LTL_KUNIT_KUNIT_H
+#define __TEST_LTL_KUNIT_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct rv_test_ltl_kunit_ops {
+ struct rv_kunit_mon mon;
+ void (*handle_example_event)(void *data, /* XXX: fill header */);
+} rv_test_ltl_kunit_ops;
+#endif
+
+#endif /* __TEST_LTL_KUNIT_KUNIT_H */
diff --git a/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_trace.h b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_trace.h
new file mode 100644
index 000000000000..a054d5b2c0ea
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl_kunit/test_ltl_kunit_trace.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_TEST_LTL_KUNIT
+DEFINE_EVENT(event_ltl_monitor_id, event_test_ltl_kunit,
+ TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next),
+ TP_ARGS(task, states, atoms, next));
+DEFINE_EVENT(error_ltl_monitor_id, error_test_ltl_kunit,
+ TP_PROTO(struct task_struct *task),
+ TP_ARGS(task));
+#endif /* CONFIG_RV_MON_TEST_LTL_KUNIT */
diff --git a/tools/verification/rvgen/tests/rvgen_kunit.t b/tools/verification/rvgen/tests/rvgen_kunit.t
new file mode 100644
index 000000000000..d27d9175f562
--- /dev/null
+++ b/tools/verification/rvgen/tests/rvgen_kunit.t
@@ -0,0 +1,41 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source ../tests/engine.sh
+test_begin
+
+set_timeout 30s
+
+# Help tests
+check "verify kunit subcommand help" \
+ "$RVGEN kunit -h" 0 "model_name" "spec"
+
+check_and_compare_folder "KUnit generation with local lookup and test_da_kunit" \
+ "$RVGEN monitor -c da -s tests/specs/test_da.dot -t per_cpu -n test_da_kunit && $RVGEN kunit -a -l -n test_da_kunit" \
+ "test_da_kunit" "Now complete the test and add it to rv_monitors_test.c" "RV_MON_OPS_INIT"
+
+check_and_compare_folder "KUnit generation with local lookup and test_ha_kunit" \
+ "$RVGEN monitor -c ha -s tests/specs/test_ha.dot -t per_task -n test_ha_kunit && $RVGEN kunit -a -l -n test_ha_kunit" \
+ "test_ha_kunit" "Successfully created KUnit" "Append the following to"
+
+check_and_compare_folder "KUnit generation with local lookup and test_ltl_kunit" \
+ "$RVGEN monitor -c ltl -s tests/specs/test_ltl.ltl -t per_task -n test_ltl_kunit && $RVGEN kunit -l -n test_ltl_kunit" \
+ "test_ltl_kunit" "RV_MON_OPS_INIT"
+
+check_and_compare_folder "KUnit generation with backup file" \
+ "$RVGEN monitor -c ltl -s tests/specs/test_ltl.ltl -t per_task -n test_bak_kunit && echo DUMMY > test_bak_kunit/test_bak_kunit_kunit.c && $RVGEN kunit -l -n test_bak_kunit" \
+ "test_bak_kunit" "KUnit file(s) already exist.*backing up existing files"
+
+# Error handling tests
+check "missing required model_name" \
+ "$RVGEN kunit" 2 "the following arguments are required: -n/--model_name"
+
+check "non-existent model_name with auto_patch" \
+ "$RVGEN kunit -a -n nonexistent" 1 \
+ "Could not find monitor C file" "Traceback (most recent call last)"
+
+check "monitor without handlers" \
+ "mkdir -p nohandler ; echo DUMMY > nohandler/nohandler.c ; $RVGEN kunit -l -n nohandler" 1 \
+ "No handlers found" "Traceback (most recent call last)"
+rm -rf nohandler
+
+test_end
--
2.55.0
^ permalink raw reply related
* [PATCH v5 09/17] verification/rvgen: Add the rvgen kunit subcommand
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
Add the rvgen kunit subcommand to patch an already generated monitor for
kunit support. It parses the handlers and create the necessary structs
and initialisations.
The only remaining manual steps are importing the test in the runner
and writing the test itself.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
Notes:
V4:
* Use pathlib instead of os.path
* Rename files to backup instead of writing to backup
tools/verification/rvgen/Makefile | 1 +
tools/verification/rvgen/__main__.py | 15 +-
tools/verification/rvgen/rvgen/generator.py | 4 +-
tools/verification/rvgen/rvgen/kunit.py | 194 ++++++++++++++++++
.../rvgen/rvgen/templates/kunit.c | 33 +++
5 files changed, 244 insertions(+), 3 deletions(-)
create mode 100644 tools/verification/rvgen/rvgen/kunit.py
create mode 100644 tools/verification/rvgen/rvgen/templates/kunit.c
diff --git a/tools/verification/rvgen/Makefile b/tools/verification/rvgen/Makefile
index 2a2b9e64ea42..48d0376a5cc4 100644
--- a/tools/verification/rvgen/Makefile
+++ b/tools/verification/rvgen/Makefile
@@ -23,6 +23,7 @@ install:
$(INSTALL) rvgen/dot2c.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/dot2c.py
$(INSTALL) dot2c -D -m 755 $(DESTDIR)$(bindir)/
$(INSTALL) rvgen/dot2k.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/dot2k.py
+ $(INSTALL) rvgen/kunit.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/kunit.py
$(INSTALL) rvgen/container.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/container.py
$(INSTALL) rvgen/generator.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/generator.py
$(INSTALL) rvgen/ltl2ba.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/ltl2ba.py
diff --git a/tools/verification/rvgen/__main__.py b/tools/verification/rvgen/__main__.py
index 5c923dc10d0f..e0ac562fbddd 100644
--- a/tools/verification/rvgen/__main__.py
+++ b/tools/verification/rvgen/__main__.py
@@ -13,6 +13,7 @@ if __name__ == '__main__':
from rvgen.generator import Monitor
from rvgen.container import Container
from rvgen.ltl2k import ltl2k
+ from rvgen.kunit import KUnit, KUnitError
from rvgen.automata import AutomataError
import argparse
import sys
@@ -41,6 +42,11 @@ if __name__ == '__main__':
container_parser = subparsers.add_parser("container", parents=[parent_parser])
container_parser.add_argument('-n', "--model_name", dest="model_name", required=True)
+ kunit_parser = subparsers.add_parser("kunit", parents=[parent_parser])
+ kunit_parser.add_argument('-n', "--model_name", dest="model_name", required=True)
+ kunit_parser.add_argument('-l', "--local", dest="local", action="store_true", required=False,
+ help="Force looking for the monitor in the current directory only")
+
params = parser.parse_args()
try:
@@ -55,11 +61,18 @@ if __name__ == '__main__':
else:
print("Unknown monitor class:", params.monitor_class)
sys.exit(1)
- else:
+ elif params.subcmd == "container":
monitor = Container(vars(params))
+ elif params.subcmd == "kunit":
+ monitor = KUnit(vars(params))
+ monitor.print_files()
+ sys.exit(0)
except AutomataError as e:
print(f"There was an error processing {params.spec}: {e}", file=sys.stderr)
sys.exit(1)
+ except KUnitError as e:
+ print(f"There was an error generating KUnit files: {e}", file=sys.stderr)
+ sys.exit(1)
print(f"Writing the monitor into the directory {monitor.name}")
monitor.print_files()
diff --git a/tools/verification/rvgen/rvgen/generator.py b/tools/verification/rvgen/rvgen/generator.py
index f1b37d34b1e9..45e2bab26cb5 100644
--- a/tools/verification/rvgen/rvgen/generator.py
+++ b/tools/verification/rvgen/rvgen/generator.py
@@ -22,9 +22,9 @@ class RVGenerator:
self.description = extra_params.get("description", self.name) or "auto-generated"
self.auto_patch = extra_params.get("auto_patch")
if self.auto_patch:
- self.__fill_rv_kernel_dir()
+ self._fill_rv_kernel_dir()
- def __fill_rv_kernel_dir(self):
+ def _fill_rv_kernel_dir(self):
# find the kernel tree root relative to this file's location
resolved_path = Path(__file__).resolve()
if len(resolved_path.parents) > 4:
diff --git a/tools/verification/rvgen/rvgen/kunit.py b/tools/verification/rvgen/rvgen/kunit.py
new file mode 100644
index 000000000000..ed2082d7d3bc
--- /dev/null
+++ b/tools/verification/rvgen/rvgen/kunit.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2026-2029 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
+#
+# Generator for runtime verification kunit files
+
+import re
+from pathlib import Path
+from . import generator
+
+
+class KUnitError(Exception):
+ """Exception raised for errors in KUnit generation and file handling."""
+
+
+class KUnit(generator.RVGenerator):
+ template_dir = ""
+
+ def __init__(self, extra_params={}):
+ super().__init__(extra_params)
+ self.local = extra_params.get("local", False)
+ self.kunit_c = self._read_template_file("kunit.c")
+ if not self.local:
+ self._fill_rv_kernel_dir()
+ try:
+ self.monitor_path = self.__find_monitor_c_file()
+ with open(self.monitor_path, 'r') as f:
+ self.content = f.read()
+ except OSError as e:
+ raise KUnitError(e) from e
+ self.monitor_class = self.__detect_monitor_class()
+
+ def _read_template_file(self, file):
+ if file in ("main.c", "Kconfig"):
+ return ""
+ return super()._read_template_file(file)
+
+ def __find_monitor_c_file(self) -> str:
+ """Look for the monitor file in the kernel tree or in the current folder."""
+ if not self.local:
+ path = Path(self.rv_dir) / "monitors" / self.name / f"{self.name}.c"
+ if path.exists():
+ return str(path)
+
+ path = Path(self.name) / f"{self.name}.c"
+ if path.exists():
+ return str(path)
+
+ raise FileNotFoundError(f"Could not find monitor C file for '{self.name}'")
+
+ def __extract_function_args(self, handler_name: str) -> str:
+ pattern = re.compile(
+ r'^\s*(.*?)\b' + re.escape(handler_name) + r'\(([^)]*)\)',
+ re.MULTILINE | re.DOTALL
+ )
+ match = pattern.search(self.content)
+ if not match:
+ return "/* XXX: fill handlers argument. */"
+
+ return match.group(2).strip()
+
+ def __parse_attach_handlers(self) -> list[str]:
+ """Find handlers by parsing when they are attached to tracepoints."""
+ probe_pattern = re.compile(
+ r'rv_attach_trace_probe\(.*, ([a-zA-Z0-9_]+)\)'
+ )
+ handlers = []
+ for match in probe_pattern.finditer(self.content):
+ handler = match.group(1)
+ if handler not in handlers:
+ handlers.append(handler)
+ return handlers
+
+ def __detect_monitor_class(self) -> str:
+ for c in ("da", "ha", "ltl"):
+ if f"{c}_monitor.h" in self.content:
+ return c
+ return "da"
+
+ def __fill_kunit_c(self, struct_name: str) -> str:
+ kunit_c = self.kunit_c
+ kunit_c = kunit_c.replace("%%MODEL_NAME%%", self.name)
+ kunit_c = kunit_c.replace("%%MODEL_NAME_UP%%", self.name.upper())
+ kunit_c = kunit_c.replace("%%MONITOR_CLASS%%", self.monitor_class)
+ kunit_c = kunit_c.replace("%%STRUCT_NAME%%", struct_name)
+ return kunit_c
+
+ def __fill_kunit_h(self, struct_name, prototypes) -> str:
+ return f"""/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Automatically generated by rvgen kunit.
+ * May need manual intervention for function prototypes that couldn't be
+ * found (e.g. are in another file) or variables to be exported.
+ */
+
+#ifndef __{self.name.upper()}_KUNIT_H
+#define __{self.name.upper()}_KUNIT_H
+
+#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+
+#include <linux/rv.h>
+#include <rv/kunit.h>
+
+extern const struct {struct_name} {{
+\tstruct rv_kunit_mon mon;
+\t{"\n\t".join(prototypes)}
+}} {struct_name};
+#endif
+
+#endif /* __{self.name.upper()}_KUNIT_H */
+"""
+
+ def __fill_monitor_handlers(self, struct_name, assignments):
+ struct_definition = f"""#if IS_ENABLED(CONFIG_RV_MONITORS_KUNIT_TEST)
+#include <kunit/visibility.h>
+#include "{self.name}_kunit.h"
+
+const struct {struct_name} {struct_name} = {{
+\t.mon = RV_MON_OPS_INIT(),
+\t{"\n\t".join(assignments)}
+}};
+EXPORT_SYMBOL_IF_KUNIT({struct_name});
+#endif"""
+
+ if self.auto_patch:
+ try:
+ with open(self.monitor_path, 'w') as f:
+ f.write(f"{self.content}\n{struct_definition}\n")
+ except OSError as e:
+ raise KUnitError(f"Error patching monitor file {self.monitor_path}: {e}") from e
+ else:
+ print(f"Append the following to {self.name}.c:\n")
+ print(struct_definition)
+ print("Now complete the test and add it to rv_monitors_test.c")
+
+ def print_files(self):
+
+ handlers = self.__parse_attach_handlers()
+
+ if not handlers:
+ raise KUnitError(f"No handlers found in {self.monitor_path}")
+
+ prototypes = []
+ assignments = []
+ for handler in handlers:
+ arguments = self.__extract_function_args(handler)
+
+ prototypes.append(f"void (*{handler})({arguments});")
+ assignments.append(f".{handler} = {handler},")
+
+ struct_name = f"rv_{self.name}_ops"
+
+ self.__fill_monitor_handlers(struct_name, assignments)
+
+ dir_path = Path(self.monitor_path).parent
+
+ header_file_path = dir_path / f"{self.name}_kunit.h"
+ kunit_c_file_path = dir_path / f"{self.name}_kunit.c"
+
+ use_backup = True
+ if header_file_path.exists() or kunit_c_file_path.exists():
+ try:
+ response = input("KUnit file(s) already exist. Backup? [Y/n] ")
+ if response.strip().lower() in ("n", "no"):
+ use_backup = False
+ except EOFError:
+ print("Non-interactive session detected, backing up existing files.")
+ else:
+ use_backup = False
+
+ if use_backup:
+ for path in (header_file_path, kunit_c_file_path):
+ if path.exists():
+ try:
+ path.rename(path.with_suffix(path.suffix + ".bak"))
+ except OSError as e:
+ raise KUnitError(f"Error backing up file {path}: {e}") from e
+
+ header_content = self.__fill_kunit_h(struct_name, prototypes)
+ try:
+ with open(header_file_path, 'w') as f:
+ f.write(header_content)
+ print(f"Successfully created KUnit header file: {header_file_path}")
+ except OSError as e:
+ raise KUnitError(f"Error writing to file {header_file_path}: {e}") from e
+
+ kunit_c_content = self.__fill_kunit_c(struct_name)
+ try:
+ with open(kunit_c_file_path, 'w') as f:
+ f.write(kunit_c_content)
+ print(f"Successfully created KUnit C file: {kunit_c_file_path}")
+ except OSError as e:
+ raise KUnitError(f"Error writing to file {kunit_c_file_path}: {e}") from e
diff --git a/tools/verification/rvgen/rvgen/templates/kunit.c b/tools/verification/rvgen/rvgen/templates/kunit.c
new file mode 100644
index 000000000000..62092b5cc6d4
--- /dev/null
+++ b/tools/verification/rvgen/rvgen/templates/kunit.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/rv.h>
+#include <rv/kunit.h>
+/*
+ * XXX: include required headers, e.g.,
+ * #include <linux/sched.h>
+ */
+#include "%%MODEL_NAME%%_kunit.h"
+
+#if IS_REACHABLE(CONFIG_RV_MON_%%MODEL_NAME_UP%%)
+
+static void rv_test_%%MODEL_NAME%%(struct kunit *test)
+{
+ struct rv_kunit_ctx *ctx = test->priv;
+ /*
+ * If you need to create task_structs with rv_kunit_alloc_mock_task()
+ * do it BEFORE preparing the test.
+ */
+
+ prepare_test(test, &%%STRUCT_NAME%%.mon);
+
+ /*
+ * XXX: write the test here
+ * e.g.
+ * RV_KUNIT_EXPECT_REACTION_HERE(test, ctx)
+ * %%STRUCT_NAME%%.handle_event(args);
+ */
+}
+
+#else
+#define rv_test_%%MODEL_NAME%% rv_test_stub
+#endif
--
2.55.0
^ permalink raw reply related
* [PATCH v5 08/17] verification/rvgen: Add selftests
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco
Cc: Nam Cao, Wen Yang, Thomas Weissschuh, Tomas Glozar, John Kacur
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
The rvgen code generator needs validation to ensure it produces correct
monitor implementations from input specifications.
Add selftests with golden reference outputs covering all monitor classes
(DA, HA, LTL) and types (global, per_cpu, per_task, per_obj), including
optional features like descriptions and parent monitors. Container
generation and error handling (missing files, invalid specifications,
missing arguments) are also validated against expected output.
Acked-by: Nam Cao <namcao@linutronix.de>
Reviewed-by: Wen Yang <wen.yang@linux.dev>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
tools/verification/rvgen/Makefile | 4 +
.../rvgen/tests/rvgen_container.t | 20 +++++
.../verification/rvgen/tests/rvgen_monitor.t | 87 +++++++++++++++++++
tools/verification/tests/engine.sh | 34 ++++++++
4 files changed, 145 insertions(+)
create mode 100644 tools/verification/rvgen/tests/rvgen_container.t
create mode 100644 tools/verification/rvgen/tests/rvgen_monitor.t
diff --git a/tools/verification/rvgen/Makefile b/tools/verification/rvgen/Makefile
index cfc4056c1e87..2a2b9e64ea42 100644
--- a/tools/verification/rvgen/Makefile
+++ b/tools/verification/rvgen/Makefile
@@ -13,6 +13,10 @@ all:
.PHONY: clean
clean:
+.PHONY: check
+check:
+ prove -o --directives -f tests/
+
.PHONY: install
install:
$(INSTALL) rvgen/automata.py -D -m 644 $(DESTDIR)$(PYLIB)/rvgen/automata.py
diff --git a/tools/verification/rvgen/tests/rvgen_container.t b/tools/verification/rvgen/tests/rvgen_container.t
new file mode 100644
index 000000000000..fa4fb3db8288
--- /dev/null
+++ b/tools/verification/rvgen/tests/rvgen_container.t
@@ -0,0 +1,20 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source ../tests/engine.sh
+test_begin
+
+set_timeout 30s
+
+# Help tests
+check "verify container subcommand help" \
+ "$RVGEN container -h" 0 "model_name" "class"
+
+check_and_compare_folder "container with description" \
+ "$RVGEN container -n test_container -D 'Test container for grouping monitors'" \
+ "test_container" "Writing the monitor into the directory test_container"
+
+# Error handling tests
+check "missing required model_name" \
+ "$RVGEN container" 2 "the following arguments are required: -n/--model_name"
+
+test_end
diff --git a/tools/verification/rvgen/tests/rvgen_monitor.t b/tools/verification/rvgen/tests/rvgen_monitor.t
new file mode 100644
index 000000000000..261476504eee
--- /dev/null
+++ b/tools/verification/rvgen/tests/rvgen_monitor.t
@@ -0,0 +1,87 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source ../tests/engine.sh
+test_begin
+
+set_timeout 30s
+
+# Help and basic tests
+check "verify help page" \
+ "$RVGEN --help" 0 "Generate kernel rv monitor"
+
+check "verify monitor subcommand help" \
+ "$RVGEN monitor --help" 0 "Monitor class"
+
+# DA monitor tests - test all monitor types
+check_and_compare_folder "DA per_cpu (default name)" \
+ "$RVGEN monitor -c da -s tests/specs/test_da.dot -t per_cpu" \
+ "test_da" "obj-\$(CONFIG_RV_MON_TEST_DA) += monitors/test_da/test_da.o"
+
+check_and_compare_folder "DA global type" \
+ "$RVGEN monitor -c da -s tests/specs/test_da.dot -t global -n da_global" \
+ "da_global" "DA_MON_EVENTS_IMPLICIT"
+
+check_and_compare_folder "DA per_task with description" \
+ "$RVGEN monitor -c da -s tests/specs/test_da2.dot -t per_task -n da_pertask_desc -D 'Custom description for testing'" \
+ "da_pertask_desc" "#include <monitors/da_pertask_desc/da_pertask_desc_trace.h>"
+
+check_and_compare_folder "DA per_obj with parent" \
+ "$RVGEN monitor -c da -s tests/specs/test_da2.dot -t per_obj -n da_perobj_parent -p parent_mon" \
+ "da_perobj_parent" "DA_MON_EVENTS_ID"
+
+# HA monitor tests
+check_and_compare_folder "HA per_task (default name)" \
+ "$RVGEN monitor -c ha -s tests/specs/test_ha.dot -t per_task" \
+ "test_ha" "HA_MON_EVENTS_ID"
+
+check_and_compare_folder "HA per_cpu type" \
+ "$RVGEN monitor -c ha -s tests/specs/test_ha.dot -t per_cpu -n ha_percpu" \
+ "ha_percpu" "HA_MON_EVENTS_IMPLICIT"
+
+# LTL monitor test
+check_and_compare_folder "LTL per_task" \
+ "$RVGEN monitor -c ltl -s tests/specs/test_ltl.ltl -t per_task -n ltl_pertask" \
+ "ltl_pertask" "source \"kernel/trace/rv/monitors/ltl_pertask/Kconfig\""
+
+check_and_compare_folder "LTL per_task with parent and description (default name)" \
+ "$RVGEN monitor -c ltl -s tests/specs/test_ltl.ltl -t per_task -p ltl_parent -D 'Simple description'" \
+ "test_ltl" "LTL_MON_EVENTS_ID"
+
+# Error handling tests
+check "missing required spec argument" \
+ "$RVGEN monitor -c da -t per_cpu" 2 \
+ "the following arguments are required: -s/--spec" "Traceback (most recent call last)"
+
+check "missing required monitor type" \
+ "$RVGEN monitor -c da -s tests/specs/test_da.dot" 2 \
+ "the following arguments are required: -t/--monitor_type" "Traceback (most recent call last)"
+
+check "missing required monitor class" \
+ "$RVGEN monitor -s tests/specs/test_da.dot -t per_cpu" 2 \
+ "the following arguments are required: -c/--class" "Traceback (most recent call last)"
+
+check "invalid monitor class" \
+ "$RVGEN monitor -c invalid -s tests/specs/test_da.dot -t per_cpu" 1 \
+ "Unknown monitor class" "Traceback (most recent call last)"
+
+check "missing dot file" \
+ "$RVGEN monitor -c da -s tests/specs/nonexistent.dot -t per_cpu" 1 \
+ "No such file or directory" "Traceback (most recent call last)"
+
+check "missing ltl file" \
+ "$RVGEN monitor -c ltl -s tests/specs/nonexistent.ltl -t per_task" 1 \
+ "No such file or directory" "Traceback (most recent call last)"
+
+check "invalid dot file syntax" \
+ "$RVGEN monitor -c da -s tests/specs/test_invalid.dot -t per_cpu" 1 \
+ "Not a valid .dot format" "Traceback (most recent call last)"
+
+check "invalid ha file syntax" \
+ "$RVGEN monitor -c ha -s tests/specs/test_invalid_ha.dot -t per_obj" 1 \
+ "Unrecognised event constraint" "Traceback (most recent call last)"
+
+check "invalid ltl file syntax" \
+ "$RVGEN monitor -c ltl -s tests/specs/test_invalid.ltl -t per_task" 1 \
+ "Illegal character 'i'" "Traceback (most recent call last)"
+
+test_end
diff --git a/tools/verification/tests/engine.sh b/tools/verification/tests/engine.sh
index 57e16dc980b1..cfdf2180aad8 100644
--- a/tools/verification/tests/engine.sh
+++ b/tools/verification/tests/engine.sh
@@ -5,6 +5,8 @@ test_begin() {
# included correctly.
ctr=0
[ -z "$RV" ] && RV="../rv/rv"
+ [ -z "$RVGEN" ] && RVGEN="python3 ../rvgen"
+ [ -z "$GOLDEN_DIR" ] && GOLDEN_DIR="tests/golden"
[ -n "$TEST_COUNT" ] && echo "1..$TEST_COUNT"
}
@@ -118,6 +120,38 @@ check_if_exists() {
fi
}
+check_and_compare_folder() {
+ # Run command, compare generated folder to golden, and cleanup
+ local desc=$1
+ local command=$2
+ local generated_dir=$3
+ local expected_output=$4
+ local unexpected_output=$5
+ local golden_dir="$GOLDEN_DIR/$generated_dir"
+
+ ctr=$((ctr + 1))
+ if [ -n "$TEST_COUNT" ]; then
+ rm -rf "$generated_dir"
+ _check "$desc" "$command" 0 "$expected_output" "$unexpected_output"
+
+ if [ "$fail" -eq 0 ] && [ ! -d "$generated_dir" ]; then
+ failure "# Generated directory not found: $generated_dir"
+ fi
+
+ if [ "$fail" -ne 0 ]; then
+ :
+ elif ! diff -r "$generated_dir" "$golden_dir" &> /dev/null; then
+ failure "# Directories differ:"
+ failbuf+=$(diff -r "$generated_dir" "$golden_dir" 2>&1 | sed 's/^/# /')
+ failbuf+=$'\n'
+ fi
+
+ report "$1"
+
+ rm -rf "$generated_dir"
+ fi
+}
+
set_timeout() {
TIMEOUT="timeout -v -k 30s $1"
}
--
2.55.0
^ permalink raw reply related
* [PATCH v5 07/17] verification/rvgen: Add golden and spec folders for tests
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
Create reference models specifications and generated files in the golded
folder. Those can be used as reference to validate rvgen still generates
files as expected in automated tests.
Reviewed-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
.../rvgen/tests/golden/da_global/Kconfig | 9 +
.../rvgen/tests/golden/da_global/da_global.c | 95 +++++++
.../rvgen/tests/golden/da_global/da_global.h | 47 ++++
.../tests/golden/da_global/da_global_trace.h | 15 ++
.../tests/golden/da_perobj_parent/Kconfig | 11 +
.../da_perobj_parent/da_perobj_parent.c | 110 ++++++++
.../da_perobj_parent/da_perobj_parent.h | 64 +++++
.../da_perobj_parent/da_perobj_parent_trace.h | 15 ++
.../tests/golden/da_pertask_desc/Kconfig | 9 +
.../golden/da_pertask_desc/da_pertask_desc.c | 105 ++++++++
.../golden/da_pertask_desc/da_pertask_desc.h | 64 +++++
.../da_pertask_desc/da_pertask_desc_trace.h | 15 ++
.../rvgen/tests/golden/ha_percpu/Kconfig | 9 +
.../rvgen/tests/golden/ha_percpu/ha_percpu.c | 244 +++++++++++++++++
.../rvgen/tests/golden/ha_percpu/ha_percpu.h | 72 +++++
.../tests/golden/ha_percpu/ha_percpu_trace.h | 19 ++
.../rvgen/tests/golden/ltl_pertask/Kconfig | 9 +
.../tests/golden/ltl_pertask/ltl_pertask.c | 107 ++++++++
.../tests/golden/ltl_pertask/ltl_pertask.h | 108 ++++++++
.../golden/ltl_pertask/ltl_pertask_trace.h | 14 +
.../rvgen/tests/golden/test_container/Kconfig | 5 +
.../golden/test_container/test_container.c | 35 +++
.../golden/test_container/test_container.h | 3 +
.../rvgen/tests/golden/test_da/Kconfig | 9 +
.../rvgen/tests/golden/test_da/test_da.c | 95 +++++++
.../rvgen/tests/golden/test_da/test_da.h | 47 ++++
.../tests/golden/test_da/test_da_trace.h | 15 ++
.../rvgen/tests/golden/test_ha/Kconfig | 9 +
.../rvgen/tests/golden/test_ha/test_ha.c | 247 ++++++++++++++++++
.../rvgen/tests/golden/test_ha/test_ha.h | 72 +++++
.../tests/golden/test_ha/test_ha_trace.h | 19 ++
.../rvgen/tests/golden/test_ltl/Kconfig | 11 +
.../rvgen/tests/golden/test_ltl/test_ltl.c | 108 ++++++++
.../rvgen/tests/golden/test_ltl/test_ltl.h | 108 ++++++++
.../tests/golden/test_ltl/test_ltl_trace.h | 14 +
.../rvgen/tests/specs/test_da.dot | 16 ++
.../rvgen/tests/specs/test_da2.dot | 19 ++
.../rvgen/tests/specs/test_ha.dot | 27 ++
.../rvgen/tests/specs/test_invalid.dot | 8 +
.../rvgen/tests/specs/test_invalid.ltl | 1 +
.../rvgen/tests/specs/test_invalid_ha.dot | 16 ++
.../rvgen/tests/specs/test_ltl.ltl | 1 +
42 files changed, 2026 insertions(+)
create mode 100644 tools/verification/rvgen/tests/golden/da_global/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/da_global/da_global.c
create mode 100644 tools/verification/rvgen/tests/golden/da_global/da_global.h
create mode 100644 tools/verification/rvgen/tests/golden/da_global/da_global_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/da_perobj_parent/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.c
create mode 100644 tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.h
create mode 100644 tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/da_pertask_desc/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.c
create mode 100644 tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.h
create mode 100644 tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/ha_percpu/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.c
create mode 100644 tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.h
create mode 100644 tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/ltl_pertask/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.c
create mode 100644 tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.h
create mode 100644 tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/test_container/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_container/test_container.c
create mode 100644 tools/verification/rvgen/tests/golden/test_container/test_container.h
create mode 100644 tools/verification/rvgen/tests/golden/test_da/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_da/test_da.c
create mode 100644 tools/verification/rvgen/tests/golden/test_da/test_da.h
create mode 100644 tools/verification/rvgen/tests/golden/test_da/test_da_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ha/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_ha/test_ha.c
create mode 100644 tools/verification/rvgen/tests/golden/test_ha/test_ha.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ha/test_ha_trace.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl/Kconfig
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl/test_ltl.c
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl/test_ltl.h
create mode 100644 tools/verification/rvgen/tests/golden/test_ltl/test_ltl_trace.h
create mode 100644 tools/verification/rvgen/tests/specs/test_da.dot
create mode 100644 tools/verification/rvgen/tests/specs/test_da2.dot
create mode 100644 tools/verification/rvgen/tests/specs/test_ha.dot
create mode 100644 tools/verification/rvgen/tests/specs/test_invalid.dot
create mode 100644 tools/verification/rvgen/tests/specs/test_invalid.ltl
create mode 100644 tools/verification/rvgen/tests/specs/test_invalid_ha.dot
create mode 100644 tools/verification/rvgen/tests/specs/test_ltl.ltl
diff --git a/tools/verification/rvgen/tests/golden/da_global/Kconfig b/tools/verification/rvgen/tests/golden/da_global/Kconfig
new file mode 100644
index 000000000000..799fbf11c3ac
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_global/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_DA_GLOBAL
+ depends on RV
+ # XXX: add dependencies if there
+ select DA_MON_EVENTS_IMPLICIT
+ bool "da_global monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/da_global/da_global.c b/tools/verification/rvgen/tests/golden/da_global/da_global.c
new file mode 100644
index 000000000000..71b26ae2d51c
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_global/da_global.c
@@ -0,0 +1,95 @@
+// 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 "da_global"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_GLOBAL
+#include "da_global.h"
+#include <rv/da_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+static void handle_event_1(void *data, /* XXX: fill header */)
+{
+ da_handle_event(event_1_da_global);
+}
+
+static void handle_event_2(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event always leads to the initial state */
+ da_handle_start_event(event_2_da_global);
+}
+
+static int enable_da_global(void)
+{
+ int retval;
+
+ retval = da_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("da_global", /* XXX: tracepoint */, handle_event_1);
+ rv_attach_trace_probe("da_global", /* XXX: tracepoint */, handle_event_2);
+
+ return 0;
+}
+
+static void disable_da_global(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("da_global", /* XXX: tracepoint */, handle_event_1);
+ rv_detach_trace_probe("da_global", /* XXX: tracepoint */, handle_event_2);
+
+ da_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "da_global",
+ .description = "auto-generated",
+ .enable = enable_da_global,
+ .disable = disable_da_global,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_da_global(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_da_global(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_da_global);
+module_exit(unregister_da_global);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("da_global: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/da_global/da_global.h b/tools/verification/rvgen/tests/golden/da_global/da_global.h
new file mode 100644
index 000000000000..40b1f1c0c681
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_global/da_global.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of da_global automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME da_global
+
+enum states_da_global {
+ state_a_da_global,
+ state_b_da_global,
+ state_max_da_global,
+};
+
+#define INVALID_STATE state_max_da_global
+
+enum events_da_global {
+ event_1_da_global,
+ event_2_da_global,
+ event_max_da_global,
+};
+
+struct automaton_da_global {
+ char *state_names[state_max_da_global];
+ char *event_names[event_max_da_global];
+ unsigned char function[state_max_da_global][event_max_da_global];
+ unsigned char initial_state;
+ bool final_states[state_max_da_global];
+};
+
+static const struct automaton_da_global automaton_da_global = {
+ .state_names = {
+ "state_a",
+ "state_b",
+ },
+ .event_names = {
+ "event_1",
+ "event_2",
+ },
+ .function = {
+ { state_b_da_global, state_a_da_global },
+ { INVALID_STATE, state_a_da_global },
+ },
+ .initial_state = state_a_da_global,
+ .final_states = { 1, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/da_global/da_global_trace.h b/tools/verification/rvgen/tests/golden/da_global/da_global_trace.h
new file mode 100644
index 000000000000..4d2730b71dd0
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_global/da_global_trace.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_DA_GLOBAL
+DEFINE_EVENT(event_da_monitor, event_da_global,
+ TP_PROTO(char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor, error_da_global,
+ TP_PROTO(char *state, char *event),
+ TP_ARGS(state, event));
+#endif /* CONFIG_RV_MON_DA_GLOBAL */
diff --git a/tools/verification/rvgen/tests/golden/da_perobj_parent/Kconfig b/tools/verification/rvgen/tests/golden/da_perobj_parent/Kconfig
new file mode 100644
index 000000000000..249ba3aee8d7
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_perobj_parent/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_DA_PEROBJ_PARENT
+ depends on RV
+ # XXX: add dependencies if there
+ depends on RV_MON_PARENT_MON
+ default y
+ select DA_MON_EVENTS_ID
+ bool "da_perobj_parent monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.c b/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.c
new file mode 100644
index 000000000000..a0f8b5a216a3
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.c
@@ -0,0 +1,110 @@
+// 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 "da_perobj_parent"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+#include <monitors/parent_mon/parent_mon.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_PER_OBJ
+typedef /* XXX: define the target type */ *monitor_target;
+#include "da_perobj_parent.h"
+#include <rv/da_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+static void handle_event_1(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event is only valid in the initial state */
+ int id = /* XXX: how do I get the id? */;
+ monitor_target t = /* XXX: how do I get t? */;
+ da_handle_start_run_event(id, t, event_1_da_perobj_parent);
+}
+
+static void handle_event_2(void *data, /* XXX: fill header */)
+{
+ int id = /* XXX: how do I get the id? */;
+ monitor_target t = /* XXX: how do I get t? */;
+ da_handle_event(id, t, event_2_da_perobj_parent);
+}
+
+static void handle_event_3(void *data, /* XXX: fill header */)
+{
+ int id = /* XXX: how do I get the id? */;
+ monitor_target t = /* XXX: how do I get t? */;
+ da_handle_event(id, t, event_3_da_perobj_parent);
+}
+
+static int enable_da_perobj_parent(void)
+{
+ int retval;
+
+ retval = da_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("da_perobj_parent", /* XXX: tracepoint */, handle_event_1);
+ rv_attach_trace_probe("da_perobj_parent", /* XXX: tracepoint */, handle_event_2);
+ rv_attach_trace_probe("da_perobj_parent", /* XXX: tracepoint */, handle_event_3);
+
+ return 0;
+}
+
+static void disable_da_perobj_parent(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("da_perobj_parent", /* XXX: tracepoint */, handle_event_1);
+ rv_detach_trace_probe("da_perobj_parent", /* XXX: tracepoint */, handle_event_2);
+ rv_detach_trace_probe("da_perobj_parent", /* XXX: tracepoint */, handle_event_3);
+
+ da_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "da_perobj_parent",
+ .description = "auto-generated",
+ .enable = enable_da_perobj_parent,
+ .disable = disable_da_perobj_parent,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_da_perobj_parent(void)
+{
+ return rv_register_monitor(&rv_this, &rv_parent_mon);
+}
+
+static void __exit unregister_da_perobj_parent(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_da_perobj_parent);
+module_exit(unregister_da_perobj_parent);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("da_perobj_parent: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.h b/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.h
new file mode 100644
index 000000000000..3c8dc3b22443
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of da_perobj_parent automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME da_perobj_parent
+
+enum states_da_perobj_parent {
+ state_a_da_perobj_parent,
+ state_b_da_perobj_parent,
+ state_c_da_perobj_parent,
+ state_max_da_perobj_parent,
+};
+
+#define INVALID_STATE state_max_da_perobj_parent
+
+enum events_da_perobj_parent {
+ event_1_da_perobj_parent,
+ event_2_da_perobj_parent,
+ event_3_da_perobj_parent,
+ event_max_da_perobj_parent,
+};
+
+struct automaton_da_perobj_parent {
+ char *state_names[state_max_da_perobj_parent];
+ char *event_names[event_max_da_perobj_parent];
+ unsigned char function[state_max_da_perobj_parent][event_max_da_perobj_parent];
+ unsigned char initial_state;
+ bool final_states[state_max_da_perobj_parent];
+};
+
+static const struct automaton_da_perobj_parent automaton_da_perobj_parent = {
+ .state_names = {
+ "state_a",
+ "state_b",
+ "state_c",
+ },
+ .event_names = {
+ "event_1",
+ "event_2",
+ "event_3",
+ },
+ .function = {
+ {
+ state_b_da_perobj_parent,
+ state_c_da_perobj_parent,
+ INVALID_STATE,
+ },
+ {
+ INVALID_STATE,
+ state_a_da_perobj_parent,
+ state_c_da_perobj_parent,
+ },
+ {
+ INVALID_STATE,
+ INVALID_STATE,
+ INVALID_STATE,
+ },
+ },
+ .initial_state = state_a_da_perobj_parent,
+ .final_states = { 1, 0, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent_trace.h b/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent_trace.h
new file mode 100644
index 000000000000..59bfca8f73d2
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_perobj_parent/da_perobj_parent_trace.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_DA_PEROBJ_PARENT
+DEFINE_EVENT(event_da_monitor_id, event_da_perobj_parent,
+ TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(id, state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor_id, error_da_perobj_parent,
+ TP_PROTO(int id, char *state, char *event),
+ TP_ARGS(id, state, event));
+#endif /* CONFIG_RV_MON_DA_PEROBJ_PARENT */
diff --git a/tools/verification/rvgen/tests/golden/da_pertask_desc/Kconfig b/tools/verification/rvgen/tests/golden/da_pertask_desc/Kconfig
new file mode 100644
index 000000000000..c6f350179098
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_pertask_desc/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_DA_PERTASK_DESC
+ depends on RV
+ # XXX: add dependencies if there
+ select DA_MON_EVENTS_ID
+ bool "da_pertask_desc monitor"
+ help
+ Custom description for testing
diff --git a/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.c b/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.c
new file mode 100644
index 000000000000..c1ae5078c4f9
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.c
@@ -0,0 +1,105 @@
+// 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 "da_pertask_desc"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_PER_TASK
+#include "da_pertask_desc.h"
+#include <rv/da_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+static void handle_event_1(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event is only valid in the initial state */
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_start_run_event(p, event_1_da_pertask_desc);
+}
+
+static void handle_event_2(void *data, /* XXX: fill header */)
+{
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_event(p, event_2_da_pertask_desc);
+}
+
+static void handle_event_3(void *data, /* XXX: fill header */)
+{
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_event(p, event_3_da_pertask_desc);
+}
+
+static int enable_da_pertask_desc(void)
+{
+ int retval;
+
+ retval = da_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("da_pertask_desc", /* XXX: tracepoint */, handle_event_1);
+ rv_attach_trace_probe("da_pertask_desc", /* XXX: tracepoint */, handle_event_2);
+ rv_attach_trace_probe("da_pertask_desc", /* XXX: tracepoint */, handle_event_3);
+
+ return 0;
+}
+
+static void disable_da_pertask_desc(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("da_pertask_desc", /* XXX: tracepoint */, handle_event_1);
+ rv_detach_trace_probe("da_pertask_desc", /* XXX: tracepoint */, handle_event_2);
+ rv_detach_trace_probe("da_pertask_desc", /* XXX: tracepoint */, handle_event_3);
+
+ da_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "da_pertask_desc",
+ .description = "Custom description for testing",
+ .enable = enable_da_pertask_desc,
+ .disable = disable_da_pertask_desc,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_da_pertask_desc(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_da_pertask_desc(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_da_pertask_desc);
+module_exit(unregister_da_pertask_desc);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("da_pertask_desc: Custom description for testing");
diff --git a/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.h b/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.h
new file mode 100644
index 000000000000..837b238754b0
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of da_pertask_desc automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME da_pertask_desc
+
+enum states_da_pertask_desc {
+ state_a_da_pertask_desc,
+ state_b_da_pertask_desc,
+ state_c_da_pertask_desc,
+ state_max_da_pertask_desc,
+};
+
+#define INVALID_STATE state_max_da_pertask_desc
+
+enum events_da_pertask_desc {
+ event_1_da_pertask_desc,
+ event_2_da_pertask_desc,
+ event_3_da_pertask_desc,
+ event_max_da_pertask_desc,
+};
+
+struct automaton_da_pertask_desc {
+ char *state_names[state_max_da_pertask_desc];
+ char *event_names[event_max_da_pertask_desc];
+ unsigned char function[state_max_da_pertask_desc][event_max_da_pertask_desc];
+ unsigned char initial_state;
+ bool final_states[state_max_da_pertask_desc];
+};
+
+static const struct automaton_da_pertask_desc automaton_da_pertask_desc = {
+ .state_names = {
+ "state_a",
+ "state_b",
+ "state_c",
+ },
+ .event_names = {
+ "event_1",
+ "event_2",
+ "event_3",
+ },
+ .function = {
+ {
+ state_b_da_pertask_desc,
+ state_c_da_pertask_desc,
+ INVALID_STATE,
+ },
+ {
+ INVALID_STATE,
+ state_a_da_pertask_desc,
+ state_c_da_pertask_desc,
+ },
+ {
+ INVALID_STATE,
+ INVALID_STATE,
+ INVALID_STATE,
+ },
+ },
+ .initial_state = state_a_da_pertask_desc,
+ .final_states = { 1, 0, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc_trace.h b/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc_trace.h
new file mode 100644
index 000000000000..4e6086c4d86e
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/da_pertask_desc/da_pertask_desc_trace.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_DA_PERTASK_DESC
+DEFINE_EVENT(event_da_monitor_id, event_da_pertask_desc,
+ TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(id, state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor_id, error_da_pertask_desc,
+ TP_PROTO(int id, char *state, char *event),
+ TP_ARGS(id, state, event));
+#endif /* CONFIG_RV_MON_DA_PERTASK_DESC */
diff --git a/tools/verification/rvgen/tests/golden/ha_percpu/Kconfig b/tools/verification/rvgen/tests/golden/ha_percpu/Kconfig
new file mode 100644
index 000000000000..0cc185ccfddf
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ha_percpu/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_HA_PERCPU
+ depends on RV
+ # XXX: add dependencies if there
+ select HA_MON_EVENTS_IMPLICIT
+ bool "ha_percpu monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.c b/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.c
new file mode 100644
index 000000000000..f61b2eae691a
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.c
@@ -0,0 +1,244 @@
+// 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 "ha_percpu"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_PER_CPU
+/* XXX: If the monitor has several instances, consider HA_TIMER_WHEEL */
+#define HA_TIMER_TYPE HA_TIMER_HRTIMER
+#include "ha_percpu.h"
+#include <rv/ha_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+#define BAR_NS(ha_mon) /* XXX: what is BAR_NS(ha_mon)? */
+
+#define FOO_NS /* XXX: what is FOO_NS? */
+
+static inline u64 bar_ns(struct ha_monitor *ha_mon)
+{
+ return /* XXX: what is bar_ns(ha_mon)? */;
+}
+
+static u64 foo_ns = /* XXX: default value */;
+module_param(foo_ns, ullong, 0644);
+
+/*
+ * These functions define how to read and reset the environment variable.
+ *
+ * Common environment variables like ns-based and jiffy-based clocks have
+ * pre-define getters and resetters you can use. The parser can infer the type
+ * of the environment variable if you supply a measure unit in the constraint.
+ * If you define your own functions, make sure to add appropriate memory
+ * barriers if required.
+ * Some environment variables don't require a storage as they read a system
+ * state (e.g. preemption count). Those variables are never reset, so we don't
+ * define a reset function on monitors only relying on this type of variables.
+ */
+static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_ha_percpu env, u64 time_ns)
+{
+ if (env == clk_ha_percpu)
+ return ha_get_clk_ns(ha_mon, env, time_ns);
+ else if (env == env1_ha_percpu)
+ return /* XXX: how do I read env1? */
+ else if (env == env2_ha_percpu)
+ return /* XXX: how do I read env2? */
+ return ENV_INVALID_VALUE;
+}
+
+static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_ha_percpu env, u64 time_ns)
+{
+ if (env == clk_ha_percpu)
+ ha_reset_clk_ns(ha_mon, env, time_ns);
+}
+
+/*
+ * These functions are used to validate state transitions.
+ *
+ * They are generated by parsing the model, there is usually no need to change them.
+ * If the monitor requires a timer, there are functions responsible to arm it when
+ * the next state has a constraint, cancel it in any other case and to check
+ * that it didn't expire before the callback run. Transitions to the same state
+ * without a reset never affect timers.
+ * Due to the different representations between invariants and guards, there is
+ * a function to convert it in case invariants or guards are reachable from
+ * another invariant without reset. Those are not present if not required in
+ * the model. This is all automatic but is worth checking because it may show
+ * errors in the model (e.g. missing resets).
+ */
+static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (curr_state == S0_ha_percpu)
+ return ha_check_invariant_ns(ha_mon, clk_ha_percpu, time_ns);
+ else if (curr_state == S2_ha_percpu)
+ return ha_check_invariant_ns(ha_mon, clk_ha_percpu, time_ns);
+ return true;
+}
+
+static inline void ha_convert_inv_guard(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (curr_state == next_state)
+ return;
+ if (curr_state == S2_ha_percpu)
+ ha_inv_to_guard(ha_mon, clk_ha_percpu, BAR_NS(ha_mon), time_ns);
+}
+
+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 == S0_ha_percpu && event == event0_ha_percpu)
+ ha_reset_env(ha_mon, clk_ha_percpu, time_ns);
+ else if (curr_state == S0_ha_percpu && event == event1_ha_percpu)
+ ha_reset_env(ha_mon, clk_ha_percpu, time_ns);
+ else if (curr_state == S1_ha_percpu && event == event0_ha_percpu)
+ ha_reset_env(ha_mon, clk_ha_percpu, time_ns);
+ else if (curr_state == S1_ha_percpu && event == event2_ha_percpu) {
+ res = ha_get_env(ha_mon, env1_ha_percpu, time_ns) == 0ull;
+ ha_reset_env(ha_mon, clk_ha_percpu, time_ns);
+ } else if (curr_state == S2_ha_percpu && event == event1_ha_percpu)
+ res = ha_monitor_env_invalid(ha_mon, clk_ha_percpu) ||
+ ha_get_env(ha_mon, clk_ha_percpu, time_ns) < foo_ns;
+ else if (curr_state == S3_ha_percpu && event == event0_ha_percpu)
+ res = ha_monitor_env_invalid(ha_mon, clk_ha_percpu) ||
+ (ha_get_env(ha_mon, clk_ha_percpu, time_ns) < FOO_NS &&
+ ha_get_env(ha_mon, env2_ha_percpu, time_ns) == 0ull);
+ else if (curr_state == S3_ha_percpu && event == event1_ha_percpu) {
+ res = ha_monitor_env_invalid(ha_mon, clk_ha_percpu) ||
+ (ha_get_env(ha_mon, clk_ha_percpu, time_ns) < 5000ull &&
+ ha_get_env(ha_mon, env1_ha_percpu, time_ns) == 1ull);
+ ha_reset_env(ha_mon, clk_ha_percpu, time_ns);
+ }
+ return res;
+}
+
+static inline void ha_setup_invariants(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (next_state == curr_state && event != event0_ha_percpu)
+ return;
+ if (next_state == S0_ha_percpu)
+ ha_start_timer_ns(ha_mon, clk_ha_percpu, bar_ns(ha_mon), time_ns);
+ else if (next_state == S2_ha_percpu)
+ ha_start_timer_ns(ha_mon, clk_ha_percpu, BAR_NS(ha_mon), time_ns);
+ else if (curr_state == S0_ha_percpu)
+ ha_cancel_timer(ha_mon);
+ else if (curr_state == S2_ha_percpu)
+ ha_cancel_timer(ha_mon);
+}
+
+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_invariants(ha_mon, curr_state, event, next_state, time_ns))
+ return false;
+
+ ha_convert_inv_guard(ha_mon, curr_state, event, next_state, time_ns);
+
+ if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
+ return false;
+
+ ha_setup_invariants(ha_mon, curr_state, event, next_state, time_ns);
+
+ return true;
+}
+
+static void handle_event0(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event always leads to the initial state */
+ da_handle_start_event(event0_ha_percpu);
+}
+
+static void handle_event1(void *data, /* XXX: fill header */)
+{
+ da_handle_event(event1_ha_percpu);
+}
+
+static void handle_event2(void *data, /* XXX: fill header */)
+{
+ da_handle_event(event2_ha_percpu);
+}
+
+static int enable_ha_percpu(void)
+{
+ int retval;
+
+ retval = ha_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("ha_percpu", /* XXX: tracepoint */, handle_event0);
+ rv_attach_trace_probe("ha_percpu", /* XXX: tracepoint */, handle_event1);
+ rv_attach_trace_probe("ha_percpu", /* XXX: tracepoint */, handle_event2);
+
+ return 0;
+}
+
+static void disable_ha_percpu(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("ha_percpu", /* XXX: tracepoint */, handle_event0);
+ rv_detach_trace_probe("ha_percpu", /* XXX: tracepoint */, handle_event1);
+ rv_detach_trace_probe("ha_percpu", /* XXX: tracepoint */, handle_event2);
+
+ ha_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "ha_percpu",
+ .description = "auto-generated",
+ .enable = enable_ha_percpu,
+ .disable = disable_ha_percpu,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_ha_percpu(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_ha_percpu(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_ha_percpu);
+module_exit(unregister_ha_percpu);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("ha_percpu: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.h b/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.h
new file mode 100644
index 000000000000..2538db4f6a26
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of ha_percpu automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME ha_percpu
+
+enum states_ha_percpu {
+ S0_ha_percpu,
+ S1_ha_percpu,
+ S2_ha_percpu,
+ S3_ha_percpu,
+ state_max_ha_percpu,
+};
+
+#define INVALID_STATE state_max_ha_percpu
+
+enum events_ha_percpu {
+ event0_ha_percpu,
+ event1_ha_percpu,
+ event2_ha_percpu,
+ event_max_ha_percpu,
+};
+
+enum envs_ha_percpu {
+ clk_ha_percpu,
+ env1_ha_percpu,
+ env2_ha_percpu,
+ env_max_ha_percpu,
+ env_max_stored_ha_percpu = env1_ha_percpu,
+};
+
+_Static_assert(env_max_stored_ha_percpu <= MAX_HA_ENV_LEN, "Not enough slots");
+#define HA_CLK_NS
+
+struct automaton_ha_percpu {
+ char *state_names[state_max_ha_percpu];
+ char *event_names[event_max_ha_percpu];
+ char *env_names[env_max_ha_percpu];
+ unsigned char function[state_max_ha_percpu][event_max_ha_percpu];
+ unsigned char initial_state;
+ bool final_states[state_max_ha_percpu];
+};
+
+static const struct automaton_ha_percpu automaton_ha_percpu = {
+ .state_names = {
+ "S0",
+ "S1",
+ "S2",
+ "S3",
+ },
+ .event_names = {
+ "event0",
+ "event1",
+ "event2",
+ },
+ .env_names = {
+ "clk",
+ "env1",
+ "env2",
+ },
+ .function = {
+ { S0_ha_percpu, S1_ha_percpu, INVALID_STATE },
+ { S0_ha_percpu, INVALID_STATE, S2_ha_percpu },
+ { INVALID_STATE, S2_ha_percpu, S3_ha_percpu },
+ { S0_ha_percpu, S1_ha_percpu, INVALID_STATE },
+ },
+ .initial_state = S0_ha_percpu,
+ .final_states = { 1, 0, 0, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu_trace.h b/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu_trace.h
new file mode 100644
index 000000000000..074ddff6a60d
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ha_percpu/ha_percpu_trace.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_HA_PERCPU
+DEFINE_EVENT(event_da_monitor, event_ha_percpu,
+ TP_PROTO(char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor, error_ha_percpu,
+ TP_PROTO(char *state, char *event),
+ TP_ARGS(state, event));
+
+DEFINE_EVENT(error_env_da_monitor, error_env_ha_percpu,
+ TP_PROTO(char *state, char *event, char *env),
+ TP_ARGS(state, event, env));
+#endif /* CONFIG_RV_MON_HA_PERCPU */
diff --git a/tools/verification/rvgen/tests/golden/ltl_pertask/Kconfig b/tools/verification/rvgen/tests/golden/ltl_pertask/Kconfig
new file mode 100644
index 000000000000..b37f46670bfd
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ltl_pertask/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_LTL_PERTASK
+ depends on RV
+ # XXX: add dependencies if there
+ select LTL_MON_EVENTS_ID
+ bool "ltl_pertask monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.c b/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.c
new file mode 100644
index 000000000000..2c60b5c5b4e0
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.c
@@ -0,0 +1,107 @@
+// 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 "ltl_pertask"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#include "ltl_pertask.h"
+#include <rv/ltl_monitor.h>
+
+static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon)
+{
+ /*
+ * This is called everytime the Buchi automaton is triggered.
+ *
+ * This function could be used to fetch the atomic propositions which
+ * are expensive to trace. It is possible only if the atomic proposition
+ * does not need to be updated at precise time.
+ *
+ * It is recommended to use tracepoints and ltl_atom_update() instead.
+ */
+}
+
+static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
+{
+ /*
+ * This should initialize as many atomic propositions as possible.
+ *
+ * @task_creation indicates whether the task is being created. This is
+ * false if the task is already running before the monitor is enabled.
+ */
+ ltl_atom_set(mon, LTL_EVENT_A, true/false);
+ ltl_atom_set(mon, LTL_EVENT_B, true/false);
+}
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ */
+static void handle_example_event(void *data, /* XXX: fill header */)
+{
+ ltl_atom_update(task, LTL_EVENT_A, true/false);
+}
+
+static int enable_ltl_pertask(void)
+{
+ int retval;
+
+ retval = ltl_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("ltl_pertask", /* XXX: tracepoint */, handle_example_event);
+
+ return 0;
+}
+
+static void disable_ltl_pertask(void)
+{
+ rv_detach_trace_probe("ltl_pertask", /* XXX: tracepoint */, handle_example_event);
+
+ ltl_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "ltl_pertask",
+ .description = "auto-generated",
+ .enable = enable_ltl_pertask,
+ .disable = disable_ltl_pertask,
+};
+
+static int __init register_ltl_pertask(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_ltl_pertask(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_ltl_pertask);
+module_exit(unregister_ltl_pertask);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("ltl_pertask: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.h b/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.h
new file mode 100644
index 000000000000..7e5de351b8fa
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * C implementation of Buchi automaton, automatically generated by
+ * tools/verification/rvgen from the linear temporal logic specification.
+ * For further information, see kernel documentation:
+ * Documentation/trace/rv/linear_temporal_logic.rst
+ */
+
+#include <linux/rv.h>
+
+#define MONITOR_NAME ltl_pertask
+
+enum ltl_atom {
+ LTL_EVENT_A,
+ LTL_EVENT_B,
+ LTL_NUM_ATOM
+};
+static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);
+
+static const char *ltl_atom_str(enum ltl_atom atom)
+{
+ static const char *const names[] = {
+ "ev_a",
+ "ev_b",
+ };
+
+ return names[atom];
+}
+
+enum ltl_buchi_state {
+ S0,
+ S1,
+ S2,
+ S3,
+ S4,
+ RV_NUM_BA_STATES
+};
+static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);
+
+static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ if (val1)
+ __set_bit(S0, mon->states);
+ if (true)
+ __set_bit(S1, mon->states);
+ if (event_b)
+ __set_bit(S4, mon->states);
+}
+
+static void
+ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ switch (state) {
+ case S0:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S1:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S2:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S3:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S4:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ }
+}
diff --git a/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask_trace.h b/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask_trace.h
new file mode 100644
index 000000000000..ebd53621a5b1
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/ltl_pertask/ltl_pertask_trace.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_LTL_PERTASK
+DEFINE_EVENT(event_ltl_monitor_id, event_ltl_pertask,
+ TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next),
+ TP_ARGS(task, states, atoms, next));
+DEFINE_EVENT(error_ltl_monitor_id, error_ltl_pertask,
+ TP_PROTO(struct task_struct *task),
+ TP_ARGS(task));
+#endif /* CONFIG_RV_MON_LTL_PERTASK */
diff --git a/tools/verification/rvgen/tests/golden/test_container/Kconfig b/tools/verification/rvgen/tests/golden/test_container/Kconfig
new file mode 100644
index 000000000000..2becb65dddad
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_container/Kconfig
@@ -0,0 +1,5 @@
+config RV_MON_TEST_CONTAINER
+ depends on RV
+ bool "test_container monitor"
+ help
+ Test container for grouping monitors
diff --git a/tools/verification/rvgen/tests/golden/test_container/test_container.c b/tools/verification/rvgen/tests/golden/test_container/test_container.c
new file mode 100644
index 000000000000..e7e34592c6c5
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_container/test_container.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rv.h>
+
+#define MODULE_NAME "test_container"
+
+#include "test_container.h"
+
+struct rv_monitor rv_test_container = {
+ .name = "test_container",
+ .description = "Test container for grouping monitors",
+ .enable = NULL,
+ .disable = NULL,
+ .reset = NULL,
+ .enabled = 0,
+};
+
+static int __init register_test_container(void)
+{
+ return rv_register_monitor(&rv_test_container, NULL);
+}
+
+static void __exit unregister_test_container(void)
+{
+ rv_unregister_monitor(&rv_test_container);
+}
+
+module_init(register_test_container);
+module_exit(unregister_test_container);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_container: Test container for grouping monitors");
diff --git a/tools/verification/rvgen/tests/golden/test_container/test_container.h b/tools/verification/rvgen/tests/golden/test_container/test_container.h
new file mode 100644
index 000000000000..83e434432650
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_container/test_container.h
@@ -0,0 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+extern struct rv_monitor rv_test_container;
diff --git a/tools/verification/rvgen/tests/golden/test_da/Kconfig b/tools/verification/rvgen/tests/golden/test_da/Kconfig
new file mode 100644
index 000000000000..0143a148ef34
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_TEST_DA
+ depends on RV
+ # XXX: add dependencies if there
+ select DA_MON_EVENTS_IMPLICIT
+ bool "test_da monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/test_da/test_da.c b/tools/verification/rvgen/tests/golden/test_da/test_da.c
new file mode 100644
index 000000000000..59b8dfabbbf1
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da/test_da.c
@@ -0,0 +1,95 @@
+// 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 "test_da"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_PER_CPU
+#include "test_da.h"
+#include <rv/da_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+static void handle_event_1(void *data, /* XXX: fill header */)
+{
+ da_handle_event(event_1_test_da);
+}
+
+static void handle_event_2(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event always leads to the initial state */
+ da_handle_start_event(event_2_test_da);
+}
+
+static int enable_test_da(void)
+{
+ int retval;
+
+ retval = da_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("test_da", /* XXX: tracepoint */, handle_event_1);
+ rv_attach_trace_probe("test_da", /* XXX: tracepoint */, handle_event_2);
+
+ return 0;
+}
+
+static void disable_test_da(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("test_da", /* XXX: tracepoint */, handle_event_1);
+ rv_detach_trace_probe("test_da", /* XXX: tracepoint */, handle_event_2);
+
+ da_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "test_da",
+ .description = "auto-generated",
+ .enable = enable_test_da,
+ .disable = disable_test_da,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_test_da(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_test_da(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_test_da);
+module_exit(unregister_test_da);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_da: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/test_da/test_da.h b/tools/verification/rvgen/tests/golden/test_da/test_da.h
new file mode 100644
index 000000000000..d55795efbb61
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da/test_da.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of test_da automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME test_da
+
+enum states_test_da {
+ state_a_test_da,
+ state_b_test_da,
+ state_max_test_da,
+};
+
+#define INVALID_STATE state_max_test_da
+
+enum events_test_da {
+ event_1_test_da,
+ event_2_test_da,
+ event_max_test_da,
+};
+
+struct automaton_test_da {
+ char *state_names[state_max_test_da];
+ char *event_names[event_max_test_da];
+ unsigned char function[state_max_test_da][event_max_test_da];
+ unsigned char initial_state;
+ bool final_states[state_max_test_da];
+};
+
+static const struct automaton_test_da automaton_test_da = {
+ .state_names = {
+ "state_a",
+ "state_b",
+ },
+ .event_names = {
+ "event_1",
+ "event_2",
+ },
+ .function = {
+ { state_b_test_da, state_a_test_da },
+ { INVALID_STATE, state_a_test_da },
+ },
+ .initial_state = state_a_test_da,
+ .final_states = { 1, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/test_da/test_da_trace.h b/tools/verification/rvgen/tests/golden/test_da/test_da_trace.h
new file mode 100644
index 000000000000..8bd67115d244
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_da/test_da_trace.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_TEST_DA
+DEFINE_EVENT(event_da_monitor, event_test_da,
+ TP_PROTO(char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor, error_test_da,
+ TP_PROTO(char *state, char *event),
+ TP_ARGS(state, event));
+#endif /* CONFIG_RV_MON_TEST_DA */
diff --git a/tools/verification/rvgen/tests/golden/test_ha/Kconfig b/tools/verification/rvgen/tests/golden/test_ha/Kconfig
new file mode 100644
index 000000000000..f4048290c774
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_TEST_HA
+ depends on RV
+ # XXX: add dependencies if there
+ select HA_MON_EVENTS_ID
+ bool "test_ha monitor"
+ help
+ auto-generated
diff --git a/tools/verification/rvgen/tests/golden/test_ha/test_ha.c b/tools/verification/rvgen/tests/golden/test_ha/test_ha.c
new file mode 100644
index 000000000000..900370428e5f
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha/test_ha.c
@@ -0,0 +1,247 @@
+// 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 "test_ha"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#define RV_MON_TYPE RV_MON_PER_TASK
+/* XXX: If the monitor has several instances, consider HA_TIMER_WHEEL */
+#define HA_TIMER_TYPE HA_TIMER_HRTIMER
+#include "test_ha.h"
+#include <rv/ha_monitor.h>
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ *
+ */
+#define BAR_NS(ha_mon) /* XXX: what is BAR_NS(ha_mon)? */
+
+#define FOO_NS /* XXX: what is FOO_NS? */
+
+static inline u64 bar_ns(struct ha_monitor *ha_mon)
+{
+ return /* XXX: what is bar_ns(ha_mon)? */;
+}
+
+static u64 foo_ns = /* XXX: default value */;
+module_param(foo_ns, ullong, 0644);
+
+/*
+ * These functions define how to read and reset the environment variable.
+ *
+ * Common environment variables like ns-based and jiffy-based clocks have
+ * pre-define getters and resetters you can use. The parser can infer the type
+ * of the environment variable if you supply a measure unit in the constraint.
+ * If you define your own functions, make sure to add appropriate memory
+ * barriers if required.
+ * Some environment variables don't require a storage as they read a system
+ * state (e.g. preemption count). Those variables are never reset, so we don't
+ * define a reset function on monitors only relying on this type of variables.
+ */
+static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_test_ha env, u64 time_ns)
+{
+ if (env == clk_test_ha)
+ return ha_get_clk_ns(ha_mon, env, time_ns);
+ else if (env == env1_test_ha)
+ return /* XXX: how do I read env1? */
+ else if (env == env2_test_ha)
+ return /* XXX: how do I read env2? */
+ return ENV_INVALID_VALUE;
+}
+
+static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_test_ha env, u64 time_ns)
+{
+ if (env == clk_test_ha)
+ ha_reset_clk_ns(ha_mon, env, time_ns);
+}
+
+/*
+ * These functions are used to validate state transitions.
+ *
+ * They are generated by parsing the model, there is usually no need to change them.
+ * If the monitor requires a timer, there are functions responsible to arm it when
+ * the next state has a constraint, cancel it in any other case and to check
+ * that it didn't expire before the callback run. Transitions to the same state
+ * without a reset never affect timers.
+ * Due to the different representations between invariants and guards, there is
+ * a function to convert it in case invariants or guards are reachable from
+ * another invariant without reset. Those are not present if not required in
+ * the model. This is all automatic but is worth checking because it may show
+ * errors in the model (e.g. missing resets).
+ */
+static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (curr_state == S0_test_ha)
+ return ha_check_invariant_ns(ha_mon, clk_test_ha, time_ns);
+ else if (curr_state == S2_test_ha)
+ return ha_check_invariant_ns(ha_mon, clk_test_ha, time_ns);
+ return true;
+}
+
+static inline void ha_convert_inv_guard(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (curr_state == next_state)
+ return;
+ if (curr_state == S2_test_ha)
+ ha_inv_to_guard(ha_mon, clk_test_ha, BAR_NS(ha_mon), time_ns);
+}
+
+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 == S0_test_ha && event == event0_test_ha)
+ ha_reset_env(ha_mon, clk_test_ha, time_ns);
+ else if (curr_state == S0_test_ha && event == event1_test_ha)
+ ha_reset_env(ha_mon, clk_test_ha, time_ns);
+ else if (curr_state == S1_test_ha && event == event0_test_ha)
+ ha_reset_env(ha_mon, clk_test_ha, time_ns);
+ else if (curr_state == S1_test_ha && event == event2_test_ha) {
+ res = ha_get_env(ha_mon, env1_test_ha, time_ns) == 0ull;
+ ha_reset_env(ha_mon, clk_test_ha, time_ns);
+ } else if (curr_state == S2_test_ha && event == event1_test_ha)
+ res = ha_monitor_env_invalid(ha_mon, clk_test_ha) ||
+ ha_get_env(ha_mon, clk_test_ha, time_ns) < foo_ns;
+ else if (curr_state == S3_test_ha && event == event0_test_ha)
+ res = ha_monitor_env_invalid(ha_mon, clk_test_ha) ||
+ (ha_get_env(ha_mon, clk_test_ha, time_ns) < FOO_NS &&
+ ha_get_env(ha_mon, env2_test_ha, time_ns) == 0ull);
+ else if (curr_state == S3_test_ha && event == event1_test_ha) {
+ res = ha_monitor_env_invalid(ha_mon, clk_test_ha) ||
+ (ha_get_env(ha_mon, clk_test_ha, time_ns) < 5000ull &&
+ ha_get_env(ha_mon, env1_test_ha, time_ns) == 1ull);
+ ha_reset_env(ha_mon, clk_test_ha, time_ns);
+ }
+ return res;
+}
+
+static inline void ha_setup_invariants(struct ha_monitor *ha_mon,
+ enum states curr_state, enum events event,
+ enum states next_state, u64 time_ns)
+{
+ if (next_state == curr_state && event != event0_test_ha)
+ return;
+ if (next_state == S0_test_ha)
+ ha_start_timer_ns(ha_mon, clk_test_ha, bar_ns(ha_mon), time_ns);
+ else if (next_state == S2_test_ha)
+ ha_start_timer_ns(ha_mon, clk_test_ha, BAR_NS(ha_mon), time_ns);
+ else if (curr_state == S0_test_ha)
+ ha_cancel_timer(ha_mon);
+ else if (curr_state == S2_test_ha)
+ ha_cancel_timer(ha_mon);
+}
+
+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_invariants(ha_mon, curr_state, event, next_state, time_ns))
+ return false;
+
+ ha_convert_inv_guard(ha_mon, curr_state, event, next_state, time_ns);
+
+ if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
+ return false;
+
+ ha_setup_invariants(ha_mon, curr_state, event, next_state, time_ns);
+
+ return true;
+}
+
+static void handle_event0(void *data, /* XXX: fill header */)
+{
+ /* XXX: validate that this event always leads to the initial state */
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_start_event(p, event0_test_ha);
+}
+
+static void handle_event1(void *data, /* XXX: fill header */)
+{
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_event(p, event1_test_ha);
+}
+
+static void handle_event2(void *data, /* XXX: fill header */)
+{
+ struct task_struct *p = /* XXX: how do I get p? */;
+ da_handle_event(p, event2_test_ha);
+}
+
+static int enable_test_ha(void)
+{
+ int retval;
+
+ retval = ha_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("test_ha", /* XXX: tracepoint */, handle_event0);
+ rv_attach_trace_probe("test_ha", /* XXX: tracepoint */, handle_event1);
+ rv_attach_trace_probe("test_ha", /* XXX: tracepoint */, handle_event2);
+
+ return 0;
+}
+
+static void disable_test_ha(void)
+{
+ rv_this.enabled = 0;
+
+ rv_detach_trace_probe("test_ha", /* XXX: tracepoint */, handle_event0);
+ rv_detach_trace_probe("test_ha", /* XXX: tracepoint */, handle_event1);
+ rv_detach_trace_probe("test_ha", /* XXX: tracepoint */, handle_event2);
+
+ ha_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "test_ha",
+ .description = "auto-generated",
+ .enable = enable_test_ha,
+ .disable = disable_test_ha,
+ .reset = da_monitor_reset_all,
+ .enabled = 0,
+};
+
+static int __init register_test_ha(void)
+{
+ return rv_register_monitor(&rv_this, NULL);
+}
+
+static void __exit unregister_test_ha(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_test_ha);
+module_exit(unregister_test_ha);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_ha: auto-generated");
diff --git a/tools/verification/rvgen/tests/golden/test_ha/test_ha.h b/tools/verification/rvgen/tests/golden/test_ha/test_ha.h
new file mode 100644
index 000000000000..949fa4453403
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha/test_ha.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Automatically generated C representation of test_ha automaton
+ * For further information about this format, see kernel documentation:
+ * Documentation/trace/rv/deterministic_automata.rst
+ */
+
+#define MONITOR_NAME test_ha
+
+enum states_test_ha {
+ S0_test_ha,
+ S1_test_ha,
+ S2_test_ha,
+ S3_test_ha,
+ state_max_test_ha,
+};
+
+#define INVALID_STATE state_max_test_ha
+
+enum events_test_ha {
+ event0_test_ha,
+ event1_test_ha,
+ event2_test_ha,
+ event_max_test_ha,
+};
+
+enum envs_test_ha {
+ clk_test_ha,
+ env1_test_ha,
+ env2_test_ha,
+ env_max_test_ha,
+ env_max_stored_test_ha = env1_test_ha,
+};
+
+_Static_assert(env_max_stored_test_ha <= MAX_HA_ENV_LEN, "Not enough slots");
+#define HA_CLK_NS
+
+struct automaton_test_ha {
+ char *state_names[state_max_test_ha];
+ char *event_names[event_max_test_ha];
+ char *env_names[env_max_test_ha];
+ unsigned char function[state_max_test_ha][event_max_test_ha];
+ unsigned char initial_state;
+ bool final_states[state_max_test_ha];
+};
+
+static const struct automaton_test_ha automaton_test_ha = {
+ .state_names = {
+ "S0",
+ "S1",
+ "S2",
+ "S3",
+ },
+ .event_names = {
+ "event0",
+ "event1",
+ "event2",
+ },
+ .env_names = {
+ "clk",
+ "env1",
+ "env2",
+ },
+ .function = {
+ { S0_test_ha, S1_test_ha, INVALID_STATE },
+ { S0_test_ha, INVALID_STATE, S2_test_ha },
+ { INVALID_STATE, S2_test_ha, S3_test_ha },
+ { S0_test_ha, S1_test_ha, INVALID_STATE },
+ },
+ .initial_state = S0_test_ha,
+ .final_states = { 1, 0, 0, 0 },
+};
diff --git a/tools/verification/rvgen/tests/golden/test_ha/test_ha_trace.h b/tools/verification/rvgen/tests/golden/test_ha/test_ha_trace.h
new file mode 100644
index 000000000000..381bafcb3322
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ha/test_ha_trace.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_TEST_HA
+DEFINE_EVENT(event_da_monitor_id, event_test_ha,
+ TP_PROTO(int id, char *state, char *event, char *next_state, bool final_state),
+ TP_ARGS(id, state, event, next_state, final_state));
+
+DEFINE_EVENT(error_da_monitor_id, error_test_ha,
+ TP_PROTO(int id, char *state, char *event),
+ TP_ARGS(id, state, event));
+
+DEFINE_EVENT(error_env_da_monitor_id, error_env_test_ha,
+ TP_PROTO(int id, char *state, char *event, char *env),
+ TP_ARGS(id, state, event, env));
+#endif /* CONFIG_RV_MON_TEST_HA */
diff --git a/tools/verification/rvgen/tests/golden/test_ltl/Kconfig b/tools/verification/rvgen/tests/golden/test_ltl/Kconfig
new file mode 100644
index 000000000000..e2d0e721f180
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+config RV_MON_TEST_LTL
+ depends on RV
+ # XXX: add dependencies if there
+ depends on RV_MON_LTL_PARENT
+ default y
+ select LTL_MON_EVENTS_ID
+ bool "test_ltl monitor"
+ help
+ Simple description
diff --git a/tools/verification/rvgen/tests/golden/test_ltl/test_ltl.c b/tools/verification/rvgen/tests/golden/test_ltl/test_ltl.c
new file mode 100644
index 000000000000..dd961c5dc8ad
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl/test_ltl.c
@@ -0,0 +1,108 @@
+// 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 "test_ltl"
+
+/*
+ * XXX: include required tracepoint headers, e.g.,
+ * #include <trace/events/sched.h>
+ */
+#include <rv_trace.h>
+#include <monitors/ltl_parent/ltl_parent.h>
+
+
+/*
+ * This is the self-generated part of the monitor. Generally, there is no need
+ * to touch this section.
+ */
+#include "test_ltl.h"
+#include <rv/ltl_monitor.h>
+
+static void ltl_atoms_fetch(struct task_struct *task, struct ltl_monitor *mon)
+{
+ /*
+ * This is called everytime the Buchi automaton is triggered.
+ *
+ * This function could be used to fetch the atomic propositions which
+ * are expensive to trace. It is possible only if the atomic proposition
+ * does not need to be updated at precise time.
+ *
+ * It is recommended to use tracepoints and ltl_atom_update() instead.
+ */
+}
+
+static void ltl_atoms_init(struct task_struct *task, struct ltl_monitor *mon, bool task_creation)
+{
+ /*
+ * This should initialize as many atomic propositions as possible.
+ *
+ * @task_creation indicates whether the task is being created. This is
+ * false if the task is already running before the monitor is enabled.
+ */
+ ltl_atom_set(mon, LTL_EVENT_A, true/false);
+ ltl_atom_set(mon, LTL_EVENT_B, true/false);
+}
+
+/*
+ * This is the instrumentation part of the monitor.
+ *
+ * This is the section where manual work is required. Here the kernel events
+ * are translated into model's event.
+ */
+static void handle_example_event(void *data, /* XXX: fill header */)
+{
+ ltl_atom_update(task, LTL_EVENT_A, true/false);
+}
+
+static int enable_test_ltl(void)
+{
+ int retval;
+
+ retval = ltl_monitor_init();
+ if (retval)
+ return retval;
+
+ rv_attach_trace_probe("test_ltl", /* XXX: tracepoint */, handle_example_event);
+
+ return 0;
+}
+
+static void disable_test_ltl(void)
+{
+ rv_detach_trace_probe("test_ltl", /* XXX: tracepoint */, handle_example_event);
+
+ ltl_monitor_destroy();
+}
+
+/*
+ * This is the monitor register section.
+ */
+static struct rv_monitor rv_this = {
+ .name = "test_ltl",
+ .description = "Simple description",
+ .enable = enable_test_ltl,
+ .disable = disable_test_ltl,
+};
+
+static int __init register_test_ltl(void)
+{
+ return rv_register_monitor(&rv_this, &rv_ltl_parent);
+}
+
+static void __exit unregister_test_ltl(void)
+{
+ rv_unregister_monitor(&rv_this);
+}
+
+module_init(register_test_ltl);
+module_exit(unregister_test_ltl);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rvgen: auto-generated");
+MODULE_DESCRIPTION("test_ltl: Simple description");
diff --git a/tools/verification/rvgen/tests/golden/test_ltl/test_ltl.h b/tools/verification/rvgen/tests/golden/test_ltl/test_ltl.h
new file mode 100644
index 000000000000..7895f2e233e8
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl/test_ltl.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * C implementation of Buchi automaton, automatically generated by
+ * tools/verification/rvgen from the linear temporal logic specification.
+ * For further information, see kernel documentation:
+ * Documentation/trace/rv/linear_temporal_logic.rst
+ */
+
+#include <linux/rv.h>
+
+#define MONITOR_NAME test_ltl
+
+enum ltl_atom {
+ LTL_EVENT_A,
+ LTL_EVENT_B,
+ LTL_NUM_ATOM
+};
+static_assert(LTL_NUM_ATOM <= RV_MAX_LTL_ATOM);
+
+static const char *ltl_atom_str(enum ltl_atom atom)
+{
+ static const char *const names[] = {
+ "ev_a",
+ "ev_b",
+ };
+
+ return names[atom];
+}
+
+enum ltl_buchi_state {
+ S0,
+ S1,
+ S2,
+ S3,
+ S4,
+ RV_NUM_BA_STATES
+};
+static_assert(RV_NUM_BA_STATES <= RV_MAX_BA_STATES);
+
+static void ltl_start(struct task_struct *task, struct ltl_monitor *mon)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ if (val1)
+ __set_bit(S0, mon->states);
+ if (true)
+ __set_bit(S1, mon->states);
+ if (event_b)
+ __set_bit(S4, mon->states);
+}
+
+static void
+ltl_possible_next_states(struct ltl_monitor *mon, unsigned int state, unsigned long *next)
+{
+ bool event_b = test_bit(LTL_EVENT_B, mon->atoms);
+ bool event_a = test_bit(LTL_EVENT_A, mon->atoms);
+ bool val1 = !event_a;
+
+ switch (state) {
+ case S0:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S1:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S2:
+ if (true)
+ __set_bit(S1, next);
+ if (true && val1)
+ __set_bit(S2, next);
+ if (event_b && val1)
+ __set_bit(S3, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S3:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ case S4:
+ if (val1)
+ __set_bit(S0, next);
+ if (true)
+ __set_bit(S1, next);
+ if (event_b)
+ __set_bit(S4, next);
+ break;
+ }
+}
diff --git a/tools/verification/rvgen/tests/golden/test_ltl/test_ltl_trace.h b/tools/verification/rvgen/tests/golden/test_ltl/test_ltl_trace.h
new file mode 100644
index 000000000000..3571b004c114
--- /dev/null
+++ b/tools/verification/rvgen/tests/golden/test_ltl/test_ltl_trace.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Snippet to be included in rv_trace.h
+ */
+
+#ifdef CONFIG_RV_MON_TEST_LTL
+DEFINE_EVENT(event_ltl_monitor_id, event_test_ltl,
+ TP_PROTO(struct task_struct *task, char *states, char *atoms, char *next),
+ TP_ARGS(task, states, atoms, next));
+DEFINE_EVENT(error_ltl_monitor_id, error_test_ltl,
+ TP_PROTO(struct task_struct *task),
+ TP_ARGS(task));
+#endif /* CONFIG_RV_MON_TEST_LTL */
diff --git a/tools/verification/rvgen/tests/specs/test_da.dot b/tools/verification/rvgen/tests/specs/test_da.dot
new file mode 100644
index 000000000000..e555c239b221
--- /dev/null
+++ b/tools/verification/rvgen/tests/specs/test_da.dot
@@ -0,0 +1,16 @@
+digraph state_automaton {
+ {node [shape = circle] "state_b"};
+ {node [shape = plaintext, style=invis, label=""] "__init_state_a"};
+ {node [shape = doublecircle] "state_a"};
+ {node [shape = circle] "state_a"};
+ "__init_state_a" -> "state_a";
+ "state_a" [label = "state_a"];
+ "state_a" -> "state_a" [ label = "event_2" ];
+ "state_a" -> "state_b" [ label = "event_1" ];
+ "state_b" [label = "state_b"];
+ "state_b" -> "state_a" [ label = "event_2" ];
+ { rank = min ;
+ "__init_state_a";
+ "state_a";
+ }
+}
diff --git a/tools/verification/rvgen/tests/specs/test_da2.dot b/tools/verification/rvgen/tests/specs/test_da2.dot
new file mode 100644
index 000000000000..cdd4192f58ae
--- /dev/null
+++ b/tools/verification/rvgen/tests/specs/test_da2.dot
@@ -0,0 +1,19 @@
+digraph state_automaton {
+ {node [shape = circle] "state_b"};
+ {node [shape = circle] "state_c"};
+ {node [shape = plaintext, style=invis, label=""] "__init_state_a"};
+ {node [shape = doublecircle] "state_a"};
+ {node [shape = circle] "state_a"};
+ "__init_state_a" -> "state_a";
+ "state_a" [label = "state_a"];
+ "state_a" -> "state_b" [ label = "event_1" ];
+ "state_a" -> "state_c" [ label = "event_2" ];
+ "state_b" [label = "state_b"];
+ "state_b" -> "state_a" [ label = "event_2" ];
+ "state_b" -> "state_c" [ label = "event_3" ];
+ "state_c" [label = "state_c"];
+ { rank = min ;
+ "__init_state_a";
+ "state_a";
+ }
+}
diff --git a/tools/verification/rvgen/tests/specs/test_ha.dot b/tools/verification/rvgen/tests/specs/test_ha.dot
new file mode 100644
index 000000000000..af18ad7389ec
--- /dev/null
+++ b/tools/verification/rvgen/tests/specs/test_ha.dot
@@ -0,0 +1,27 @@
+digraph state_automaton {
+ center = true;
+ size = "7,11";
+ {node [shape = circle] "S1"};
+ {node [shape = plaintext, style=invis, label=""] "__init_S0"};
+ {node [shape = doublecircle] "S0"};
+ {node [shape = circle] "S0"};
+ {node [shape = circle] "S2"};
+ {node [shape = circle] "S3"};
+ "__init_S0" -> "S0";
+ "S0" [label = "S0\nclk < bar_ns()", color = green3];
+ "S1" [label = "S1"];
+ "S2" [label = "S2\nclk < BAR_NS()"];
+ "S3" [label = "S3"];
+ "S1" -> "S0" [ label = "event0;reset(clk)" ];
+ "S0" -> "S1" [ label = "event1;reset(clk)" ];
+ "S0" -> "S0" [ label = "event0;reset(clk)" ];
+ "S1" -> "S2" [ label = "event2;env1 == 0;reset(clk)" ];
+ "S2" -> "S3" [ label = "event2" ];
+ "S2" -> "S2" [ label = "event1;clk < foo_ns" ];
+ "S3" -> "S0" [ label = "event0;clk < FOO_NS && env2 == 0" ];
+ "S3" -> "S1" [ label = "event1;clk < 5us && env1 == 1;reset(clk)" ];
+ { rank = min ;
+ "__init_S0";
+ "S0";
+ }
+}
diff --git a/tools/verification/rvgen/tests/specs/test_invalid.dot b/tools/verification/rvgen/tests/specs/test_invalid.dot
new file mode 100644
index 000000000000..17c63fc57f17
--- /dev/null
+++ b/tools/verification/rvgen/tests/specs/test_invalid.dot
@@ -0,0 +1,8 @@
+digraph invalid {
+ {node [shape = circle] "init"};
+ {node [shape = circle] "state1"};
+ "init" [label = "init"];
+ "init" -> "state1" [ label = "event_a" ];
+ "state1" [label = "state1"];
+ "state1" -> "init" [ label = "event_b" ];
+}
diff --git a/tools/verification/rvgen/tests/specs/test_invalid.ltl b/tools/verification/rvgen/tests/specs/test_invalid.ltl
new file mode 100644
index 000000000000..cf36307e003c
--- /dev/null
+++ b/tools/verification/rvgen/tests/specs/test_invalid.ltl
@@ -0,0 +1 @@
+RULE = A invalid B
diff --git a/tools/verification/rvgen/tests/specs/test_invalid_ha.dot b/tools/verification/rvgen/tests/specs/test_invalid_ha.dot
new file mode 100644
index 000000000000..06de6aa8709f
--- /dev/null
+++ b/tools/verification/rvgen/tests/specs/test_invalid_ha.dot
@@ -0,0 +1,16 @@
+digraph state_automaton {
+ {node [shape = circle] "state_b"};
+ {node [shape = plaintext, style=invis, label=""] "__init_state_a"};
+ {node [shape = doublecircle] "state_a"};
+ {node [shape = circle] "state_a"};
+ "__init_state_a" -> "state_a";
+ "state_a" [label = "state_a;clk < 1"];
+ "state_a" -> "state_a" [ label = "event_2;reset(clk)" ];
+ "state_a" -> "state_b" [ label = "event_1;wrong_constraint" ];
+ "state_b" [label = "state_b"];
+ "state_b" -> "state_a" [ label = "event_2" ];
+ { rank = min ;
+ "__init_state_a";
+ "state_a";
+ }
+}
diff --git a/tools/verification/rvgen/tests/specs/test_ltl.ltl b/tools/verification/rvgen/tests/specs/test_ltl.ltl
new file mode 100644
index 000000000000..5ed658abd69c
--- /dev/null
+++ b/tools/verification/rvgen/tests/specs/test_ltl.ltl
@@ -0,0 +1 @@
+RULE = always (EVENT_A imply eventually EVENT_B)
--
2.55.0
^ permalink raw reply related
* [PATCH v5 06/17] tools/rv: Add selftests
From: Gabriele Monaco @ 2026-07-23 7:45 UTC (permalink / raw)
To: linux-kernel, linux-trace-kernel, Steven Rostedt, Gabriele Monaco
Cc: Nam Cao, Thomas Weissschuh, Tomas Glozar, John Kacur, Wen Yang
In-Reply-To: <20260723074534.43521-1-gmonaco@redhat.com>
The rv tool needs automated testing to catch regressions and verify
correct functionality across different usage scenarios.
Add selftests that validate monitor listing (including containers and
nested monitors), monitor execution with different configurations
(reactors, verbose output, tracing), and trace output format for both
per-task and per-cpu monitors. Error handling paths are also tested.
Tests use a shared engine for common patterns.
Acked-by: Nam Cao <namcao@linutronix.de>
Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
---
Notes:
V4:
* Retry multiple times when getting the pid to avoid races
tools/verification/rv/Makefile | 5 +-
tools/verification/rv/tests/rv_list.t | 48 +++++++++
tools/verification/rv/tests/rv_mon.t | 95 +++++++++++++++++
tools/verification/tests/engine.sh | 141 ++++++++++++++++++++++++++
4 files changed, 288 insertions(+), 1 deletion(-)
create mode 100644 tools/verification/rv/tests/rv_list.t
create mode 100644 tools/verification/rv/tests/rv_mon.t
create mode 100644 tools/verification/tests/engine.sh
diff --git a/tools/verification/rv/Makefile b/tools/verification/rv/Makefile
index 5b898360ba48..8ae5fc0d1d17 100644
--- a/tools/verification/rv/Makefile
+++ b/tools/verification/rv/Makefile
@@ -78,4 +78,7 @@ clean: doc_clean fixdep-clean
$(Q)rm -f rv rv-static fixdep FEATURE-DUMP rv-*
$(Q)rm -rf feature
-.PHONY: FORCE clean
+check: $(RV)
+ RV=$(RV) prove -o --directives -f tests/
+
+.PHONY: FORCE clean check
diff --git a/tools/verification/rv/tests/rv_list.t b/tools/verification/rv/tests/rv_list.t
new file mode 100644
index 000000000000..201af33a52cc
--- /dev/null
+++ b/tools/verification/rv/tests/rv_list.t
@@ -0,0 +1,48 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source ../tests/engine.sh
+test_begin
+
+set_timeout 30s
+
+RVDIR=/sys/kernel/tracing/rv/
+
+# Help and basic tests
+check "verify help page" \
+ "$RV --help" 0 "usage: rv command"
+
+check "verify list subcommand help" \
+ "$RV list --help" 0 "list all available monitors"
+
+all_nested=$(grep : $RVDIR/available_monitors | cut -d: -f2 | paste -s | sed 's/\t/\\|/g')
+all_non_nested=$(grep -v : $RVDIR/available_monitors | cut -d: -f2 | paste -s | sed 's/\t/\\|/g')
+sched_monitors=$(grep sched: $RVDIR/available_monitors | cut -d: -f2 | paste -s | sed 's/\t/\\|/g')
+description_state="[[:space:]]\+[[:print:]]\+\[\(OFF\|ON\)\]"
+line_nested=" - \($all_nested\)${description_state}"
+line_non_nested="\($all_non_nested\)${description_state}"
+
+# List monitors and containers
+check "list all monitors" \
+ "$RV list" 0 "" "" "^\($line_nested\|$line_non_nested\)$"
+
+check_if_exists "list container" \
+ "$RV list sched" "$RVDIR/monitors/sched" \
+ "" "-- No monitor found in container sched --" \
+ "^\($sched_monitors\)${description_state}$"
+
+check_if_exists "list non-container" \
+ "$RV list wwnr" "$RVDIR/monitors/wwnr" \
+ "-- No monitor found in container wwnr --" \
+ "^\( - \)\?[[:alnum:]]\+${description_state}$"
+
+check "list incomplete container name" \
+ "$RV list s" 0 "-- No monitor found in container s --"
+
+# Error handling tests
+check "no command" \
+ "$RV" 1 "rv requires a command"
+
+check "invalid command" \
+ "$RV invalid" 1 "rv does not know the invalid command"
+
+test_end
diff --git a/tools/verification/rv/tests/rv_mon.t b/tools/verification/rv/tests/rv_mon.t
new file mode 100644
index 000000000000..cbc346c74c71
--- /dev/null
+++ b/tools/verification/rv/tests/rv_mon.t
@@ -0,0 +1,95 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+source ../tests/engine.sh
+test_begin
+
+set_timeout 30s
+
+RVDIR=/sys/kernel/tracing/rv/
+
+# Help and basic tests
+check "verify mon subcommand help" \
+ "$RV mon --help" 0 "run a monitor"
+
+# Error handling tests
+check "mon without monitor name" \
+ "$RV mon" 1 "usage: rv mon"
+
+check "invalid monitor name" \
+ "$RV mon invalid" 1 "monitor invalid does not exist"
+
+if [ -d $RVDIR/monitors/wwnr ]; then
+
+check "invalid reactor name" \
+ "$RV mon wwnr -r invalid" 1 "failed to set invalid reactor, is it available?"
+
+check "monitor name is substring of another monitor" \
+ "$RV mon nr" 1 "monitor nr does not exist"
+
+check "already enabled monitor returns error" \
+ "echo 1 > $RVDIR/monitors/wwnr/enable; $RV mon wwnr" 1 \
+ "monitor wwnr (in-kernel) is already enabled"
+echo 0 > $RVDIR/monitors/wwnr/enable
+
+fi
+
+# rv mon runs until terminated
+set_expected_timeout 2s
+
+# Run monitors with different configurations
+check_if_exists "run the monitor without parameters" \
+ "$RV mon wwnr" "$RVDIR/monitors/wwnr" "" "."
+
+check_if_exists "run the monitor as verbose" \
+ "$RV mon wwnr -v" "$RVDIR/monitors/wwnr" \
+ "my pid is \$pid" "\(event\|error\)"
+
+check_if_exists "run the monitor with a reactor" \
+ "$RV mon wwnr -r printk & sleep .5 && cat $RVDIR/monitors/wwnr/reactors && wait" \
+ "$RVDIR/monitors/wwnr/reactors" "\[printk\]"
+
+check_if_exists "reactor is restored after exit" \
+ "cat $RVDIR/monitors/wwnr/reactors" \
+ "$RVDIR/monitors/wwnr/reactors" "\[nop\]"
+
+check_if_exists "run a nested monitor with a reactor" \
+ "$RV mon snroc -r printk & sleep .5 && cat $RVDIR/monitors/sched/snroc/reactors && wait" \
+ "$RVDIR/monitors/sched/snroc/reactors" "\[printk\]"
+
+check_if_exists "run an explicitly nested monitor with a reactor" \
+ "$RV mon sched:sssw -r printk & sleep .5 && cat $RVDIR/monitors/sched/sssw/reactors && wait" \
+ "$RVDIR/monitors/sched/sssw/reactors" "\[printk\]"
+
+check_if_exists "run container monitor" \
+ "$RV mon sched & sleep .5 && cat $RVDIR/monitors/sched/{sssw,sco}/enable && wait" \
+ "$RVDIR/monitors/sched" "1" "0" "^1$"
+
+# Regexes for the trace
+header="^[[:space:]]\+\(\([][A-Z_x<>-]\+\||\)[[:space:]]*\)\+$"
+type="\(event\|error\)[[:space:]]\+"
+genpid="[0-9]\+[[:space:]]\+"
+selfpid="\$pid[[:space:]]\+"
+cpu="\[[0-9]\{3\}\][[:space:]]\+"
+state="[a-z_]\+ "
+trace_task="${genpid}${cpu}${type}${genpid}${state}"
+trace_task_self="${genpid}${cpu}${type}${selfpid}${state}"
+trace_cpu="${genpid}${cpu}${type}${state}"
+trace_cpu_self="${selfpid}${cpu}${type}${state}"
+
+check_if_exists "run per-task monitor with tracing" \
+ "$RV mon sssw -t" "$RVDIR/monitors/sched/sssw" \
+ "$header" "$trace_task_self" "\($header\|$trace_task\)"
+
+check_if_exists "run per-task monitor tracing also self" \
+ "$RV mon sched:sssw -t -s" "$RVDIR/monitors/sched/sssw" \
+ "$trace_task_self" "" "\($header\|$trace_task\)"
+
+check_if_exists "run per-cpu monitor with tracing" \
+ "$RV mon sched:sco -t" "$RVDIR/monitors/sched/sco" \
+ "$header" "$trace_cpu_self" "\($header\|$trace_cpu\)"
+
+check_if_exists "run per-cpu monitor tracing also self" \
+ "$RV mon sco -t -s" "$RVDIR/monitors/sched/sco" \
+ "$trace_cpu_self" "" "\($header\|$trace_cpu\)"
+
+test_end
diff --git a/tools/verification/tests/engine.sh b/tools/verification/tests/engine.sh
new file mode 100644
index 000000000000..57e16dc980b1
--- /dev/null
+++ b/tools/verification/tests/engine.sh
@@ -0,0 +1,141 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+test_begin() {
+ # Count tests to allow the test harness to double-check if all were
+ # included correctly.
+ ctr=0
+ [ -z "$RV" ] && RV="../rv/rv"
+ [ -n "$TEST_COUNT" ] && echo "1..$TEST_COUNT"
+}
+
+failure() {
+ fail=1
+ if [ $# -gt 0 ]; then
+ failbuf+="$1"
+ failbuf+=$'\n'
+ fi
+}
+
+report() {
+ local desc="$1"
+
+ if [ "$fail" -eq 0 ]; then
+ echo "ok $ctr - $desc"
+ else
+ # Add output and exit code as comments in case of failure
+ echo "not ok $ctr - $desc"
+ echo -n "$failbuf"
+ echo "$result" | col -b | while read -r line; do echo "# $line"; done
+ printf "#\n# exit code %s\n" "$exitcode"
+ fi
+}
+
+_check() {
+ local command=$2
+ local expected_exitcode=${3:-0}
+ local expected_output=$4
+ local unexpected_output=$5
+ local all_lines_pattern=$6
+ local patterns="$expected_output $unexpected_output $all_lines_pattern"
+ local bgpid pid
+
+ eval "$TIMEOUT" "$command" &> check_output.$$ &
+ bgpid=$!
+
+ if grep -q "\$pid" <<< "$patterns"; then
+ for _ in {1..30}; do
+ pid=$(pgrep -f "${command%%[|;&>]*}" | tail -n1)
+ [ -n "$pid" ] && break
+ sleep 0.1
+ done
+ fi
+
+ wait $bgpid
+ exitcode=$?
+ result=$(tr -d '\0' < check_output.$$)
+ rm -f check_output.$$
+
+ failbuf=''
+ fail=0
+
+ # Suppress any other error if a needed pid is empty
+ if [ -z "$pid" ] && grep -q "\$pid" <<< "$patterns"; then
+ result=''
+ failure "# Empty pid for $command"
+ return 1
+ fi
+
+ expected_output="${expected_output//\$pid/$pid}"
+ unexpected_output="${unexpected_output//\$pid/$pid}"
+ all_lines_pattern="${all_lines_pattern//\$pid/$pid}"
+
+ # Test if the results matches if requested
+ if [ -n "$expected_output" ] && ! grep -qe "$expected_output" <<< "$result"; then
+ failure "# Output match failed: \"$expected_output\""
+ fi
+
+ if [ -n "$unexpected_output" ] && grep -qe "$unexpected_output" <<< "$result"; then
+ failure "# Output non-match failed: \"$unexpected_output\""
+ fi
+
+ if [ -n "$all_lines_pattern" ] && grep -vqe "$all_lines_pattern" <<< "$result"; then
+ failure "# All-lines pattern failed: \"$all_lines_pattern\""
+ fi
+
+ if [ $exitcode -ne "$expected_exitcode" ]; then
+ failure "# Expected exit code $expected_exitcode"
+ fi
+}
+
+check() {
+ # Simple check: run the command with given arguments and test exit code.
+ # If TEST_COUNT is set, run the test. Otherwise, just count.
+ ctr=$((ctr + 1))
+ if [ -n "$TEST_COUNT" ]; then
+ _check "$@"
+ report "$1"
+ fi
+}
+
+check_if_exists() {
+ # Conditional check that skips if a file or folder doesn't exist
+ local desc=$1
+ local command=$2
+ local file=$3
+ local expected_output=$4
+ local unexpected_output=$5
+ local all_lines_pattern=$6
+
+ ctr=$((ctr + 1))
+ if [ -n "$TEST_COUNT" ]; then
+ if [ ! -e "$file" ]; then
+ echo "ok $ctr - $desc # SKIP file not found: $file"
+ else
+ _check "$desc" "$command" 0 "$expected_output" \
+ "$unexpected_output" "$all_lines_pattern"
+ report "$desc"
+ fi
+ fi
+}
+
+set_timeout() {
+ TIMEOUT="timeout -v -k 30s $1"
+}
+
+set_expected_timeout() {
+ TIMEOUT="timeout --preserve-status -k 30s $1"
+}
+
+unset_timeout() {
+ unset TIMEOUT
+}
+
+test_end() {
+ # If running without TEST_COUNT, tests are not actually run, just
+ # counted. In that case, re-run the test with the correct count.
+ [ -z "$TEST_COUNT" ] && TEST_COUNT=$ctr exec bash "$0" || true
+}
+
+# Avoid any environmental discrepancies
+export LC_ALL=C
+unset_timeout
--
2.55.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox