From: Gabriele Monaco <gmonaco@redhat.com>
To: wen.yang@linux.dev
Cc: linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org,
Steven Rostedt <rostedt@goodmis.org>
Subject: Re: [RFC PATCH v2 06/10] rvgen: support reset() on the __init arrow for global-window HA clocks
Date: Tue, 12 May 2026 15:25:41 +0200 [thread overview]
Message-ID: <63b8f141689e2ba34adaed43ca78a8cd883bfa5f.camel@redhat.com> (raw)
In-Reply-To: <aa156a1c7696e054f8db57c48a26fa6ec1e17395.1778522945.git.wen.yang@linux.dev>
On Tue, 2026-05-12 at 02:24 +0800, wen.yang@linux.dev wrote:
> From: Wen Yang <wen.yang@linux.dev>
>
> rvgen rejects a state invariant when its env is never reset on any
> state-transition edge. This prevents expressing monitors where a clock
> tracks the full monitoring window — reset once at object creation,
> active in all states.
>
> Allow reset() annotations on the __init_STATE -> STATE arrow.
> automata.py adds listed envs to the new env_init_started set (and to
> env_stored so the HA framework allocates per-object storage). dot2k.py
> uses env_init_started for three purposes:
>
> - Generate a handle_monitor_start() skeleton that resets the env and
> arms the timer after the caller sets up DA storage and initial state.
>
> - Guard ha_inv_to_guard calls with !ha_monitor_env_invalid() for these
> envs: a concurrent DA event between da_handle_start_event() and
> ha_reset_env() would otherwise store U64_MAX - BUDGET as the guard
> anchor, silently disabling enforcement.
>
> - Always generate ha_verify_guards() for monitors with invariants,
> providing a stable extension point for future per-event guards.
>
> Models without __init resets (e.g. stall.dot) are unaffected.
>
> Signed-off-by: Wen Yang <wen.yang@linux.dev>
Mmh, I see that's an issue, but technically the init arrow isn't a real state
transition. In your case, you have a start condition that you labelled
"switch_in_tlob" although it isn't a switch in.
Why don't you make it a separate event (e.g. "start_tlob"), it seems to me you
cannot really have that occur multiple times, so doing the following wouldn't
harm and you wouldn't need to change how HA monitors work:
|
|
v
+-----------------------------+ start;reset(clk)
| running | -------------------+
switch_in | clk < BUDGET_NS() | |
+---------> | | <------------------+
| +-----------------------------+
| | |
| | sleep |
| v |
| |
| sleeping |
| clk < BUDGET_NS() |
| | preempt
| | |
| | wakeup |
| v |
| |
| waiting |
+---------- clk < BUDGET_NS() <+
Then you also wouldn't need to call reset() and start_timer() manually.
Isn't that feasible?
Thanks,
Gabriele
> ---
> tools/verification/rvgen/rvgen/automata.py | 26 ++++++
> tools/verification/rvgen/rvgen/dot2k.py | 100 +++++++++++++++++++--
> 2 files changed, 119 insertions(+), 7 deletions(-)
>
> diff --git a/tools/verification/rvgen/rvgen/automata.py
> b/tools/verification/rvgen/rvgen/automata.py
> index b9f8149f7118..178a1a4ffd8a 100644
> --- a/tools/verification/rvgen/rvgen/automata.py
> +++ b/tools/verification/rvgen/rvgen/automata.py
> @@ -69,15 +69,41 @@ class Automata:
> self.states, self.initial_state, self.final_states =
> self.__get_state_variables()
> self.env_types = {}
> self.env_stored = set()
> + self.env_init_started = set()
> self.constraint_vars = set()
> self.self_loop_reset_events = set()
> self.events, self.envs = self.__get_event_variables()
> + self.__parse_init_resets()
> self.function, self.constraints = self.__create_matrix()
> self.events_start, self.events_start_run = self.__store_init_events()
> self.env_stored = sorted(self.env_stored)
> + self.env_init_started = sorted(self.env_init_started)
> self.constraint_vars = sorted(self.constraint_vars)
> self.self_loop_reset_events = sorted(self.self_loop_reset_events)
>
> + def __parse_init_resets(self) -> None:
> + """Parse reset() annotations on the __init_STATE -> STATE arrow.
> +
> + Adds each listed env to env_stored (HA framework allocates per-object
> + storage) and env_init_started (ha2k generates
> handle_monitor_start()).
> + """
> + init_prefix = f'"{self.init_marker}'
> + for line in map(str.lstrip, self.__dot_lines):
> + if not line.startswith(init_prefix):
> + continue
> + split_line = line.split()
> + if len(split_line) < 3 or split_line[1] != "->":
> + continue
> + if "label" not in line:
> + continue
> + label = "".join(split_line[split_line.index("label") + 2:-
> 1]).replace('"', '')
> + for part in label.split(";"):
> + reset_m = self.constraint_reset.search(part.strip())
> + if reset_m:
> + env = reset_m["env"]
> + self.env_stored.add(env)
> + self.env_init_started.add(env)
> +
> def __get_model_name(self) -> str:
> basename = ntpath.basename(self.__dot_path)
> if not basename.endswith(".dot") and not basename.endswith(".gv"):
> diff --git a/tools/verification/rvgen/rvgen/dot2k.py
> b/tools/verification/rvgen/rvgen/dot2k.py
> index e6f476b903b0..e8066260c0af 100644
> --- a/tools/verification/rvgen/rvgen/dot2k.py
> +++ b/tools/verification/rvgen/rvgen/dot2k.py
> @@ -366,7 +366,18 @@ f"""static inline void ha_convert_inv_guard(struct
> ha_monitor *ha_mon,
> conf_g = [e for s, e in conflict_guards if s == state]
> if not conf_i and not conf_g:
> continue
> - buff.append(f"\t{_else}if (curr_state ==
> {self.states[state]}{self.enum_suffix})")
> +
> + state_name = f"{self.states[state]}{self.enum_suffix}"
> + env_full = self.__get_constraint_env(constr)
> + env_bare = env_full[:-len(self.enum_suffix)]
> + if env_bare in self.env_init_started:
> + # env_store is ENV_INVALID_VALUE until
> handle_monitor_start();
> + # skip ha_inv_to_guard during the init race window.
> + cont = "\t\t " if _else else "\t "
> + buff.append(f"\t{_else}if (curr_state == {state_name} &&")
> + buff.append(f"{cont}!ha_monitor_env_invalid(ha_mon,
> {env_full}))")
> + else:
> + buff.append(f"\t{_else}if (curr_state == {state_name})")
>
> buff.append(f"\t\t{self.__start_to_conv(constr)};")
> _else = "else "
> @@ -376,16 +387,22 @@ f"""static inline void ha_convert_inv_guard(struct
> ha_monitor *ha_mon,
>
> def __fill_verify_guards_func(self) -> list[str]:
> buff = []
> - if not self.guards:
> + # Always generate for monitors with invariants: stable extension
> + # point for future guard conditions.
> + if not self.guards and not self.invariants:
> return []
>
> buff.append(
> f"""static inline bool ha_verify_guards(struct ha_monitor *ha_mon,
> \t\t\t\t enum {self.enum_states_def} curr_state, enum
> {self.enum_events_def} event,
> \t\t\t\t enum {self.enum_states_def} next_state, u64 time_ns)
> -{{
> -\tbool res = true;
> -""")
> +{{""")
> +
> + if not self.guards:
> + buff.append("\treturn true;\n}\n")
> + return buff
> +
> + buff.append("\tbool res = true;\n")
>
> _else = ""
> for edge, constr in sorted(self.guards.items()):
> @@ -522,7 +539,7 @@ f"""static bool ha_verify_constraint(struct ha_monitor
> *ha_mon,
> buff.append("\tha_convert_inv_guard(ha_mon, curr_state, event, "
> "next_state, time_ns);\n")
>
> - if self.guards:
> + if self.guards or self.invariants:
> buff.append("\tif (!ha_verify_guards(ha_mon, curr_state, event, "
> "next_state, time_ns))\n\t\treturn false;\n")
>
> @@ -599,8 +616,77 @@ f"""static bool ha_verify_constraint(struct ha_monitor
> *ha_mon,
> buff.append("}\n")
> return buff
>
> + def __fill_init_start_helper(self) -> list[str]:
> + """Generate handle_monitor_start() for envs reset on the __init
> arrow.
> +
> + env_store is invalid inside da_handle_start_event(); this helper must
> + be called after DA storage is allocated and initial state is set.
> + """
> + if not self.env_init_started:
> + return []
> +
> + # Collect the ha_start_timer call for each init-started env from the
> + # first state invariant that references it.
> + timer_calls: dict[str, str] = {}
> + for env in self.env_init_started:
> + env_full = f"{env}{self.enum_suffix}"
> + for constr in self.invariants.values():
> + if env_full in constr:
> + timer_calls[env] = constr
> + break
> +
> + buff = []
> + buff.append(
> +"""/*
> + * handle_monitor_start - reset per-object clock(s) and arm the timer.
> + *
> + * env_store is invalid inside da_handle_start_event(); call this helper
> + * after allocating DA storage and setting the initial DA state.
> + *
> + * XXX: replace the placeholders with the actual logic for your monitor.
> + */""")
> +
> + if self.monitor_type == "per_obj":
> + buff.append("static int handle_monitor_start(int id,
> monitor_target t)")
> + buff.append("{")
> + buff.append("\tstruct ha_monitor *ha_mon;")
> + buff.append("\tu64 time_ns = ktime_get_ns();\n")
> + buff.append("\t/* XXX: allocate DA storage, e.g.
> da_create_or_get(id, t) */")
> + buff.append("\t/* XXX: set initial DA state, e.g.
> da_handle_start_event(id, t, <event>) */")
> + buff.append("\tha_mon = /* XXX: retrieve ha_monitor for (id, t)
> */;")
> + elif self.monitor_type == "per_task":
> + buff.append("static int handle_monitor_start(struct task_struct
> *p)")
> + buff.append("{")
> + buff.append("\tstruct ha_monitor *ha_mon;")
> + buff.append("\tu64 time_ns = ktime_get_ns();\n")
> + buff.append("\t/* XXX: allocate DA storage, e.g.
> da_create_or_get(p->pid, p) */")
> + buff.append("\t/* XXX: set initial DA state, e.g.
> da_handle_start_event(p->pid, p, <event>) */")
> + buff.append("\tha_mon = /* XXX: retrieve ha_monitor for p */;")
> + else:
> + buff.append("static int handle_monitor_start(void)")
> + buff.append("{")
> + buff.append("\tstruct ha_monitor *ha_mon;")
> + buff.append("\tu64 time_ns = ktime_get_ns();\n")
> + buff.append("\tha_mon = /* XXX: retrieve global ha_monitor */;")
> +
> + buff.append("\tif (!ha_mon)")
> + buff.append("\t\treturn -ENOENT;")
> +
> + for env in self.env_init_started:
> + buff.append(f"\tha_reset_env(ha_mon, {env}{self.enum_suffix},
> time_ns);")
> + if env in timer_calls:
> + buff.append(f"\t{timer_calls[env]};")
> + else:
> + buff.append(f"\t/* XXX: arm timer for {env} */")
> +
> + buff.append("\treturn 0;")
> + buff.append("}\n")
> + return buff
> +
> def _fill_hybrid_definitions(self) -> list[str]:
> - return self.__fill_hybrid_get_reset_functions() +
> self.__fill_constr_func()
> + return (self.__fill_hybrid_get_reset_functions() +
> + self.__fill_init_start_helper() +
> + self.__fill_constr_func())
>
> def _fill_timer_type(self) -> list:
> if self.invariants:
next prev parent reply other threads:[~2026-05-12 13:25 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-11 18:24 [RFC PATCH v2 00/10] rv/tlob: Add task latency over budget RV monitor wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 01/10] rv/da: fix monitor start ordering and memory ordering for monitoring flag wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 02/10] rv/da: fix per-task da_monitor_destroy() ordering and sync wen.yang
2026-05-12 8:27 ` Gabriele Monaco
2026-05-12 9:09 ` Gabriele Monaco
2026-05-11 18:24 ` [RFC PATCH v2 03/10] selftests/verification: fix verificationtest-ktap for out-of-tree execution wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 04/10] rv/da: add pre-allocated storage pool for per-object monitors wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 05/10] rv: add generic uprobe infrastructure for RV monitors wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 06/10] rvgen: support reset() on the __init arrow for global-window HA clocks wen.yang
2026-05-12 13:25 ` Gabriele Monaco [this message]
2026-05-11 18:24 ` [RFC PATCH v2 07/10] rv/tlob: add tlob model DOT file wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 08/10] rv/tlob: add tlob hybrid automaton monitor wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 09/10] rv/tlob: add KUnit tests for the tlob monitor wen.yang
2026-05-11 18:24 ` [RFC PATCH v2 10/10] selftests/verification: add tlob selftests 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=63b8f141689e2ba34adaed43ca78a8cd883bfa5f.camel@redhat.com \
--to=gmonaco@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-trace-kernel@vger.kernel.org \
--cc=rostedt@goodmis.org \
--cc=wen.yang@linux.dev \
/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