Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/include/rv/ha_monitor.h
170847 views
1
/* SPDX-License-Identifier: GPL-2.0 */
2
/*
3
* Copyright (C) 2025-2028 Red Hat, Inc. Gabriele Monaco <[email protected]>
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
480