Hybrid automata monitors may start timers, depending on the model, these
may remain active on an exiting task and cause false positives or even
access freed memory.

Add an enable/disable hook in the HA code, currently only populated by
the per-task handler for registration and deregistration.
This hooks to the sched_process_exit event and ensures the timer is
stopped for every exiting task. The handler is enabled automatically but
may be disabled, for instance if the monitor uses the event for another
purpose (but should still manually ensure timers are stopped).

Fixes: f5587d1b6ec9 ("rv: Add Hybrid Automata monitor type")
Reviewed-by: Nam Cao <[email protected]>
Signed-off-by: Gabriele Monaco <[email protected]>
---
 include/rv/ha_monitor.h                       | 60 +++++++++++++++++++
 kernel/trace/rv/monitors/nomiss/nomiss.c      |  4 +-
 kernel/trace/rv/monitors/opid/opid.c          |  4 +-
 kernel/trace/rv/monitors/stall/stall.c        |  4 +-
 .../rvgen/rvgen/templates/dot2k/main.c        |  4 +-
 5 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/include/rv/ha_monitor.h b/include/rv/ha_monitor.h
index bd8705556..4002b5247 100644
--- a/include/rv/ha_monitor.h
+++ b/include/rv/ha_monitor.h
@@ -28,6 +28,7 @@ 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 inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon);
 static bool ha_monitor_handle_constraint(struct da_monitor *da_mon,
                                         enum states curr_state,
                                         enum events event,
@@ -37,6 +38,26 @@ static bool ha_monitor_handle_constraint(struct da_monitor 
*da_mon,
 #define da_monitor_init_hook ha_monitor_init_env
 #define da_monitor_reset_hook ha_monitor_reset_env
 
+#if !defined(HA_SKIP_AUTO_CLEANUP) && RV_MON_TYPE == RV_MON_PER_TASK
+/*
+ * Automatic cleanup handlers for per-task HA monitors, only skip if you know
+ * what you are doing (e.g. you want to implement cleanup manually in another
+ * handler doing more things).
+ */
+static void ha_handle_sched_process_exit(void *data, struct task_struct *p,
+                                        bool group_dead);
+
+#define ha_monitor_enable_hook()                                             \
+       rv_attach_trace_probe(__stringify(MONITOR_NAME), sched_process_exit, \
+                             ha_handle_sched_process_exit)
+#define ha_monitor_disable_hook()                                            \
+       rv_detach_trace_probe(__stringify(MONITOR_NAME), sched_process_exit, \
+                             ha_handle_sched_process_exit)
+#else
+#define ha_monitor_enable_hook() ((void)0)
+#define ha_monitor_disable_hook() ((void)0)
+#endif
+
 #include <rv/da_monitor.h>
 #include <linux/seq_buf.h>
 
@@ -115,6 +136,22 @@ static enum hrtimer_restart 
ha_monitor_timer_callback(struct hrtimer *hrtimer);
 #define ha_get_ns() 0
 #endif /* HA_CLK_NS */
 
+static int ha_monitor_init(void)
+{
+       int ret;
+
+       ret = da_monitor_init();
+       if (ret == 0)
+               ha_monitor_enable_hook();
+       return ret;
+}
+
+static void ha_monitor_destroy(void)
+{
+       ha_monitor_disable_hook();
+       da_monitor_destroy();
+}
+
 /* Should be supplied by the monitor */
 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,
@@ -200,6 +237,20 @@ static inline void ha_trace_error_env(struct ha_monitor 
*ha_mon,
 {
        CONCATENATE(trace_error_env_, MONITOR_NAME)(id, curr_state, event, env);
 }
+
+#if !defined(HA_SKIP_AUTO_CLEANUP) && RV_MON_TYPE == RV_MON_PER_TASK
+static void ha_handle_sched_process_exit(void *data, struct task_struct *p,
+                                        bool group_dead)
+{
+       struct da_monitor *da_mon = da_get_monitor(p);
+
+       if (likely(da_monitoring(da_mon))) {
+               da_monitor_reset(da_mon);
+               ha_cancel_timer_sync(to_ha_monitor(da_mon));
+       }
+}
+#endif
+
 #endif /* RV_MON_TYPE */
 
 /*
@@ -412,6 +463,10 @@ static inline bool ha_cancel_timer(struct ha_monitor 
*ha_mon)
 {
        return timer_delete(&ha_mon->timer);
 }
+static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon)
+{
+       timer_delete_sync(&ha_mon->timer);
+}
 #elif HA_TIMER_TYPE == HA_TIMER_HRTIMER
 /*
  * Helper functions to handle the monitor timer.
@@ -463,6 +518,10 @@ static inline bool ha_cancel_timer(struct ha_monitor 
*ha_mon)
 {
        return hrtimer_try_to_cancel(&ha_mon->hrtimer) == 1;
 }
+static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon)
+{
+       hrtimer_cancel(&ha_mon->hrtimer);
+}
 #else /* HA_TIMER_NONE */
 /*
  * Start function is intentionally not defined, monitors using timers must
@@ -473,6 +532,7 @@ static inline bool ha_cancel_timer(struct ha_monitor 
*ha_mon)
 {
        return false;
 }
+static inline void ha_cancel_timer_sync(struct ha_monitor *ha_mon) { }
 #endif
 
 #endif
diff --git a/kernel/trace/rv/monitors/nomiss/nomiss.c 
b/kernel/trace/rv/monitors/nomiss/nomiss.c
index 31f90f363..8ead8783c 100644
--- a/kernel/trace/rv/monitors/nomiss/nomiss.c
+++ b/kernel/trace/rv/monitors/nomiss/nomiss.c
@@ -227,7 +227,7 @@ static int enable_nomiss(void)
 {
        int retval;
 
-       retval = da_monitor_init();
+       retval = ha_monitor_init();
        if (retval)
                return retval;
 
@@ -263,7 +263,7 @@ static void disable_nomiss(void)
        rv_detach_trace_probe("nomiss", sched_switch, handle_sched_switch);
        rv_detach_trace_probe("nomiss", sched_wakeup, handle_sched_wakeup);
 
-       da_monitor_destroy();
+       ha_monitor_destroy();
 }
 
 static struct rv_monitor rv_this = {
diff --git a/kernel/trace/rv/monitors/opid/opid.c 
b/kernel/trace/rv/monitors/opid/opid.c
index 4594c7c46..2922318c6 100644
--- a/kernel/trace/rv/monitors/opid/opid.c
+++ b/kernel/trace/rv/monitors/opid/opid.c
@@ -73,7 +73,7 @@ static int enable_opid(void)
 {
        int retval;
 
-       retval = da_monitor_init();
+       retval = ha_monitor_init();
        if (retval)
                return retval;
 
@@ -90,7 +90,7 @@ static void disable_opid(void)
        rv_detach_trace_probe("opid", sched_set_need_resched_tp, 
handle_sched_need_resched);
        rv_detach_trace_probe("opid", sched_waking, handle_sched_waking);
 
-       da_monitor_destroy();
+       ha_monitor_destroy();
 }
 
 /*
diff --git a/kernel/trace/rv/monitors/stall/stall.c 
b/kernel/trace/rv/monitors/stall/stall.c
index 9ccfda6b0..3c38fb1a0 100644
--- a/kernel/trace/rv/monitors/stall/stall.c
+++ b/kernel/trace/rv/monitors/stall/stall.c
@@ -103,7 +103,7 @@ static int enable_stall(void)
 {
        int retval;
 
-       retval = da_monitor_init();
+       retval = ha_monitor_init();
        if (retval)
                return retval;
 
@@ -120,7 +120,7 @@ static void disable_stall(void)
        rv_detach_trace_probe("stall", sched_switch, handle_sched_switch);
        rv_detach_trace_probe("stall", sched_wakeup, handle_sched_wakeup);
 
-       da_monitor_destroy();
+       ha_monitor_destroy();
 }
 
 static struct rv_monitor rv_this = {
diff --git a/tools/verification/rvgen/rvgen/templates/dot2k/main.c 
b/tools/verification/rvgen/rvgen/templates/dot2k/main.c
index bf0999f66..889446760 100644
--- a/tools/verification/rvgen/rvgen/templates/dot2k/main.c
+++ b/tools/verification/rvgen/rvgen/templates/dot2k/main.c
@@ -35,7 +35,7 @@ static int enable_%%MODEL_NAME%%(void)
 {
        int retval;
 
-       retval = da_monitor_init();
+       retval = %%MONITOR_CLASS%%_monitor_init();
        if (retval)
                return retval;
 
@@ -50,7 +50,7 @@ static void disable_%%MODEL_NAME%%(void)
 
 %%TRACEPOINT_DETACH%%
 
-       da_monitor_destroy();
+       %%MONITOR_CLASS%%_monitor_destroy();
 }
 
 /*
-- 
2.54.0


Reply via email to