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