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