Linux Trace Kernel
 help / color / mirror / Atom feed
From: Gabriele Monaco <gmonaco@redhat.com>
To: Tobias Schaffner <tobias.schaffner@siemens.com>,
	Steven Rostedt <rostedt@goodmis.org>
Cc: Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	 Masami Hiramatsu <mhiramat@kernel.org>,
	Mathieu Desnoyers <mathieu.desnoyers@efficios.com>,
	 linux-trace-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
	 linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org,
	rpm@xenomai.org, 	jan.kiszka@siemens.com
Subject: Re: [PATCH v2 2/3] rv: add per-monitor edge-stat facility and stats file
Date: Tue, 13 Oct 2026 13:43:28 +0200	[thread overview]
Message-ID: <0eab40ebeb128ca308762f0bf33d2f7084ef32f9.camel@redhat.com> (raw)
In-Reply-To: <20260911123439.463052-3-tobias.schaffner@siemens.com>

On Fri, 2026-09-11 at 14:34 +0200, Tobias Schaffner wrote:
> Add CONFIG_RV_EDGE_STAT, an optional feature that records how long a
> monitor's automaton dwells in a state and exposes it per edge through a
> per-monitor "stats" tracefs file.
> 
> The facility uses a static per-CPU matrix and reports, per edge and per
> CPU, the count, summed dwell time and maximum dwell time. Only the owning
> CPU writes the counters, so a reader snapshots them with local64_read()
> with no IPI and no locking on the accounting path.
> 
> Signed-off-by: Tobias Schaffner <tobias.schaffner@siemens.com>
> ---
...

> +
> +/*
> + * Static storage avoids allocation lifetime races. Only the owning CPU
> writes,
> + * so readers can snapshot counters with local64_read().
> + */
> +#define RV_THIS_NR_EDGES	(STATE_MAX * EVENT_MAX)
> +
> +struct rv_this_edges {
> +	struct rv_edge_stat	edge[RV_THIS_NR_EDGES];
> +};

Does this really need to be a new struct? Cannot you just define it as:

DEFINE_PER_CPU(struct rv_edge_stat[RV_THIS_NR_EDGES], DA_MON_EDGES);

Then use it as:

per_cpu_ptr(DA_MON_EDGES, cpu), sizeof(DA_MON_EDGES)

struct rv_edge_stat *edges = per_cpu_ptr(DA_MON_EDGES, cpu);
&edges[e].count

> +
> +static DEFINE_PER_CPU(struct rv_this_edges, DA_MON_EDGES);
> +static struct dentry *rv_this_stats_file;
> +
> +/* Called before tracepoints are registered, so memset cannot race an update.
> */
> +static void rv_edge_stats_reset(void)
> +{
> +	int cpu;
> +
> +	for_each_possible_cpu(cpu)
> +		memset(per_cpu_ptr(&DA_MON_EDGES, cpu), 0,
> +		       sizeof(struct rv_this_edges));
> +}
> +
> +static int rv_edge_stats_show(struct seq_file *seq, void *v)
> +{
> +	unsigned int e;
> +	int cpu;
> +
> +	seq_puts(seq, "# cpu edge label count sum_ns max_ns\n");
> +	/* Include offline CPUs and keep output stable across CPU hotplug. */
> +	for_each_possible_cpu(cpu) {
> +		struct rv_this_edges *m = per_cpu_ptr(&DA_MON_EDGES, cpu);
> +
> +		for (e = 0; e < RV_THIS_NR_EDGES; e++)
> +			seq_printf(seq, "%d %u %s:%s %llu %llu %llu\n",
> +				   cpu, e,
> +				   model_get_state_name(e / EVENT_MAX),
> +				   model_get_event_name(e % EVENT_MAX),

Perhaps you could have macros like DA_EDGE(s, e) or DA_EDGE_TO_EVENT(e)
DA_EDGE_TO_STATE(s) to make the matrix selection a bit more readable,
but that's a matter of taste.

> +				   (u64)local64_read(&m->edge[e].count),
> +				   (u64)local64_read(&m->edge[e].sum_ns),
> +				   (u64)local64_read(&m->edge[e].max_ns));
> +	}

I would like to see a way to tell if an edge is invalid or not, we
expect zero on invalid edges.
Perhaps that can be a new column so you also show the count in case they
are not 0.

You can just use model_get_next_state() == INVALID_STATE

> +	return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(rv_edge_stats);
> +
> +static int rv_edge_stats_create(struct dentry *parent)
> +{
> +	rv_edge_stats_reset();
> +	/* RV_MODE_READ is private to kernel/trace/rv/. */

I suggested you to use the rv helpers for this, you can obviously also
use RV_MODE_READ. Now monitors are in kernel/trace/rv/, so it won't be
an issue.

If we add support for modular monitors, moving the definitions to
linux/rv.h is the easiest thing to do anyway (you can do it too if you
really want and/or your agents complain, in a separate commit).

> +	rv_this_stats_file = tracefs_create_file("stats", 0440, parent,
> +						 NULL, &rv_edge_stats_fops);
> +	return rv_this_stats_file ? 0 : -ENOMEM;
> +}
> +
> +static void rv_edge_stats_remove(void)
> +{
> +	tracefs_remove(rv_this_stats_file);
> +	rv_this_stats_file = NULL;
> +}
> +
> +/* A zero timestamp marks resets, which can run on a different CPU. */
> +#define da_state_entered(s)	da_state_pack((s), local_clock() &
> RV_TS_MASK)
> +
> +static __always_inline void
> +rv_edge_account(da_state_t old, da_state_t new, enum states curr, enum events
> ev)
> +{
> +	struct rv_this_edges *m;
> +	u64 prev = da_ts_of(old);
> +
> +	if (!prev)
> +		return;

This happens only the first time after reset, isn't it unlikely()?

> +
> +	m = this_cpu_ptr(&DA_MON_EDGES);
> +	rv_edge_stat_account(&m->edge[curr * EVENT_MAX + ev],
> +			     (da_ts_of(new) - prev) & RV_TS_MASK);

If you decide to go for the macro, this would be DA_EDGE(s, e) or whatever
looks clear to you.

> +}
> +
> +#else /* per-cpu accounting off for this monitor type */
> +
> +#define da_state_entered(s)	((da_state_t)(s))
> +
> +static __always_inline void
> +rv_edge_account(da_state_t old, da_state_t new, enum states curr, enum events
> ev) { }
> +
> +#endif /* RV_MON_TYPE == RV_MON_PER_CPU */
> +
> +static __always_inline bool
> +da_state_try_commit(da_state_t *word, da_state_t *old, enum states next,
> +		    enum events event)
> +{
> +	da_state_t prev = *old;
> +	da_state_t new = da_state_entered(next);
> +
> +	if (!try_cmpxchg(word, old, new))
> +		return false;
> +
> +	rv_edge_account(prev, new, da_state_of(prev), event);
> +	return true;
> +}
> +
> +#else /* !CONFIG_RV_EDGE_STAT */
> +
> +/*
> + * Feature off: only the hooks the DA/HA layer calls are provided, as trivial
> + * pass-throughs. da_state_t is a plain state word with no packed timestamp.
> + */
> +#define da_state_of(w)		((unsigned int)(w))

