From: Wen Yang <wen.yang@linux.dev>
To: Gabriele Monaco <gmonaco@redhat.com>,
linux-kernel@vger.kernel.org,
Steven Rostedt <rostedt@goodmis.org>,
Nam Cao <namcao@linutronix.de>,
linux-trace-kernel@vger.kernel.org
Subject: Re: [PATCH 6/9] rv: Ensure synchronous cleanup for HA monitors
Date: Sun, 17 May 2026 17:12:56 +0800 [thread overview]
Message-ID: <b7d760dc-162a-48e8-af75-e566bfa46493@linux.dev> (raw)
In-Reply-To: <20260512140250.262190-7-gmonaco@redhat.com>
The guard(rcu)() + synchronize_rcu() mechanism for HA timer callbacks
is correct.
One concern: TOCTOU between the pre-check and guard(rcu)().
da_monitor_reset() calls reset_hook BEFORE clearing monitoring:
da_monitor_reset_hook(da_mon); /* ha_cancel_timer [async] */
WRITE_ONCE(da_mon->monitoring, 0); /* cleared AFTER reset_hook */
da_mon->curr_state = model_get_initial_state();
This may creates a window where the callback pre-check passes but the
monitor is reset before guard(rcu)() is acquired:
/* __ha_monitor_timer_callback() */
if (unlikely(!da_monitor_handling_event(&ha_mon->da_mon)))
return;
/* passes: monitoring=1
*
* WINDOW ─ CPU A runs da_monitor_reset_all() here:
* ha_cancel_timer() [returns: callback is running, cannot cancel]
* WRITE_ONCE(monitoring, 0)
* curr_state = model_get_initial_state()
*/
guard(rcu)();
curr_state = READ_ONCE(ha_mon->da_mon.curr_state); /* initial_state */
/* no second da_monitoring() check */
ha_react(curr_state, EVENT_NONE, env_string.buffer); /* spurious call */
ha_trace_error_env(ha_mon, ...); /* fires
unconditionally */
Result: spurious ha_trace_error_env() for initial_state. For existing
monitors (stall/nomiss/opid), model_should_send_event_env(initial, NONE)
returns false, so no false-positive reaction, but the trace event fires.
Monitors where initial_state carries a constraint would produce a false
positive.
Proposed fix : re-check inside the RCU critical section:
guard(rcu)();
if (unlikely(!da_monitoring(&ha_mon->da_mon))) /* re-check here */
return;
curr_state = READ_ONCE(ha_mon->da_mon.curr_state);
--
Best wishes,
Wen
On 5/12/26 22:02, Gabriele Monaco wrote:
> HA monitors may start timers, all cleanup functions currently stop the
> timers asynchronously to avoid sleeping in the wrong context.
> Nothing makes sure running callbacks terminate on cleanup.
>
> Run the entire HA timer callback in an RCU read-side critical section,
> this way we can simply synchronize_rcu() with any pending timer and are
> sure any cleanup using kfree_rcu() runs after callbacks terminated.
> Additionally make sure any unlikely callback running late won't run any
> code if the monitor is marked as disabled.
>
> Fixes: f5587d1b6ec9 ("rv: Add Hybrid Automata monitor type")
> Fixes: 4a24127bd6cb ("rv: Add support for per-object monitors in DA/HA")
> Signed-off-by: Gabriele Monaco <gmonaco@redhat.com>
> ---
> include/rv/da_monitor.h | 23 +++++++++++++++++++----
> include/rv/ha_monitor.h | 18 ++++++++++++++++--
> 2 files changed, 35 insertions(+), 6 deletions(-)
>
> diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h
> index a4a13b62d1a4..402d3b935c08 100644
> --- a/include/rv/da_monitor.h
> +++ b/include/rv/da_monitor.h
> @@ -57,6 +57,15 @@ static struct rv_monitor rv_this;
> #define da_monitor_reset_hook(da_mon)
> #endif
>
> +/*
> + * Hook to allow the implementation of hybrid automata: define it with a
> + * function that waits for the termination of all monitors background
> + * activities (e.g. all timers). This hook can sleep.
> + */
> +#ifndef da_monitor_sync_hook
> +#define da_monitor_sync_hook()
> +#endif
> +
> /*
> * Type for the target id, default to int but can be overridden.
> * A long type can work as hash table key (PER_OBJ) but will be downgraded to
> @@ -179,6 +188,7 @@ static inline int da_monitor_init(void)
> static inline void da_monitor_destroy(void)
> {
> da_monitor_reset_all();
> + da_monitor_sync_hook();
> }
>
> #ifndef da_implicit_guard
> @@ -232,6 +242,7 @@ static inline int da_monitor_init(void)
> static inline void da_monitor_destroy(void)
> {
> da_monitor_reset_all();
> + da_monitor_sync_hook();
> }
>
> #ifndef da_implicit_guard
> @@ -319,6 +330,7 @@ static inline void da_monitor_destroy(void)
> }
>
> da_monitor_reset_all();
> + da_monitor_sync_hook();
>
> rv_put_task_monitor_slot(task_mon_slot);
> task_mon_slot = RV_PER_TASK_MONITOR_INIT;
> @@ -497,10 +509,9 @@ static void da_monitor_reset_all(void)
> struct da_monitor_storage *mon_storage;
> int bkt;
>
> - rcu_read_lock();
> + guard(rcu)();
> hash_for_each_rcu(da_monitor_ht, bkt, mon_storage, node)
> da_monitor_reset(&mon_storage->rv.da_mon);
> - rcu_read_unlock();
> }
>
> static inline int da_monitor_init(void)
> @@ -516,13 +527,17 @@ static inline void da_monitor_destroy(void)
> int bkt;
>
> tracepoint_synchronize_unregister();
> + scoped_guard(rcu) {
> + hash_for_each_rcu(da_monitor_ht, bkt, mon_storage, node) {
> + da_monitor_reset_hook(&mon_storage->rv.da_mon);
> + }
> + }
> + da_monitor_sync_hook();
> /*
> * This function is called after all probes are disabled and no longer
> * pending, we can safely assume no concurrent user.
> */
> - synchronize_rcu();
> hash_for_each_safe(da_monitor_ht, bkt, tmp, mon_storage, node) {
> - da_monitor_reset_hook(&mon_storage->rv.da_mon);
> hash_del_rcu(&mon_storage->node);
> kfree(mon_storage);
> }
> diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h
> index d59507e8cb30..47ff1a41febe 100644
> --- a/include/rv/ha_monitor.h
> +++ b/include/rv/ha_monitor.h
> @@ -36,6 +36,7 @@ static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
> #define da_monitor_event_hook ha_monitor_handle_constraint
> #define da_monitor_init_hook ha_monitor_init_env
> #define da_monitor_reset_hook ha_monitor_reset_env
> +#define da_monitor_sync_hook() synchronize_rcu()
>
> #include <rv/da_monitor.h>
> #include <linux/seq_buf.h>
> @@ -237,12 +238,25 @@ static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
> return false;
> }
>
> +/*
> + * __ha_monitor_timer_callback - generic callback representation
> + *
> + * This callback runs in an RCU read-side critical section to allow the
> + * destruction sequence to easily synchronize_rcu() with all pending timer
> + * after asynchronously disabling them.
> + */
> static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon)
> {
> - enum states curr_state = READ_ONCE(ha_mon->da_mon.curr_state);
> DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
> - u64 time_ns = ha_get_ns();
> + enum states curr_state;
> + u64 time_ns;
> +
> + if (unlikely(!da_monitor_handling_event(&ha_mon->da_mon)))
> + return;
>
> + guard(rcu)();
> + curr_state = READ_ONCE(ha_mon->da_mon.curr_state);
> + time_ns = ha_get_ns();
> ha_get_env_string(&env_string, ha_mon, time_ns);
> ha_react(curr_state, EVENT_NONE, env_string.buffer);
> ha_trace_error_env(ha_mon, model_get_state_name(curr_state),
next prev parent reply other threads:[~2026-05-17 9:13 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-12 14:02 [PATCH 0/9] rv: Fixes on Deterministic and Hybrid Automata Gabriele Monaco
2026-05-12 14:02 ` [PATCH 1/9] rv: Fix __user specifier usage in extract_params() Gabriele Monaco
2026-05-17 8:48 ` Wen Yang
2026-05-12 14:02 ` [PATCH 2/9] rv: Fix read_lock scope in per-task DA cleanup Gabriele Monaco
2026-05-17 8:51 ` Wen Yang
2026-05-12 14:02 ` [PATCH 3/9] rv: Reset per-task DA monitors before releasing the slot Gabriele Monaco
2026-05-17 8:55 ` Wen Yang
2026-05-12 14:02 ` [PATCH 4/9] rv: Prevent task migration while handling per-CPU events Gabriele Monaco
2026-05-17 8:57 ` Wen Yang
2026-05-12 14:02 ` [PATCH 5/9] rv: Ensure all pending probes terminate on per-obj monitor destroy Gabriele Monaco
2026-05-17 9:01 ` Wen Yang
2026-05-12 14:02 ` [PATCH 6/9] rv: Ensure synchronous cleanup for HA monitors Gabriele Monaco
2026-05-17 9:12 ` Wen Yang [this message]
2026-05-18 11:54 ` Gabriele Monaco
2026-05-19 9:31 ` Gabriele Monaco
2026-05-12 14:02 ` [PATCH 7/9] rv: Do not rely on clean monitor when initialising HA Gabriele Monaco
2026-05-17 9:15 ` Wen Yang
2026-05-12 14:02 ` [PATCH 8/9] rv: Add automatic cleanup handlers for per-task HA monitors Gabriele Monaco
2026-05-17 9:40 ` Wen Yang
2026-05-18 12:18 ` Gabriele Monaco
2026-05-12 14:02 ` [PATCH 9/9] rv: Mandate deallocation for per-obj monitors Gabriele Monaco
2026-05-17 9:52 ` Wen Yang
2026-05-18 6:36 ` Gabriele Monaco
2026-05-18 15:40 ` Wen Yang
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=b7d760dc-162a-48e8-af75-e566bfa46493@linux.dev \
--to=wen.yang@linux.dev \
--cc=gmonaco@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=namcao@linutronix.de \
--cc=rostedt@goodmis.org \
/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