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