Is this cast necessary? We assign it to an enum, the sign isn't really
important. I guess it's the same for the other implementation of
da_state_of() (which is now in another patch).

Thanks,
Gabriele


> +#define da_state_entered(s)	(s)
> +
> +static __always_inline bool
> +da_state_try_commit(da_state_t *word, da_state_t *old, enum states next,
> +		    enum events event)
> +{
> +	return try_cmpxchg(word, old, next);
> +}
> +
> +#if RV_MON_TYPE == RV_MON_PER_CPU
> +static inline int rv_edge_stats_create(struct dentry *parent) { return 0; }
> +static inline void rv_edge_stats_remove(void) { }
> +#endif
> +
>  #endif /* CONFIG_RV_EDGE_STAT */
>  
>  #endif /* _RV_EDGE_STAT_H */
> diff --git a/kernel/trace/rv/Kconfig b/kernel/trace/rv/Kconfig
> index 3884b14df375..3045037fb36a 100644
> --- a/kernel/trace/rv/Kconfig
> +++ b/kernel/trace/rv/Kconfig
> @@ -59,6 +59,18 @@ config RV_PER_TASK_MONITORS
>  	  This option configures the maximum number of per-task RV monitors
> that can run
>  	  simultaneously.
>  
> +config RV_EDGE_STAT
> +	bool "Per-edge dwell-time statistics"
> +	depends on RV && 64BIT && DA_MON_EVENTS_IMPLICIT
> +	help
> +	  Record per-edge dwell-time statistics for per-cpu deterministic and
> +	  hybrid automaton monitors and expose them through a per-monitor
> +	  "stats" tracefs file. This times each monitored automaton
> transition
> +	  with local_clock(), so leave it off if you do not need the
> +	  statistics.
> +
> +	  If unsure, say N.
> +
>  source "kernel/trace/rv/monitors/wip/Kconfig"
>  source "kernel/trace/rv/monitors/wwnr/Kconfig"
> 


  parent reply	other threads:[~2026-10-13 11:43 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-11 12:34 [PATCH v2 0/3] rv: per-edge dwell-time statistics for per-cpu monitors Tobias Schaffner
2026-09-11 12:34 ` [PATCH v2 1/3] rv: add per-edge dwell-time statistics primitive Tobias Schaffner
2026-09-24  7:21   ` Gabriele Monaco
2026-09-11 12:34 ` [PATCH v2 2/3] rv: add per-monitor edge-stat facility and stats file Tobias Schaffner
2026-09-24  7:47   ` Gabriele Monaco
2026-10-13 11:43   ` Gabriele Monaco [this message]
2026-09-11 12:34 ` [PATCH v2 3/3] rv: collect per-edge dwell time for per-cpu DA/HA monitors Tobias Schaffner
2026-09-24  7:18   ` Gabriele Monaco
2026-10-12 11:47 ` [PATCH v2 0/3] rv: per-edge dwell-time statistics for per-cpu monitors Gabriele Monaco
2026-10-12 16:18   ` Tobias Schaffner
2026-10-12 19:21     ` Steven Rostedt
2026-10-13  7:53       ` Gabriele Monaco
2026-10-13  7:26     ` Gabriele Monaco

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0eab40ebeb128ca308762f0bf33d2f7084ef32f9.camel@redhat.com \
    --to=gmonaco@redhat.com \
    --cc=corbet@lwn.net \
    --cc=jan.kiszka@siemens.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=mhiramat@kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=rpm@xenomai.org \
    --cc=skhan@linuxfoundation.org \
    --cc=tobias.schaffner@siemens.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox