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


Reply via email to