#ifndef _RV_HA_MONITOR_H
#define _RV_HA_MONITOR_H
#include <rv/automata.h>
#ifndef da_id_type
#define da_id_type int
#endif
static inline void ha_monitor_init_env(struct da_monitor *da_mon);
static inline void ha_monitor_reset_env(struct da_monitor *da_mon);
static inline void ha_setup_timer(struct ha_monitor *ha_mon);
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon);
static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
enum states curr_state,
enum events event,
enum states next_state,
da_id_type id);
#define da_monitor_event_hook ha_monitor_handle_constraint
#define da_monitor_init_hook ha_monitor_init_env
#define da_monitor_reset_hook ha_monitor_reset_env
#include <rv/da_monitor.h>
#include <linux/seq_buf.h>
_Static_assert(offsetof(struct ha_monitor, da_mon) == 0,
"da_mon must be the first element in an ha_mon!");
#define to_ha_monitor(da) container_of(da, struct ha_monitor, da_mon)
#define ENV_MAX CONCATENATE(env_max_, MONITOR_NAME)
#define ENV_MAX_STORED CONCATENATE(env_max_stored_, MONITOR_NAME)
#define envs CONCATENATE(envs_, MONITOR_NAME)
#define ENV_INVALID_VALUE U64_MAX
#define EVENT_NONE EVENT_MAX
#define EVENT_NONE_LBL "none"
#define ENV_BUFFER_SIZE 64
#ifdef CONFIG_RV_REACTORS
static void ha_react(enum states curr_state, enum events event, char *env)
{
rv_react(&rv_this,
"rv: monitor %s does not allow event %s on state %s with env %s\n",
__stringify(MONITOR_NAME),
event == EVENT_NONE ? EVENT_NONE_LBL : model_get_event_name(event),
model_get_state_name(curr_state), env);
}
#else
static void ha_react(enum states curr_state, enum events event, char *env) { }
#endif
static char *model_get_env_name(enum envs env)
{
if ((env < 0) || (env >= ENV_MAX))
return "INVALID";
return RV_AUTOMATON_NAME.env_names[env];
}
#ifndef HA_TIMER_TYPE
#define HA_TIMER_TYPE HA_TIMER_NONE
#endif
#if HA_TIMER_TYPE == HA_TIMER_WHEEL
static void ha_monitor_timer_callback(struct timer_list *timer);
#elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer);
#endif
#ifdef HA_CLK_NS
#define ha_get_ns() ktime_get_ns()
#else
#define ha_get_ns() 0
#endif
static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs env, u64 time_ns);
static bool ha_verify_constraint(struct ha_monitor *ha_mon,
enum states curr_state,
enum events event,
enum states next_state,
u64 time_ns);
static inline void ha_monitor_reset_all_stored(struct ha_monitor *ha_mon)
{
for (int i = 0; i < ENV_MAX_STORED; i++)
WRITE_ONCE(ha_mon->env_store[i], ENV_INVALID_VALUE);
}
static inline void ha_monitor_init_env(struct da_monitor *da_mon)
{
struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
ha_monitor_reset_all_stored(ha_mon);
ha_setup_timer(ha_mon);
}
static inline void ha_monitor_reset_env(struct da_monitor *da_mon)
{
struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
if (likely(da_monitoring(da_mon)))
ha_cancel_timer(ha_mon);
}
static inline bool ha_monitor_env_invalid(struct ha_monitor *ha_mon, enum envs env)
{
return READ_ONCE(ha_mon->env_store[env]) == ENV_INVALID_VALUE;
}
static inline void ha_get_env_string(struct seq_buf *s,
struct ha_monitor *ha_mon, u64 time_ns)
{
const char *format_str = "%s=%llu";
for (int i = 0; i < ENV_MAX; i++) {
seq_buf_printf(s, format_str, model_get_env_name(i),
ha_get_env(ha_mon, i, time_ns));
format_str = ",%s=%llu";
}
}
#if RV_MON_TYPE == RV_MON_GLOBAL || RV_MON_TYPE == RV_MON_PER_CPU
static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
char *curr_state, char *event, char *env,
da_id_type id)
{
CONCATENATE(trace_error_env_, MONITOR_NAME)(curr_state, event, env);
}
#elif RV_MON_TYPE == RV_MON_PER_TASK || RV_MON_TYPE == RV_MON_PER_OBJ
#define ha_get_target(ha_mon) da_get_target(&ha_mon->da_mon)
static inline void ha_trace_error_env(struct ha_monitor *ha_mon,
char *curr_state, char *event, char *env,
da_id_type id)
{
CONCATENATE(trace_error_env_, MONITOR_NAME)(id, curr_state, event, env);
}
#endif
#define ha_get_monitor(...) to_ha_monitor(da_get_monitor(__VA_ARGS__))
static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
enum states curr_state,
enum events event,
enum states next_state,
da_id_type id)
{
struct ha_monitor *ha_mon = to_ha_monitor(da_mon);
u64 time_ns = ha_get_ns();
DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
if (ha_verify_constraint(ha_mon, curr_state, event, next_state, time_ns))
return true;
ha_get_env_string(&env_string, ha_mon, time_ns);
ha_react(curr_state, event, env_string.buffer);
ha_trace_error_env(ha_mon,
model_get_state_name(curr_state),
model_get_event_name(event),
env_string.buffer, id);
return false;
}
static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon)
{
enum states curr_state = READ_ONCE(ha_mon->da_mon.curr_state);
DECLARE_SEQ_BUF(env_string, ENV_BUFFER_SIZE);
u64 time_ns = ha_get_ns();
ha_get_env_string(&env_string, ha_mon, time_ns);
ha_react(curr_state, EVENT_NONE, env_string.buffer);
ha_trace_error_env(ha_mon, model_get_state_name(curr_state),
EVENT_NONE_LBL, env_string.buffer,
da_get_id(&ha_mon->da_mon));
da_monitor_reset(&ha_mon->da_mon);
}
static inline u64 ha_get_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
{
return time_ns - READ_ONCE(ha_mon->env_store[env]);
}
static inline void ha_reset_clk_ns(struct ha_monitor *ha_mon, enum envs env, u64 time_ns)
{
WRITE_ONCE(ha_mon->env_store[env], time_ns);
}
static inline void ha_set_invariant_ns(struct ha_monitor *ha_mon, enum envs env,
u64 value, u64 time_ns)
{
WRITE_ONCE(ha_mon->env_store[env], time_ns + value);
}
static inline bool ha_check_invariant_ns(struct ha_monitor *ha_mon,
enum envs env, u64 time_ns)
{
return READ_ONCE(ha_mon->env_store[env]) >= time_ns;
}
static inline u64 ha_invariant_passed_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = 0;
if (env < 0 || env >= ENV_MAX_STORED)
return 0;
if (ha_monitor_env_invalid(ha_mon, env))
return 0;
passed = ha_get_env(ha_mon, env, time_ns);
ha_set_invariant_ns(ha_mon, env, expire - passed, time_ns);
return passed;
}
static inline u64 ha_get_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
{
return get_jiffies_64() - READ_ONCE(ha_mon->env_store[env]);
}
static inline void ha_reset_clk_jiffy(struct ha_monitor *ha_mon, enum envs env)
{
WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64());
}
static inline void ha_set_invariant_jiffy(struct ha_monitor *ha_mon,
enum envs env, u64 value)
{
WRITE_ONCE(ha_mon->env_store[env], get_jiffies_64() + value);
}
static inline bool ha_check_invariant_jiffy(struct ha_monitor *ha_mon,
enum envs env, u64 time_ns)
{
return time_after64(READ_ONCE(ha_mon->env_store[env]), get_jiffies_64());
}
static inline u64 ha_invariant_passed_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = 0;
if (env < 0 || env >= ENV_MAX_STORED)
return 0;
if (ha_monitor_env_invalid(ha_mon, env))
return 0;
passed = ha_get_env(ha_mon, env, time_ns);
ha_set_invariant_jiffy(ha_mon, env, expire - passed);
return passed;
}
static inline void ha_inv_to_guard(struct ha_monitor *ha_mon, enum envs env,
u64 value, u64 time_ns)
{
WRITE_ONCE(ha_mon->env_store[env], READ_ONCE(ha_mon->env_store[env]) - value);
}
#if HA_TIMER_TYPE == HA_TIMER_WHEEL
static void ha_monitor_timer_callback(struct timer_list *timer)
{
struct ha_monitor *ha_mon = container_of(timer, struct ha_monitor, timer);
__ha_monitor_timer_callback(ha_mon);
}
static inline void ha_setup_timer(struct ha_monitor *ha_mon)
{
int mode = 0;
if (RV_MON_TYPE == RV_MON_PER_CPU)
mode |= TIMER_PINNED;
timer_setup(&ha_mon->timer, ha_monitor_timer_callback, mode);
}
static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
mod_timer(&ha_mon->timer, get_jiffies_64() + expire - passed);
}
static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
ha_start_timer_jiffy(ha_mon, ENV_MAX_STORED,
nsecs_to_jiffies(expire - passed + TICK_NSEC - 1), time_ns);
}
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
{
return timer_delete(&ha_mon->timer);
}
#elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
static enum hrtimer_restart ha_monitor_timer_callback(struct hrtimer *hrtimer)
{
struct ha_monitor *ha_mon = container_of(hrtimer, struct ha_monitor, hrtimer);
__ha_monitor_timer_callback(ha_mon);
return HRTIMER_NORESTART;
}
static inline void ha_setup_timer(struct ha_monitor *ha_mon)
{
hrtimer_setup(&ha_mon->hrtimer, ha_monitor_timer_callback,
CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
}
static inline void ha_start_timer_ns(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
int mode = HRTIMER_MODE_REL_HARD;
u64 passed = ha_invariant_passed_ns(ha_mon, env, expire, time_ns);
if (RV_MON_TYPE == RV_MON_PER_CPU)
mode |= HRTIMER_MODE_PINNED;
hrtimer_start(&ha_mon->hrtimer, ns_to_ktime(expire - passed), mode);
}
static inline void ha_start_timer_jiffy(struct ha_monitor *ha_mon, enum envs env,
u64 expire, u64 time_ns)
{
u64 passed = ha_invariant_passed_jiffy(ha_mon, env, expire, time_ns);
ha_start_timer_ns(ha_mon, ENV_MAX_STORED,
jiffies_to_nsecs(expire - passed), time_ns);
}
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
{
return hrtimer_try_to_cancel(&ha_mon->hrtimer) == 1;
}
#else
static inline void ha_setup_timer(struct ha_monitor *ha_mon) { }
static inline bool ha_cancel_timer(struct ha_monitor *ha_mon)
{
return false;
}
#endif
#endif