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