xref: /linux/include/rv/ha_monitor.h (revision fdbfee9fc56e13a1307868829d438ad66ab308a4)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Copyright (C) 2025-2028 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
4  *
5  * Hybrid automata (HA) monitor functions, to be used together
6  * with automata models in C generated by the rvgen tool.
7  *
8  * This type of monitors extends the Deterministic automata (DA) class by
9  * adding a set of environment variables (e.g. clocks) that can be used to
10  * constraint the valid transitions.
11  *
12  * The rvgen tool is available at tools/verification/rvgen/
13  *
14  * For further information, see:
15  *   Documentation/trace/rv/monitor_synthesis.rst
16  */
17 
18 #ifndef _RV_HA_MONITOR_H
19 #define _RV_HA_MONITOR_H
20 
21 #include <rv/automata.h>
22 
23 #ifndef da_id_type
24 #define da_id_type int
25 #endif
26 
27 static inline void ha_monitor_init_env(struct da_monitor *da_mon);
28 static inline void ha_monitor_reset_env(struct da_monitor *da_mon);
29 static inline void ha_setup_timer(struct ha_monitor *ha_mon);
30 static inline bool ha_cancel_timer(struct ha_monitor *ha_mon);
31 static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
32 					 enum states curr_state,
33 					 enum events event,
34 					 enum states next_state,
35 					 da_id_type id);
36 #define da_monitor_event_hook ha_monitor_handle_constraint
37 #define da_monitor_init_hook ha_monitor_init_env
38 #define da_monitor_reset_hook ha_monitor_reset_env
39 
40 #include <rv/da_monitor.h>
41 #include <linux/seq_buf.h>
42 
43 /* This simplifies things since da_mon and ha_mon coexist in the same union */
44 _Static_assert(offsetof(struct ha_monitor, da_mon) == 0,
45 	       "da_mon must be the first element in an ha_mon!");
46 #define to_ha_monitor(da) container_of(da, struct ha_monitor, da_mon)
47 
48 #define ENV_MAX CONCATENATE(env_max_, MONITOR_NAME)
49 #define ENV_MAX_STORED CONCATENATE(env_max_stored_, MONITOR_NAME)
50 #define envs CONCATENATE(envs_, MONITOR_NAME)
51 
52 /* Environment storage before being reset */
53 #define ENV_INVALID_VALUE U64_MAX
54 /* Error with no event occurs only on timeouts */
55 #define EVENT_NONE EVENT_MAX
56 #define EVENT_NONE_LBL "none"
57 #define ENV_BUFFER_SIZE 64
58 
59 #ifdef CONFIG_RV_REACTORS
60 
61 /*
62  * ha_react - trigger the reaction after a failed environment constraint
63  *
64  * The transition from curr_state with event is otherwise valid, but the
65  * environment constraint is false. This function can be called also with no
66  * event from a timer (state constraints only).
67  */
ha_react(enum states curr_state,enum events event,char * env)68 static void ha_react(enum states curr_state, enum events event, char *env)
69 {
70 	rv_react(&rv_this,
71 		 "rv: monitor %s does not allow event %s on state %s with env %s\n",
72 		 __stringify(MONITOR_NAME),
73 		 event == EVENT_NONE ? EVENT_NONE_LBL : model_get_event_name(event),
74 		 model_get_state_name(curr_state), env);
75 }
76 
77 #else /* CONFIG_RV_REACTOR */
78 
ha_react(enum states curr_state,enum events event,char * env)79 static void ha_react(enum states curr_state, enum events event, char *env) { }
80 #endif
81 
82 /*
83  * model_get_state_name - return the (string) name of the given state
84  */
model_get_env_name(enum envs env)85 static char *model_get_env_name(enum envs env)
86 {
87 	if ((env < 0) || (env >= ENV_MAX))
88 		return "INVALID";
89 
90 	return RV_AUTOMATON_NAME.env_names[env];
91 }
92 
93 /*
94  * Monitors requiring a timer implementation need to request it explicitly.
95  */
96 #ifndef HA_TIMER_TYPE
97 #define HA_TIMER_TYPE HA_TIMER_NONE
98 #endif
99 
100 #if HA_TIMER_TYPE == HA_TIMER_WHEEL
101 static void ha_monitor_timer_callback(struct timer_list *timer);
102 #elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
103 static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer);
104 #endif
105 
106 /*
107  * ktime_get_ns is expensive, since we usually don't require precise accounting
108  * of changes within the same event, cache the current time at the beginning of
109  * the constraint handler and use the cache for subsequent calls.
110  * Monitors without ns clocks automatically skip this.
111  */
112 #ifdef HA_CLK_NS
113 #define ha_get_ns() ktime_get_ns()
114 #else
115 #define ha_get_ns() 0
116 #endif /* HA_CLK_NS */
117 
118 /* Should be supplied by the monitor */
119 static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env, u64 time_ns);
120 static bool ha_verify_constraint(struct ha_monitor *ha_mon,
121 				 enum states curr_state,
122 				 enum events event,
123 				 enum states next_state,
124 				 u64 time_ns);
125 
126 /*
127  * ha_monitor_reset_all_stored - reset all environment variables in the monitor
128  */
ha_monitor_reset_all_stored(struct ha_monitor * ha_mon)129 static inline void ha_monitor_reset_all_stored(struct ha_monitor *ha_mon)
130 {
131 	for (int i = 0; i < ENV_MAX_STORED; i++)
132 		WRITE_ONCE(ha_mon->env_store[i], ENV_INVALID_VALUE);
133 }
134 
135 /*
136  * ha_monitor_init_env - setup timer and reset all environment
137  *
138  * Called from a hook in the DA start functions, it supplies the da_mon
139  * corresponding to the current ha_mon.
140  * Not all hybrid automata require the timer, still set it for simplicity.
141  */
ha_monitor_init_env(struct da_monitor * da_mon)142 static inline void ha_monitor_init_env(struct da_monitor *da_mon)
143 {
144 	struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
145 
146 	ha_monitor_reset_all_stored(ha_mon);
147 	ha_setup_timer(ha_mon);
148 }
149 
150 /*
151  * ha_monitor_reset_env - stop timer and reset all environment
152  *
153  * Called from a hook in the DA reset functions, it supplies the da_mon
154  * corresponding to the current ha_mon.
155  * Not all hybrid automata require the timer, still clear it for simplicity.
156  */
ha_monitor_reset_env(struct da_monitor * da_mon)157 static inline void ha_monitor_reset_env(struct da_monitor *da_mon)
158 {
159 	struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
160 
161 	/* Initialisation resets the monitor before initialising the timer */
162 	if (likely(da_monitoring(da_mon)))
163 		ha_cancel_timer(ha_mon);
164 }
165 
166 /*
167  * ha_monitor_env_invalid - return true if env has not been initialised
168  */
ha_monitor_env_invalid(struct ha_monitor * ha_mon,enum envs env)169 static inline bool ha_monitor_env_invalid(struct ha_monitor *ha_mon, enum envs env)
170 {
171 	return READ_ONCE(ha_mon->env_store[env]) == ENV_INVALID_VALUE;
172 }
173 
ha_get_env_string(struct seq_buf * s,struct ha_monitor * ha_mon,u64 time_ns)174 static inline void ha_get_env_string(struct seq_buf *s,
175 				     struct ha_monitor *ha_mon, u64 time_ns)
176 {
177 	const char *format_str = "%s=%llu";
178 
179 	for (int i = 0; i < ENV_MAX; i++) {
180 		seq_buf_printf(s, format_str, model_get_env_name(i),
181 			       ha_get_env(ha_mon, i, time_ns));
182 		format_str = ",%s=%llu";
183 	}
184 }
185 
186 #if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
ha_trace_error_env(struct ha_monitor * ha_mon,char * curr_state,char * event,char * env,da_id_type id)187 static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
188 				      char *curr_state, char *event, char *env,
189 				      da_id_type id)
190 {
191 	CONCATENATE(trace_error_env_, MONITOR_NAME)(curr_state, event, env);
192 }
193 #elif RV_MON_TYPE == RV_MON_PER_TASK || RV_MON_TYPE == RV_MON_PER_OBJ
194 
195 #define ha_get_target(ha_mon) da_get_target(&ha_mon->da_mon)
196 
ha_trace_error_env(struct ha_monitor * ha_mon,char * curr_state,char * event,char * env,da_id_type id)197 static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
198 				      char *curr_state, char *event, char *env,
199 				      da_id_type id)
200 {
201 	CONCATENATE(trace_error_env_, MONITOR_NAME)(id, curr_state, event, env);
202 }
203 #endif /* RV_MON_TYPE */
204 
205 /*
206  * ha_get_monitor - return the current monitor
207  */
208 #define ha_get_monitor(...) to_ha_monitor(da_get_monitor(__VA_ARGS__))
209 
210 /*
211  * ha_monitor_handle_constraint - handle the constraint on the current transition
212  *
213  * If the monitor implementation defines a constraint in the transition from
214  * curr_state to event, react and trace appropriately as well as return false.
215  * This function is called from the hook in the DA event handle function and
216  * triggers a failure in the monitor.
217  */
ha_monitor_handle_constraint(struct da_monitor * da_mon,enum states curr_state,enum events event,enum states next_state,da_id_type id)218 static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
219 					 enum states curr_state,
220 					 enum events event,
221 					 enum states next_state,
222 					 da_id_type id)
223 {
224 	struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
225 	u64 time_ns = ha_get_ns();
226 	DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
227 
228 	if (ha_verify_constraint(ha_mon, curr_state, event, next_state, time_ns))
229 		return true;
230 
231 	ha_get_env_string(&env_string, ha_mon, time_ns);
232 	ha_react(curr_state, event, env_string.buffer);
233 	ha_trace_error_env(ha_mon,
234 			   model_get_state_name(curr_state),
235 			   model_get_event_name(event),
236 			   env_string.buffer, id);
237 	return false;
238 }
239 
__ha_monitor_timer_callback(struct ha_monitor * ha_mon)240 static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon)
241 {
242 	enum states curr_state = READ_ONCE(ha_mon->da_mon.curr_state);
243 	DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
244 	u64 time_ns = ha_get_ns();
245 
246 	ha_get_env_string(&env_string, ha_mon, time_ns);
247 	ha_react(curr_state, EVENT_NONE, env_string.buffer);
248 	ha_trace_error_env(ha_mon, model_get_state_name(curr_state),
249 			   EVENT_NONE_LBL, env_string.buffer,
250 			   da_get_id(&ha_mon->da_mon));
251 
252 	da_monitor_reset(&ha_mon->da_mon);
253 }
254 
255 /*
256  * The clock variables have 2 different representations in the env_store:
257  * - The guard representation is the timestamp of the last reset
258  * - The invariant representation is the timestamp when the invariant expires
259  * As the representations are incompatible, care must be taken when switching
260  * between them: the invariant representation can only be used when starting a
261  * timer when the previous representation was guard (e.g. no other invariant
262  * started since the last reset operation).
263  * Likewise, switching from invariant to guard representation without a reset
264  * can be done only by subtracting the exact value used to start the invariant.
265  *
266  * Reading the environment variable (ha_get_clk) also reflects this difference
267  * any reads in states that have an invariant return the (possibly negative)
268  * time since expiration, other reads return the time since last reset.
269  */
270 
271 /*
272  * Helper functions for env variables describing clocks with ns granularity
273  */
ha_get_clk_ns(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)274 static inline u64 ha_get_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
275 {
276 	return time_ns - READ_ONCE(ha_mon->env_store[env]);
277 }
ha_reset_clk_ns(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)278 static inline void ha_reset_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
279 {
280 	WRITE_ONCE(ha_mon->env_store[env], time_ns);
281 }
ha_set_invariant_ns(struct ha_monitor * ha_mon,enum envs env,u64 value,u64 time_ns)282 static inline void ha_set_invariant_ns(struct ha_monitor *ha_mon, enum envs env,
283 				       u64 value, u64 time_ns)
284 {
285 	WRITE_ONCE(ha_mon->env_store[env], time_ns + value);
286 }
ha_check_invariant_ns(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)287 static inline bool ha_check_invariant_ns(struct ha_monitor *ha_mon,
288 					 enum envs env, u64 time_ns)
289 {
290 	return READ_ONCE(ha_mon->env_store[env]) >= time_ns;
291 }
292 /*
293  * ha_invariant_passed_ns - prepare the invariant and return the time since reset
294  */
ha_invariant_passed_ns(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)295 static inline u64 ha_invariant_passed_ns(struct ha_monitor *ha_mon, enum envs env,
296 				   u64 expire, u64 time_ns)
297 {
298 	u64 passed = 0;
299 
300 	if (env < 0 || env >= ENV_MAX_STORED)
301 		return 0;
302 	if (ha_monitor_env_invalid(ha_mon, env))
303 		return 0;
304 	passed = ha_get_env(ha_mon, env, time_ns);
305 	ha_set_invariant_ns(ha_mon, env, expire - passed, time_ns);
306 	return passed;
307 }
308 
309 /*
310  * Helper functions for env variables describing clocks with jiffy granularity
311  */
ha_get_clk_jiffy(struct ha_monitor * ha_mon,enum envs env)312 static inline u64 ha_get_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
313 {
314 	return get_jiffies_64() - READ_ONCE(ha_mon->env_store[env]);
315 }
ha_reset_clk_jiffy(struct ha_monitor * ha_mon,enum envs env)316 static inline void ha_reset_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
317 {
318 	WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64());
319 }
ha_set_invariant_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 value)320 static inline void ha_set_invariant_jiffy(struct ha_monitor *ha_mon,
321 					  enum envs env, u64 value)
322 {
323 	WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64() + value);
324 }
ha_check_invariant_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)325 static inline bool ha_check_invariant_jiffy(struct ha_monitor *ha_mon,
326 					    enum envs env, u64 time_ns)
327 {
328 	return time_after64(READ_ONCE(ha_mon->env_store[env]), get_jiffies_64());
329 
330 }
331 /*
332  * ha_invariant_passed_jiffy - prepare the invariant and return the time since reset
333  */
ha_invariant_passed_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)334 static inline u64 ha_invariant_passed_jiffy(struct ha_monitor *ha_mon, enum envs env,
335 				      u64 expire, u64 time_ns)
336 {
337 	u64 passed = 0;
338 
339 	if (env < 0 || env >= ENV_MAX_STORED)
340 		return 0;
341 	if (ha_monitor_env_invalid(ha_mon, env))
342 		return 0;
343 	passed = ha_get_env(ha_mon, env, time_ns);
344 	ha_set_invariant_jiffy(ha_mon, env, expire - passed);
345 	return passed;
346 }
347 
348 /*
349  * Retrieve the last reset time (guard representation) from the invariant
350  * representation (expiration).
351  * It the caller's responsibility to make sure the storage was actually in the
352  * invariant representation (e.g. the current state has an invariant).
353  * The provided value must be the same used when starting the invariant.
354  *
355  * This function's access to the storage is NOT atomic, due to the rarity when
356  * this is used. If a monitor allows writes concurrent to this, likely
357  * other things are broken and need rethinking the model or additional locking.
358  */
ha_inv_to_guard(struct ha_monitor * ha_mon,enum envs env,u64 value,u64 time_ns)359 static inline void ha_inv_to_guard(struct ha_monitor *ha_mon, enum envs env,
360 				   u64 value, u64 time_ns)
361 {
362 	WRITE_ONCE(ha_mon->env_store[env], READ_ONCE(ha_mon->env_store[env]) - value);
363 }
364 
365 #if HA_TIMER_TYPE == HA_TIMER_WHEEL
366 /*
367  * Helper functions to handle the monitor timer.
368  * Not all monitors require a timer, in such case the timer will be set up but
369  * never armed.
370  * Timers start since the last reset of the supplied env or from now if env is
371  * not an environment variable. If env was not initialised no timer starts.
372  * Timers can expire on any CPU unless the monitor is per-cpu,
373  * where we assume every event occurs on the local CPU.
374  */
ha_monitor_timer_callback(struct timer_list * timer)375 static void ha_monitor_timer_callback(struct timer_list *timer)
376 {
377 	struct ha_monitor *ha_mon = container_of(timer, struct ha_monitor, timer);
378 
379 	__ha_monitor_timer_callback(ha_mon);
380 }
ha_setup_timer(struct ha_monitor * ha_mon)381 static inline void ha_setup_timer(struct ha_monitor *ha_mon)
382 {
383 	int mode = 0;
384 
385 	if (RV_MON_TYPE == RV_MON_PER_CPU)
386 		mode |= TIMER_PINNED;
387 	timer_setup(&ha_mon->timer, ha_monitor_timer_callback, mode);
388 }
ha_start_timer_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)389 static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
390 					u64 expire, u64 time_ns)
391 {
392 	u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
393 
394 	mod_timer(&ha_mon->timer, get_jiffies_64() + expire - passed);
395 }
ha_start_timer_ns(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)396 static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
397 				     u64 expire, u64 time_ns)
398 {
399 	u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
400 
401 	ha_start_timer_jiffy(ha_mon, ENV_MAX_STORED,
402 			     nsecs_to_jiffies(expire - passed + TICK_NSEC - 1), time_ns);
403 }
404 /*
405  * ha_cancel_timer - Cancel the timer
406  *
407  * Returns:
408  *  *  1 when the timer was active
409  *  *  0 when the timer was not active or running a callback
410  */
ha_cancel_timer(struct ha_monitor * ha_mon)411 static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
412 {
413 	return timer_delete(&ha_mon->timer);
414 }
415 #elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
416 /*
417  * Helper functions to handle the monitor timer.
418  * Not all monitors require a timer, in such case the timer will be set up but
419  * never armed.
420  * Timers start since the last reset of the supplied env or from now if env is
421  * not an environment variable. If env was not initialised no timer starts.
422  * Timers can expire on any CPU unless the monitor is per-cpu,
423  * where we assume every event occurs on the local CPU.
424  */
ha_monitor_timer_callback(struct hrtimer * hrtimer)425 static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer)
426 {
427 	struct ha_monitor *ha_mon = container_of(hrtimer, struct ha_monitor, hrtimer);
428 
429 	__ha_monitor_timer_callback(ha_mon);
430 	return HRTIMER_NORESTART;
431 }
ha_setup_timer(struct ha_monitor * ha_mon)432 static inline void ha_setup_timer(struct ha_monitor *ha_mon)
433 {
434 	hrtimer_setup(&ha_mon->hrtimer, ha_monitor_timer_callback,
435 		      CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
436 }
ha_start_timer_ns(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)437 static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
438 				     u64 expire, u64 time_ns)
439 {
440 	int mode = HRTIMER_MODE_REL_HARD;
441 	u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
442 
443 	if (RV_MON_TYPE == RV_MON_PER_CPU)
444 		mode |= HRTIMER_MODE_PINNED;
445 	hrtimer_start(&ha_mon->hrtimer, ns_to_ktime(expire - passed), mode);
446 }
ha_start_timer_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)447 static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
448 					u64 expire, u64 time_ns)
449 {
450 	u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
451 
452 	ha_start_timer_ns(ha_mon, ENV_MAX_STORED,
453 			  jiffies_to_nsecs(expire - passed), time_ns);
454 }
455 /*
456  * ha_cancel_timer - Cancel the timer
457  *
458  * Returns:
459  *  *  1 when the timer was active
460  *  *  0 when the timer was not active or running a callback
461  */
ha_cancel_timer(struct ha_monitor * ha_mon)462 static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
463 {
464 	return hrtimer_try_to_cancel(&ha_mon->hrtimer) == 1;
465 }
466 #else /* HA_TIMER_NONE */
467 /*
468  * Start function is intentionally not defined, monitors using timers must
469  * set HA_TIMER_TYPE to either HA_TIMER_WHEEL or HA_TIMER_HRTIMER.
470  */
ha_setup_timer(struct ha_monitor * ha_mon)471 static inline void ha_setup_timer(struct ha_monitor *ha_mon) { }
ha_cancel_timer(struct ha_monitor * ha_mon)472 static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
473 {
474 	return false;
475 }
476 #endif
477 
478 #endif
479