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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 */ 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 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 */ 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 } 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 } 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 } 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 */ 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 */ 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 } 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 } 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 } 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 */ 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 */ 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 */ 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 } 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 } 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 } 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 */ 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 */ 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 } 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 } 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 } 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 */ 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 */ 471 static inline void ha_setup_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