Hook the edge-stat facility into the DA/HA layer, so that any per-cpu automaton monitor also reports how long it lingers in each state.
Account a transition as soon as its state change is committed. An HA constraint that rejects the transition can therefore reset the monitor without leaving an accounting window for nested events. Signed-off-by: Tobias Schaffner <[email protected]> --- .../trace/rv/runtime-verification.rst | 28 +++++++++++++++++++ include/linux/rv.h | 2 +- include/rv/da_monitor.h | 16 +++++++++-- include/rv/ha_monitor.h | 2 +- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Documentation/trace/rv/runtime-verification.rst b/Documentation/trace/rv/runtime-verification.rst index c700dde9259c..012bb037c323 100644 --- a/Documentation/trace/rv/runtime-verification.rst +++ b/Documentation/trace/rv/runtime-verification.rst @@ -229,3 +229,31 @@ For example:: nop [panic] printk + +**monitors/MONITOR/stats** + +Present only when the kernel is built with CONFIG_RV_EDGE_STAT=y and *MONITOR* +is a per-cpu deterministic or hybrid automaton monitor. The file exists while +the monitor is enabled and reports how long the automaton dwells in each state +before leaving it, timed with local_clock() and accounted per outgoing edge and +per CPU. + +- The first line is a header naming the columns. +- Each following line describes one edge on one CPU:: + + cpu edge label valid count sum_ns max_ns + + *label* is "state:event", *valid* is 1 if the model defines the edge and 0 + otherwise, *count* is the number of times the transition was committed, and + *sum_ns* and *max_ns* are the total and worst dwell in nanoseconds. An + invalid edge always reports a zero count. + +The counters are reset each time the monitor is enabled. + +For example:: + + # cat monitors/wip/stats + # cpu edge label valid count sum_ns max_ns + 0 0 preemptive:preempt_disable 1 4210 95501200 183200 + 0 1 preemptive:preempt_enable 0 0 0 0 + 0 4 non_preemptive:preempt_enable 1 4208 3812900 42600 diff --git a/include/linux/rv.h b/include/linux/rv.h index 1dc7c45f476c..6c5c2fa7dd68 100644 --- a/include/linux/rv.h +++ b/include/linux/rv.h @@ -33,7 +33,7 @@ */ struct da_monitor { bool monitoring; - unsigned int curr_state; + da_state_t curr_state; }; #ifdef CONFIG_RV_LTL_MONITOR diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 6fc5ef8142ac..c8f7497800d0 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -17,6 +17,7 @@ #include <rv/automata.h> #include <linux/rv.h> #include <rv/kunit.h> +#include <rv/edge_stat.h> #include <linux/stringify.h> #include <linux/bug.h> #include <linux/sched.h> @@ -113,7 +114,7 @@ static inline void da_monitor_reset(struct da_monitor *da_mon) */ static inline void da_monitor_start(struct da_monitor *da_mon) { - da_mon->curr_state = model_get_initial_state(); + da_mon->curr_state = da_state_entered(model_get_initial_state()); da_monitor_init_hook(da_mon); /* Pairs with smp_load_acquire in da_monitoring(). */ smp_store_release(&da_mon->monitoring, 1); @@ -276,6 +277,11 @@ static inline void da_monitor_reset_state_all(void) */ static inline int da_monitor_init(void) { + int retval = rv_edge_stats_create(rv_this.root_d); + + if (retval) + return retval; + da_monitor_reset_state_all(); return 0; } @@ -287,6 +293,7 @@ static inline void da_monitor_destroy(void) { da_monitor_reset_all(); da_monitor_sync_hook(); + rv_edge_stats_remove(); } #ifndef da_implicit_guard @@ -689,9 +696,11 @@ static inline void da_trace_error(struct da_monitor *da_mon, static inline bool da_event(struct da_monitor *da_mon, enum events event, da_id_type id) { enum states curr_state, next_state; + da_state_t old; - curr_state = READ_ONCE(da_mon->curr_state); + old = READ_ONCE(da_mon->curr_state); for (int i = 0; i < MAX_DA_RETRY_RACING_EVENTS; i++) { + curr_state = da_state_of(old); next_state = model_get_next_state(curr_state, event); if (next_state == INVALID_STATE) { react(curr_state, event); @@ -699,7 +708,8 @@ static inline bool da_event(struct da_monitor *da_mon, enum events event, da_id_ model_get_event_name(event), id); return false; } - if (likely(try_cmpxchg(&da_mon->curr_state, &curr_state, next_state))) { + if (likely(da_state_try_commit(&da_mon->curr_state, &old, + next_state, event))) { if (!da_monitor_event_hook(da_mon, curr_state, event, next_state, id)) return false; da_trace_event(da_mon, model_get_state_name(curr_state), diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h index 6e1c7fe5449a..78d1bb80aefd 100644 --- a/include/rv/ha_monitor.h +++ b/include/rv/ha_monitor.h @@ -312,7 +312,7 @@ static inline void __ha_monitor_timer_callback(struct ha_monitor *ha_mon) if (unlikely(READ_ONCE(ha_mon_destroying))) return; /* Ensure consistent curr_state if we race with da_monitor_reset */ - curr_state = smp_load_acquire(&ha_mon->da_mon.curr_state); + curr_state = da_state_of(smp_load_acquire(&ha_mon->da_mon.curr_state)); if (unlikely(!da_monitor_handling_event(&ha_mon->da_mon))) return; -- 2.53.0
