xref: /linux/include/rv/ha_monitor.h (revision fdbfee9fc56e13a1307868829d438ad66ab308a4)
1f5587d1bSGabriele Monaco /* SPDX-License-Identifier: GPL-2.0 */
2f5587d1bSGabriele Monaco /*
3f5587d1bSGabriele Monaco  * Copyright (C) 2025-2028 Red Hat, Inc. Gabriele Monaco <gmonaco@redhat.com>
4f5587d1bSGabriele Monaco  *
5f5587d1bSGabriele Monaco  * Hybrid automata (HA) monitor functions, to be used together
6f5587d1bSGabriele Monaco  * with automata models in C generated by the rvgen tool.
7f5587d1bSGabriele Monaco  *
8f5587d1bSGabriele Monaco  * This type of monitors extends the Deterministic automata (DA) class by
9f5587d1bSGabriele Monaco  * adding a set of environment variables (e.g. clocks) that can be used to
10f5587d1bSGabriele Monaco  * constraint the valid transitions.
11f5587d1bSGabriele Monaco  *
12f5587d1bSGabriele Monaco  * The rvgen tool is available at tools/verification/rvgen/
13f5587d1bSGabriele Monaco  *
14f5587d1bSGabriele Monaco  * For further information, see:
15f5587d1bSGabriele Monaco  *   Documentation/trace/rv/monitor_synthesis.rst
16f5587d1bSGabriele Monaco  */
17f5587d1bSGabriele Monaco 
18f5587d1bSGabriele Monaco #ifndef _RV_HA_MONITOR_H
19f5587d1bSGabriele Monaco #define _RV_HA_MONITOR_H
20f5587d1bSGabriele Monaco 
21f5587d1bSGabriele Monaco #include <rv/automata.h>
22f5587d1bSGabriele Monaco 
23f5587d1bSGabriele Monaco #ifndef da_id_type
24f5587d1bSGabriele Monaco #define da_id_type int
25f5587d1bSGabriele Monaco #endif
26f5587d1bSGabriele Monaco 
27f5587d1bSGabriele Monaco static inline void ha_monitor_init_env(struct da_monitor *da_mon);
28f5587d1bSGabriele Monaco static inline void ha_monitor_reset_env(struct da_monitor *da_mon);
29f5587d1bSGabriele Monaco static inline void ha_setup_timer(struct ha_monitor *ha_mon);
30f5587d1bSGabriele Monaco static inline bool ha_cancel_timer(struct ha_monitor *ha_mon);
31f5587d1bSGabriele Monaco static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
32f5587d1bSGabriele Monaco 					 enum states curr_state,
33f5587d1bSGabriele Monaco 					 enum events event,
34f5587d1bSGabriele Monaco 					 enum states next_state,
35f5587d1bSGabriele Monaco 					 da_id_type id);
36f5587d1bSGabriele Monaco #define da_monitor_event_hook ha_monitor_handle_constraint
37f5587d1bSGabriele Monaco #define da_monitor_init_hook ha_monitor_init_env
38f5587d1bSGabriele Monaco #define da_monitor_reset_hook ha_monitor_reset_env
39f5587d1bSGabriele Monaco 
40f5587d1bSGabriele Monaco #include <rv/da_monitor.h>
41f5587d1bSGabriele Monaco #include <linux/seq_buf.h>
42f5587d1bSGabriele Monaco 
43f5587d1bSGabriele Monaco /* This simplifies things since da_mon and ha_mon coexist in the same union */
44f5587d1bSGabriele Monaco _Static_assert(offsetof(struct ha_monitor, da_mon) == 0,
45f5587d1bSGabriele Monaco 	       "da_mon must be the first element in an ha_mon!");
46f5587d1bSGabriele Monaco #define to_ha_monitor(da) container_of(da, struct ha_monitor, da_mon)
47f5587d1bSGabriele Monaco 
48f5587d1bSGabriele Monaco #define ENV_MAX CONCATENATE(env_max_, MONITOR_NAME)
49f5587d1bSGabriele Monaco #define ENV_MAX_STORED CONCATENATE(env_max_stored_, MONITOR_NAME)
50f5587d1bSGabriele Monaco #define envs CONCATENATE(envs_, MONITOR_NAME)
51f5587d1bSGabriele Monaco 
52f5587d1bSGabriele Monaco /* Environment storage before being reset */
53f5587d1bSGabriele Monaco #define ENV_INVALID_VALUE U64_MAX
54f5587d1bSGabriele Monaco /* Error with no event occurs only on timeouts */
55f5587d1bSGabriele Monaco #define EVENT_NONE EVENT_MAX
56f5587d1bSGabriele Monaco #define EVENT_NONE_LBL "none"
57f5587d1bSGabriele Monaco #define ENV_BUFFER_SIZE 64
58f5587d1bSGabriele Monaco 
59f5587d1bSGabriele Monaco #ifdef CONFIG_RV_REACTORS
60f5587d1bSGabriele Monaco 
61f5587d1bSGabriele Monaco /*
62f5587d1bSGabriele Monaco  * ha_react - trigger the reaction after a failed environment constraint
63f5587d1bSGabriele Monaco  *
64f5587d1bSGabriele Monaco  * The transition from curr_state with event is otherwise valid, but the
65f5587d1bSGabriele Monaco  * environment constraint is false. This function can be called also with no
66f5587d1bSGabriele Monaco  * event from a timer (state constraints only).
67f5587d1bSGabriele Monaco  */
ha_react(enum states curr_state,enum events event,char * env)68f5587d1bSGabriele Monaco static void ha_react(enum states curr_state, enum events event, char *env)
69f5587d1bSGabriele Monaco {
70f5587d1bSGabriele Monaco 	rv_react(&rv_this,
71f5587d1bSGabriele Monaco 		 "rv: monitor %s does not allow event %s on state %s with env %s\n",
72f5587d1bSGabriele Monaco 		 __stringify(MONITOR_NAME),
73f5587d1bSGabriele Monaco 		 event == EVENT_NONE ? EVENT_NONE_LBL : model_get_event_name(event),
74f5587d1bSGabriele Monaco 		 model_get_state_name(curr_state), env);
75f5587d1bSGabriele Monaco }
76f5587d1bSGabriele Monaco 
77f5587d1bSGabriele Monaco #else /* CONFIG_RV_REACTOR */
78f5587d1bSGabriele Monaco 
ha_react(enum states curr_state,enum events event,char * env)79f5587d1bSGabriele Monaco static void ha_react(enum states curr_state, enum events event, char *env) { }
80f5587d1bSGabriele Monaco #endif
81f5587d1bSGabriele Monaco 
82f5587d1bSGabriele Monaco /*
83f5587d1bSGabriele Monaco  * model_get_state_name - return the (string) name of the given state
84f5587d1bSGabriele Monaco  */
model_get_env_name(enum envs env)85f5587d1bSGabriele Monaco static char *model_get_env_name(enum envs env)
86f5587d1bSGabriele Monaco {
87f5587d1bSGabriele Monaco 	if ((env < 0) || (env >= ENV_MAX))
88f5587d1bSGabriele Monaco 		return "INVALID";
89f5587d1bSGabriele Monaco 
90f5587d1bSGabriele Monaco 	return RV_AUTOMATON_NAME.env_names[env];
91f5587d1bSGabriele Monaco }
92f5587d1bSGabriele Monaco 
93f5587d1bSGabriele Monaco /*
94f5587d1bSGabriele Monaco  * Monitors requiring a timer implementation need to request it explicitly.
95f5587d1bSGabriele Monaco  */
96f5587d1bSGabriele Monaco #ifndef HA_TIMER_TYPE
97f5587d1bSGabriele Monaco #define HA_TIMER_TYPE HA_TIMER_NONE
98f5587d1bSGabriele Monaco #endif
99f5587d1bSGabriele Monaco 
100f5587d1bSGabriele Monaco #if HA_TIMER_TYPE == HA_TIMER_WHEEL
101f5587d1bSGabriele Monaco static void ha_monitor_timer_callback(struct timer_list *timer);
102f5587d1bSGabriele Monaco #elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
103f5587d1bSGabriele Monaco static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer);
104f5587d1bSGabriele Monaco #endif
105f5587d1bSGabriele Monaco 
106f5587d1bSGabriele Monaco /*
107f5587d1bSGabriele Monaco  * ktime_get_ns is expensive, since we usually don't require precise accounting
108f5587d1bSGabriele Monaco  * of changes within the same event, cache the current time at the beginning of
109f5587d1bSGabriele Monaco  * the constraint handler and use the cache for subsequent calls.
110f5587d1bSGabriele Monaco  * Monitors without ns clocks automatically skip this.
111f5587d1bSGabriele Monaco  */
112f5587d1bSGabriele Monaco #ifdef HA_CLK_NS
113f5587d1bSGabriele Monaco #define ha_get_ns() ktime_get_ns()
114f5587d1bSGabriele Monaco #else
115f5587d1bSGabriele Monaco #define ha_get_ns() 0
116f5587d1bSGabriele Monaco #endif /* HA_CLK_NS */
117f5587d1bSGabriele Monaco 
118f5587d1bSGabriele Monaco /* Should be supplied by the monitor */
119f5587d1bSGabriele Monaco static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env, u64 time_ns);
120f5587d1bSGabriele Monaco static bool ha_verify_constraint(struct ha_monitor *ha_mon,
121f5587d1bSGabriele Monaco 				 enum states curr_state,
122f5587d1bSGabriele Monaco 				 enum events event,
123f5587d1bSGabriele Monaco 				 enum states next_state,
124f5587d1bSGabriele Monaco 				 u64 time_ns);
125f5587d1bSGabriele Monaco 
126f5587d1bSGabriele Monaco /*
127f5587d1bSGabriele Monaco  * ha_monitor_reset_all_stored - reset all environment variables in the monitor
128f5587d1bSGabriele Monaco  */
ha_monitor_reset_all_stored(struct ha_monitor * ha_mon)129f5587d1bSGabriele Monaco static inline void ha_monitor_reset_all_stored(struct ha_monitor *ha_mon)
130f5587d1bSGabriele Monaco {
131f5587d1bSGabriele Monaco 	for (int i = 0; i < ENV_MAX_STORED; i++)
132f5587d1bSGabriele Monaco 		WRITE_ONCE(ha_mon->env_store[i], ENV_INVALID_VALUE);
133f5587d1bSGabriele Monaco }
134f5587d1bSGabriele Monaco 
135f5587d1bSGabriele Monaco /*
136f5587d1bSGabriele Monaco  * ha_monitor_init_env - setup timer and reset all environment
137f5587d1bSGabriele Monaco  *
138f5587d1bSGabriele Monaco  * Called from a hook in the DA start functions, it supplies the da_mon
139f5587d1bSGabriele Monaco  * corresponding to the current ha_mon.
140f5587d1bSGabriele Monaco  * Not all hybrid automata require the timer, still set it for simplicity.
141f5587d1bSGabriele Monaco  */
ha_monitor_init_env(struct da_monitor * da_mon)142f5587d1bSGabriele Monaco static inline void ha_monitor_init_env(struct da_monitor *da_mon)
143f5587d1bSGabriele Monaco {
144f5587d1bSGabriele Monaco 	struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
145f5587d1bSGabriele Monaco 
146f5587d1bSGabriele Monaco 	ha_monitor_reset_all_stored(ha_mon);
147f5587d1bSGabriele Monaco 	ha_setup_timer(ha_mon);
148f5587d1bSGabriele Monaco }
149f5587d1bSGabriele Monaco 
150f5587d1bSGabriele Monaco /*
151f5587d1bSGabriele Monaco  * ha_monitor_reset_env - stop timer and reset all environment
152f5587d1bSGabriele Monaco  *
153f5587d1bSGabriele Monaco  * Called from a hook in the DA reset functions, it supplies the da_mon
154f5587d1bSGabriele Monaco  * corresponding to the current ha_mon.
155f5587d1bSGabriele Monaco  * Not all hybrid automata require the timer, still clear it for simplicity.
156f5587d1bSGabriele Monaco  */
ha_monitor_reset_env(struct da_monitor * da_mon)157f5587d1bSGabriele Monaco static inline void ha_monitor_reset_env(struct da_monitor *da_mon)
158f5587d1bSGabriele Monaco {
159f5587d1bSGabriele Monaco 	struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
160f5587d1bSGabriele Monaco 
161f5587d1bSGabriele Monaco 	/* Initialisation resets the monitor before initialising the timer */
162f5587d1bSGabriele Monaco 	if (likely(da_monitoring(da_mon)))
163f5587d1bSGabriele Monaco 		ha_cancel_timer(ha_mon);
164f5587d1bSGabriele Monaco }
165f5587d1bSGabriele Monaco 
166f5587d1bSGabriele Monaco /*
167f5587d1bSGabriele Monaco  * ha_monitor_env_invalid - return true if env has not been initialised
168f5587d1bSGabriele Monaco  */
ha_monitor_env_invalid(struct ha_monitor * ha_mon,enum envs env)169f5587d1bSGabriele Monaco static inline bool ha_monitor_env_invalid(struct ha_monitor *ha_mon, enum envs env)
170f5587d1bSGabriele Monaco {
171f5587d1bSGabriele Monaco 	return READ_ONCE(ha_mon->env_store[env]) == ENV_INVALID_VALUE;
172f5587d1bSGabriele Monaco }
173f5587d1bSGabriele Monaco 
ha_get_env_string(struct seq_buf * s,struct ha_monitor * ha_mon,u64 time_ns)174f5587d1bSGabriele Monaco static inline void ha_get_env_string(struct seq_buf *s,
175f5587d1bSGabriele Monaco 				     struct ha_monitor *ha_mon, u64 time_ns)
176f5587d1bSGabriele Monaco {
177f5587d1bSGabriele Monaco 	const char *format_str = "%s=%llu";
178f5587d1bSGabriele Monaco 
179f5587d1bSGabriele Monaco 	for (int i = 0; i < ENV_MAX; i++) {
180f5587d1bSGabriele Monaco 		seq_buf_printf(s, format_str, model_get_env_name(i),
181f5587d1bSGabriele Monaco 			       ha_get_env(ha_mon, i, time_ns));
182f5587d1bSGabriele Monaco 		format_str = ",%s=%llu";
183f5587d1bSGabriele Monaco 	}
184f5587d1bSGabriele Monaco }
185f5587d1bSGabriele Monaco 
186f5587d1bSGabriele Monaco #if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
ha_trace_error_env(struct ha_monitor * ha_mon,char * curr_state,char * event,char * env,da_id_type id)187f5587d1bSGabriele Monaco static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
188f5587d1bSGabriele Monaco 				      char *curr_state, char *event, char *env,
189f5587d1bSGabriele Monaco 				      da_id_type id)
190f5587d1bSGabriele Monaco {
191f5587d1bSGabriele Monaco 	CONCATENATE(trace_error_env_, MONITOR_NAME)(curr_state, event, env);
192f5587d1bSGabriele Monaco }
193*4a24127bSGabriele Monaco #elif RV_MON_TYPE == RV_MON_PER_TASK || RV_MON_TYPE == RV_MON_PER_OBJ
194*4a24127bSGabriele Monaco 
195*4a24127bSGabriele Monaco #define ha_get_target(ha_mon) da_get_target(&ha_mon->da_mon)
196*4a24127bSGabriele Monaco 
ha_trace_error_env(struct ha_monitor * ha_mon,char * curr_state,char * event,char * env,da_id_type id)197f5587d1bSGabriele Monaco static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
198f5587d1bSGabriele Monaco 				      char *curr_state, char *event, char *env,
199f5587d1bSGabriele Monaco 				      da_id_type id)
200f5587d1bSGabriele Monaco {
201f5587d1bSGabriele Monaco 	CONCATENATE(trace_error_env_, MONITOR_NAME)(id, curr_state, event, env);
202f5587d1bSGabriele Monaco }
203f5587d1bSGabriele Monaco #endif /* RV_MON_TYPE */
204f5587d1bSGabriele Monaco 
205f5587d1bSGabriele Monaco /*
206f5587d1bSGabriele Monaco  * ha_get_monitor - return the current monitor
207f5587d1bSGabriele Monaco  */
208f5587d1bSGabriele Monaco #define ha_get_monitor(...) to_ha_monitor(da_get_monitor(__VA_ARGS__))
209f5587d1bSGabriele Monaco 
210f5587d1bSGabriele Monaco /*
211f5587d1bSGabriele Monaco  * ha_monitor_handle_constraint - handle the constraint on the current transition
212f5587d1bSGabriele Monaco  *
213f5587d1bSGabriele Monaco  * If the monitor implementation defines a constraint in the transition from
214f5587d1bSGabriele Monaco  * curr_state to event, react and trace appropriately as well as return false.
215f5587d1bSGabriele Monaco  * This function is called from the hook in the DA event handle function and
216f5587d1bSGabriele Monaco  * triggers a failure in the monitor.
217f5587d1bSGabriele Monaco  */
ha_monitor_handle_constraint(struct da_monitor * da_mon,enum states curr_state,enum events event,enum states next_state,da_id_type id)218f5587d1bSGabriele Monaco static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
219f5587d1bSGabriele Monaco 					 enum states curr_state,
220f5587d1bSGabriele Monaco 					 enum events event,
221f5587d1bSGabriele Monaco 					 enum states next_state,
222f5587d1bSGabriele Monaco 					 da_id_type id)
223f5587d1bSGabriele Monaco {
224f5587d1bSGabriele Monaco 	struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
225f5587d1bSGabriele Monaco 	u64 time_ns = ha_get_ns();
226f5587d1bSGabriele Monaco 	DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
227f5587d1bSGabriele Monaco 
228f5587d1bSGabriele Monaco 	if (ha_verify_constraint(ha_mon, curr_state, event, next_state, time_ns))
229f5587d1bSGabriele Monaco 		return true;
230f5587d1bSGabriele Monaco 
231f5587d1bSGabriele Monaco 	ha_get_env_string(&env_string, ha_mon, time_ns);
232f5587d1bSGabriele Monaco 	ha_react(curr_state, event, env_string.buffer);
233f5587d1bSGabriele Monaco 	ha_trace_error_env(ha_mon,
234f5587d1bSGabriele Monaco 			   model_get_state_name(curr_state),
235f5587d1bSGabriele Monaco 			   model_get_event_name(event),
236f5587d1bSGabriele Monaco 			   env_string.buffer, id);
237f5587d1bSGabriele Monaco 	return false;
238f5587d1bSGabriele Monaco }
239f5587d1bSGabriele Monaco 
__ha_monitor_timer_callback(struct ha_monitor * ha_mon)240f5587d1bSGabriele Monaco static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon)
241f5587d1bSGabriele Monaco {
242f5587d1bSGabriele Monaco 	enum states curr_state = READ_ONCE(ha_mon->da_mon.curr_state);
243f5587d1bSGabriele Monaco 	DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
244f5587d1bSGabriele Monaco 	u64 time_ns = ha_get_ns();
245f5587d1bSGabriele Monaco 
246f5587d1bSGabriele Monaco 	ha_get_env_string(&env_string, ha_mon, time_ns);
247f5587d1bSGabriele Monaco 	ha_react(curr_state, EVENT_NONE, env_string.buffer);
248f5587d1bSGabriele Monaco 	ha_trace_error_env(ha_mon, model_get_state_name(curr_state),
249f5587d1bSGabriele Monaco 			   EVENT_NONE_LBL, env_string.buffer,
250f5587d1bSGabriele Monaco 			   da_get_id(&ha_mon->da_mon));
251f5587d1bSGabriele Monaco 
252f5587d1bSGabriele Monaco 	da_monitor_reset(&ha_mon->da_mon);
253f5587d1bSGabriele Monaco }
254f5587d1bSGabriele Monaco 
255f5587d1bSGabriele Monaco /*
256f5587d1bSGabriele Monaco  * The clock variables have 2 different representations in the env_store:
257f5587d1bSGabriele Monaco  * - The guard representation is the timestamp of the last reset
258f5587d1bSGabriele Monaco  * - The invariant representation is the timestamp when the invariant expires
259f5587d1bSGabriele Monaco  * As the representations are incompatible, care must be taken when switching
260f5587d1bSGabriele Monaco  * between them: the invariant representation can only be used when starting a
261f5587d1bSGabriele Monaco  * timer when the previous representation was guard (e.g. no other invariant
262f5587d1bSGabriele Monaco  * started since the last reset operation).
263f5587d1bSGabriele Monaco  * Likewise, switching from invariant to guard representation without a reset
264f5587d1bSGabriele Monaco  * can be done only by subtracting the exact value used to start the invariant.
265f5587d1bSGabriele Monaco  *
266f5587d1bSGabriele Monaco  * Reading the environment variable (ha_get_clk) also reflects this difference
267f5587d1bSGabriele Monaco  * any reads in states that have an invariant return the (possibly negative)
268f5587d1bSGabriele Monaco  * time since expiration, other reads return the time since last reset.
269f5587d1bSGabriele Monaco  */
270f5587d1bSGabriele Monaco 
271f5587d1bSGabriele Monaco /*
272f5587d1bSGabriele Monaco  * Helper functions for env variables describing clocks with ns granularity
273f5587d1bSGabriele Monaco  */
ha_get_clk_ns(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)274f5587d1bSGabriele Monaco static inline u64 ha_get_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
275f5587d1bSGabriele Monaco {
276f5587d1bSGabriele Monaco 	return time_ns - READ_ONCE(ha_mon->env_store[env]);
277f5587d1bSGabriele Monaco }
ha_reset_clk_ns(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)278f5587d1bSGabriele Monaco static inline void ha_reset_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
279f5587d1bSGabriele Monaco {
280f5587d1bSGabriele Monaco 	WRITE_ONCE(ha_mon->env_store[env], time_ns);
281f5587d1bSGabriele Monaco }
ha_set_invariant_ns(struct ha_monitor * ha_mon,enum envs env,u64 value,u64 time_ns)282f5587d1bSGabriele Monaco static inline void ha_set_invariant_ns(struct ha_monitor *ha_mon, enum envs env,
283f5587d1bSGabriele Monaco 				       u64 value, u64 time_ns)
284f5587d1bSGabriele Monaco {
285f5587d1bSGabriele Monaco 	WRITE_ONCE(ha_mon->env_store[env], time_ns + value);
286f5587d1bSGabriele Monaco }
ha_check_invariant_ns(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)287f5587d1bSGabriele Monaco static inline bool ha_check_invariant_ns(struct ha_monitor *ha_mon,
288f5587d1bSGabriele Monaco 					 enum envs env, u64 time_ns)
289f5587d1bSGabriele Monaco {
290f5587d1bSGabriele Monaco 	return READ_ONCE(ha_mon->env_store[env]) >= time_ns;
291f5587d1bSGabriele Monaco }
292f5587d1bSGabriele Monaco /*
293f5587d1bSGabriele Monaco  * ha_invariant_passed_ns - prepare the invariant and return the time since reset
294f5587d1bSGabriele Monaco  */
ha_invariant_passed_ns(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)295f5587d1bSGabriele Monaco static inline u64 ha_invariant_passed_ns(struct ha_monitor *ha_mon, enum envs env,
296f5587d1bSGabriele Monaco 				   u64 expire, u64 time_ns)
297f5587d1bSGabriele Monaco {
298f5587d1bSGabriele Monaco 	u64 passed = 0;
299f5587d1bSGabriele Monaco 
300f5587d1bSGabriele Monaco 	if (env < 0 || env >= ENV_MAX_STORED)
301f5587d1bSGabriele Monaco 		return 0;
302f5587d1bSGabriele Monaco 	if (ha_monitor_env_invalid(ha_mon, env))
303f5587d1bSGabriele Monaco 		return 0;
304f5587d1bSGabriele Monaco 	passed = ha_get_env(ha_mon, env, time_ns);
305f5587d1bSGabriele Monaco 	ha_set_invariant_ns(ha_mon, env, expire - passed, time_ns);
306f5587d1bSGabriele Monaco 	return passed;
307f5587d1bSGabriele Monaco }
308f5587d1bSGabriele Monaco 
309f5587d1bSGabriele Monaco /*
310f5587d1bSGabriele Monaco  * Helper functions for env variables describing clocks with jiffy granularity
311f5587d1bSGabriele Monaco  */
ha_get_clk_jiffy(struct ha_monitor * ha_mon,enum envs env)312f5587d1bSGabriele Monaco static inline u64 ha_get_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
313f5587d1bSGabriele Monaco {
314f5587d1bSGabriele Monaco 	return get_jiffies_64() - READ_ONCE(ha_mon->env_store[env]);
315f5587d1bSGabriele Monaco }
ha_reset_clk_jiffy(struct ha_monitor * ha_mon,enum envs env)316f5587d1bSGabriele Monaco static inline void ha_reset_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
317f5587d1bSGabriele Monaco {
318f5587d1bSGabriele Monaco 	WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64());
319f5587d1bSGabriele Monaco }
ha_set_invariant_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 value)320f5587d1bSGabriele Monaco static inline void ha_set_invariant_jiffy(struct ha_monitor *ha_mon,
321f5587d1bSGabriele Monaco 					  enum envs env, u64 value)
322f5587d1bSGabriele Monaco {
323f5587d1bSGabriele Monaco 	WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64() + value);
324f5587d1bSGabriele Monaco }
ha_check_invariant_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 time_ns)325f5587d1bSGabriele Monaco static inline bool ha_check_invariant_jiffy(struct ha_monitor *ha_mon,
326f5587d1bSGabriele Monaco 					    enum envs env, u64 time_ns)
327f5587d1bSGabriele Monaco {
328f5587d1bSGabriele Monaco 	return time_after64(READ_ONCE(ha_mon->env_store[env]), get_jiffies_64());
329f5587d1bSGabriele Monaco 
330f5587d1bSGabriele Monaco }
331f5587d1bSGabriele Monaco /*
332f5587d1bSGabriele Monaco  * ha_invariant_passed_jiffy - prepare the invariant and return the time since reset
333f5587d1bSGabriele Monaco  */
ha_invariant_passed_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)334f5587d1bSGabriele Monaco static inline u64 ha_invariant_passed_jiffy(struct ha_monitor *ha_mon, enum envs env,
335f5587d1bSGabriele Monaco 				      u64 expire, u64 time_ns)
336f5587d1bSGabriele Monaco {
337f5587d1bSGabriele Monaco 	u64 passed = 0;
338f5587d1bSGabriele Monaco 
339f5587d1bSGabriele Monaco 	if (env < 0 || env >= ENV_MAX_STORED)
340f5587d1bSGabriele Monaco 		return 0;
341f5587d1bSGabriele Monaco 	if (ha_monitor_env_invalid(ha_mon, env))
342f5587d1bSGabriele Monaco 		return 0;
343f5587d1bSGabriele Monaco 	passed = ha_get_env(ha_mon, env, time_ns);
344f5587d1bSGabriele Monaco 	ha_set_invariant_jiffy(ha_mon, env, expire - passed);
345f5587d1bSGabriele Monaco 	return passed;
346f5587d1bSGabriele Monaco }
347f5587d1bSGabriele Monaco 
348f5587d1bSGabriele Monaco /*
349f5587d1bSGabriele Monaco  * Retrieve the last reset time (guard representation) from the invariant
350f5587d1bSGabriele Monaco  * representation (expiration).
351f5587d1bSGabriele Monaco  * It the caller's responsibility to make sure the storage was actually in the
352f5587d1bSGabriele Monaco  * invariant representation (e.g. the current state has an invariant).
353f5587d1bSGabriele Monaco  * The provided value must be the same used when starting the invariant.
354f5587d1bSGabriele Monaco  *
355f5587d1bSGabriele Monaco  * This function's access to the storage is NOT atomic, due to the rarity when
356f5587d1bSGabriele Monaco  * this is used. If a monitor allows writes concurrent to this, likely
357f5587d1bSGabriele Monaco  * other things are broken and need rethinking the model or additional locking.
358f5587d1bSGabriele Monaco  */
ha_inv_to_guard(struct ha_monitor * ha_mon,enum envs env,u64 value,u64 time_ns)359f5587d1bSGabriele Monaco static inline void ha_inv_to_guard(struct ha_monitor *ha_mon, enum envs env,
360f5587d1bSGabriele Monaco 				   u64 value, u64 time_ns)
361f5587d1bSGabriele Monaco {
362f5587d1bSGabriele Monaco 	WRITE_ONCE(ha_mon->env_store[env], READ_ONCE(ha_mon->env_store[env]) - value);
363f5587d1bSGabriele Monaco }
364f5587d1bSGabriele Monaco 
365f5587d1bSGabriele Monaco #if HA_TIMER_TYPE == HA_TIMER_WHEEL
366f5587d1bSGabriele Monaco /*
367f5587d1bSGabriele Monaco  * Helper functions to handle the monitor timer.
368f5587d1bSGabriele Monaco  * Not all monitors require a timer, in such case the timer will be set up but
369f5587d1bSGabriele Monaco  * never armed.
370f5587d1bSGabriele Monaco  * Timers start since the last reset of the supplied env or from now if env is
371f5587d1bSGabriele Monaco  * not an environment variable. If env was not initialised no timer starts.
372f5587d1bSGabriele Monaco  * Timers can expire on any CPU unless the monitor is per-cpu,
373f5587d1bSGabriele Monaco  * where we assume every event occurs on the local CPU.
374f5587d1bSGabriele Monaco  */
ha_monitor_timer_callback(struct timer_list * timer)375f5587d1bSGabriele Monaco static void ha_monitor_timer_callback(struct timer_list *timer)
376f5587d1bSGabriele Monaco {
377f5587d1bSGabriele Monaco 	struct ha_monitor *ha_mon = container_of(timer, struct ha_monitor, timer);
378f5587d1bSGabriele Monaco 
379f5587d1bSGabriele Monaco 	__ha_monitor_timer_callback(ha_mon);
380f5587d1bSGabriele Monaco }
ha_setup_timer(struct ha_monitor * ha_mon)381f5587d1bSGabriele Monaco static inline void ha_setup_timer(struct ha_monitor *ha_mon)
382f5587d1bSGabriele Monaco {
383f5587d1bSGabriele Monaco 	int mode = 0;
384f5587d1bSGabriele Monaco 
385f5587d1bSGabriele Monaco 	if (RV_MON_TYPE == RV_MON_PER_CPU)
386f5587d1bSGabriele Monaco 		mode |= TIMER_PINNED;
387f5587d1bSGabriele Monaco 	timer_setup(&ha_mon->timer, ha_monitor_timer_callback, mode);
388f5587d1bSGabriele Monaco }
ha_start_timer_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)389f5587d1bSGabriele Monaco static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
390f5587d1bSGabriele Monaco 					u64 expire, u64 time_ns)
391f5587d1bSGabriele Monaco {
392f5587d1bSGabriele Monaco 	u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
393f5587d1bSGabriele Monaco 
394f5587d1bSGabriele Monaco 	mod_timer(&ha_mon->timer, get_jiffies_64() + expire - passed);
395f5587d1bSGabriele Monaco }
ha_start_timer_ns(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)396f5587d1bSGabriele Monaco static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
397f5587d1bSGabriele Monaco 				     u64 expire, u64 time_ns)
398f5587d1bSGabriele Monaco {
399f5587d1bSGabriele Monaco 	u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
400f5587d1bSGabriele Monaco 
401f5587d1bSGabriele Monaco 	ha_start_timer_jiffy(ha_mon, ENV_MAX_STORED,
402f5587d1bSGabriele Monaco 			     nsecs_to_jiffies(expire - passed + TICK_NSEC - 1), time_ns);
403f5587d1bSGabriele Monaco }
404f5587d1bSGabriele Monaco /*
405f5587d1bSGabriele Monaco  * ha_cancel_timer - Cancel the timer
406f5587d1bSGabriele Monaco  *
407f5587d1bSGabriele Monaco  * Returns:
408f5587d1bSGabriele Monaco  *  *  1 when the timer was active
409f5587d1bSGabriele Monaco  *  *  0 when the timer was not active or running a callback
410f5587d1bSGabriele Monaco  */
ha_cancel_timer(struct ha_monitor * ha_mon)411f5587d1bSGabriele Monaco static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
412f5587d1bSGabriele Monaco {
413f5587d1bSGabriele Monaco 	return timer_delete(&ha_mon->timer);
414f5587d1bSGabriele Monaco }
415f5587d1bSGabriele Monaco #elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
416f5587d1bSGabriele Monaco /*
417f5587d1bSGabriele Monaco  * Helper functions to handle the monitor timer.
418f5587d1bSGabriele Monaco  * Not all monitors require a timer, in such case the timer will be set up but
419f5587d1bSGabriele Monaco  * never armed.
420f5587d1bSGabriele Monaco  * Timers start since the last reset of the supplied env or from now if env is
421f5587d1bSGabriele Monaco  * not an environment variable. If env was not initialised no timer starts.
422f5587d1bSGabriele Monaco  * Timers can expire on any CPU unless the monitor is per-cpu,
423f5587d1bSGabriele Monaco  * where we assume every event occurs on the local CPU.
424f5587d1bSGabriele Monaco  */
ha_monitor_timer_callback(struct hrtimer * hrtimer)425f5587d1bSGabriele Monaco static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer)
426f5587d1bSGabriele Monaco {
427f5587d1bSGabriele Monaco 	struct ha_monitor *ha_mon = container_of(hrtimer, struct ha_monitor, hrtimer);
428f5587d1bSGabriele Monaco 
429f5587d1bSGabriele Monaco 	__ha_monitor_timer_callback(ha_mon);
430f5587d1bSGabriele Monaco 	return HRTIMER_NORESTART;
431f5587d1bSGabriele Monaco }
ha_setup_timer(struct ha_monitor * ha_mon)432f5587d1bSGabriele Monaco static inline void ha_setup_timer(struct ha_monitor *ha_mon)
433f5587d1bSGabriele Monaco {
434f5587d1bSGabriele Monaco 	hrtimer_setup(&ha_mon->hrtimer, ha_monitor_timer_callback,
435f5587d1bSGabriele Monaco 		      CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
436f5587d1bSGabriele Monaco }
ha_start_timer_ns(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)437f5587d1bSGabriele Monaco static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
438f5587d1bSGabriele Monaco 				     u64 expire, u64 time_ns)
439f5587d1bSGabriele Monaco {
440f5587d1bSGabriele Monaco 	int mode = HRTIMER_MODE_REL_HARD;
441f5587d1bSGabriele Monaco 	u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
442f5587d1bSGabriele Monaco 
443f5587d1bSGabriele Monaco 	if (RV_MON_TYPE == RV_MON_PER_CPU)
444f5587d1bSGabriele Monaco 		mode |= HRTIMER_MODE_PINNED;
445f5587d1bSGabriele Monaco 	hrtimer_start(&ha_mon->hrtimer, ns_to_ktime(expire - passed), mode);
446f5587d1bSGabriele Monaco }
ha_start_timer_jiffy(struct ha_monitor * ha_mon,enum envs env,u64 expire,u64 time_ns)447f5587d1bSGabriele Monaco static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
448f5587d1bSGabriele Monaco 					u64 expire, u64 time_ns)
449f5587d1bSGabriele Monaco {
450f5587d1bSGabriele Monaco 	u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
451f5587d1bSGabriele Monaco 
452f5587d1bSGabriele Monaco 	ha_start_timer_ns(ha_mon, ENV_MAX_STORED,
453f5587d1bSGabriele Monaco 			  jiffies_to_nsecs(expire - passed), time_ns);
454f5587d1bSGabriele Monaco }
455f5587d1bSGabriele Monaco /*
456f5587d1bSGabriele Monaco  * ha_cancel_timer - Cancel the timer
457f5587d1bSGabriele Monaco  *
458f5587d1bSGabriele Monaco  * Returns:
459f5587d1bSGabriele Monaco  *  *  1 when the timer was active
460f5587d1bSGabriele Monaco  *  *  0 when the timer was not active or running a callback
461f5587d1bSGabriele Monaco  */
ha_cancel_timer(struct ha_monitor * ha_mon)462f5587d1bSGabriele Monaco static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
463f5587d1bSGabriele Monaco {
464f5587d1bSGabriele Monaco 	return hrtimer_try_to_cancel(&ha_mon->hrtimer) == 1;
465f5587d1bSGabriele Monaco }
466f5587d1bSGabriele Monaco #else /* HA_TIMER_NONE */
467f5587d1bSGabriele Monaco /*
468f5587d1bSGabriele Monaco  * Start function is intentionally not defined, monitors using timers must
469f5587d1bSGabriele Monaco  * set HA_TIMER_TYPE to either HA_TIMER_WHEEL or HA_TIMER_HRTIMER.
470f5587d1bSGabriele Monaco  */
ha_setup_timer(struct ha_monitor * ha_mon)471f5587d1bSGabriele Monaco static inline void ha_setup_timer(struct ha_monitor *ha_mon) { }
ha_cancel_timer(struct ha_monitor * ha_mon)472f5587d1bSGabriele Monaco static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
473f5587d1bSGabriele Monaco {
474f5587d1bSGabriele Monaco 	return false;
475f5587d1bSGabriele Monaco }
476f5587d1bSGabriele Monaco #endif
477f5587d1bSGabriele Monaco 
478f5587d1bSGabriele Monaco #endif
479