1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/ftrace.h>
3 #include <linux/tracepoint.h>
4 #include <linux/kernel.h>
5 #include <linux/module.h>
6 #include <linux/init.h>
7 #include <linux/rv.h>
8 #include <rv/instrumentation.h>
9
10 #define MODULE_NAME "stall"
11
12 #include <trace/events/sched.h>
13 #include <rv_trace.h>
14
15 #define RV_MON_TYPE RV_MON_PER_TASK
16 #define HA_TIMER_TYPE HA_TIMER_WHEEL
17 #include "stall.h"
18 #include <rv/ha_monitor.h>
19
20 static u64 threshold_jiffies = 1000;
21 module_param(threshold_jiffies, ullong, 0644);
22
ha_get_env(struct ha_monitor * ha_mon,enum envs_stall env,u64 time_ns)23 static u64 ha_get_env(struct ha_monitor *ha_mon, enum envs_stall env, u64 time_ns)
24 {
25 if (env == clk_stall)
26 return ha_get_clk_jiffy(ha_mon, env);
27 return ENV_INVALID_VALUE;
28 }
29
ha_reset_env(struct ha_monitor * ha_mon,enum envs_stall env,u64 time_ns)30 static void ha_reset_env(struct ha_monitor *ha_mon, enum envs_stall env, u64 time_ns)
31 {
32 if (env == clk_stall)
33 ha_reset_clk_jiffy(ha_mon, env);
34 }
35
ha_verify_invariants(struct ha_monitor * ha_mon,enum states curr_state,enum events event,enum states next_state,u64 time_ns)36 static inline bool ha_verify_invariants(struct ha_monitor *ha_mon,
37 enum states curr_state, enum events event,
38 enum states next_state, u64 time_ns)
39 {
40 if (curr_state == enqueued_stall)
41 return ha_check_invariant_jiffy(ha_mon, clk_stall, time_ns);
42 return true;
43 }
44
ha_verify_guards(struct ha_monitor * ha_mon,enum states curr_state,enum events event,enum states next_state,u64 time_ns)45 static inline bool ha_verify_guards(struct ha_monitor *ha_mon,
46 enum states curr_state, enum events event,
47 enum states next_state, u64 time_ns)
48 {
49 bool res = true;
50
51 if (curr_state == dequeued_stall && event == sched_wakeup_stall)
52 ha_reset_env(ha_mon, clk_stall, time_ns);
53 else if (curr_state == running_stall && event == sched_switch_preempt_stall)
54 ha_reset_env(ha_mon, clk_stall, time_ns);
55 return res;
56 }
57
ha_setup_invariants(struct ha_monitor * ha_mon,enum states curr_state,enum events event,enum states next_state,u64 time_ns)58 static inline void ha_setup_invariants(struct ha_monitor *ha_mon,
59 enum states curr_state, enum events event,
60 enum states next_state, u64 time_ns)
61 {
62 if (next_state == curr_state)
63 return;
64 if (next_state == enqueued_stall)
65 ha_start_timer_jiffy(ha_mon, clk_stall, threshold_jiffies, time_ns);
66 else if (curr_state == enqueued_stall)
67 ha_cancel_timer(ha_mon);
68 }
69
ha_verify_constraint(struct ha_monitor * ha_mon,enum states curr_state,enum events event,enum states next_state,u64 time_ns)70 static bool ha_verify_constraint(struct ha_monitor *ha_mon,
71 enum states curr_state, enum events event,
72 enum states next_state, u64 time_ns)
73 {
74 if (!ha_verify_invariants(ha_mon, curr_state, event, next_state, time_ns))
75 return false;
76
77 if (!ha_verify_guards(ha_mon, curr_state, event, next_state, time_ns))
78 return false;
79
80 ha_setup_invariants(ha_mon, curr_state, event, next_state, time_ns);
81
82 return true;
83 }
84
handle_sched_switch(void * data,bool preempt,struct task_struct * prev,struct task_struct * next,unsigned int prev_state)85 static void handle_sched_switch(void *data, bool preempt,
86 struct task_struct *prev,
87 struct task_struct *next,
88 unsigned int prev_state)
89 {
90 if (!preempt && prev_state != TASK_RUNNING)
91 da_handle_start_event(prev, sched_switch_wait_stall);
92 else
93 da_handle_event(prev, sched_switch_preempt_stall);
94 da_handle_event(next, sched_switch_in_stall);
95 }
96
handle_sched_wakeup(void * data,struct task_struct * p)97 static void handle_sched_wakeup(void *data, struct task_struct *p)
98 {
99 da_handle_event(p, sched_wakeup_stall);
100 }
101
enable_stall(void)102 static int enable_stall(void)
103 {
104 int retval;
105
106 retval = da_monitor_init();
107 if (retval)
108 return retval;
109
110 rv_attach_trace_probe("stall", sched_switch, handle_sched_switch);
111 rv_attach_trace_probe("stall", sched_wakeup, handle_sched_wakeup);
112
113 return 0;
114 }
115
disable_stall(void)116 static void disable_stall(void)
117 {
118 rv_this.enabled = 0;
119
120 rv_detach_trace_probe("stall", sched_switch, handle_sched_switch);
121 rv_detach_trace_probe("stall", sched_wakeup, handle_sched_wakeup);
122
123 da_monitor_destroy();
124 }
125
126 static struct rv_monitor rv_this = {
127 .name = "stall",
128 .description = "identify tasks stalled for longer than a threshold.",
129 .enable = enable_stall,
130 .disable = disable_stall,
131 .reset = da_monitor_reset_all,
132 .enabled = 0,
133 };
134
register_stall(void)135 static int __init register_stall(void)
136 {
137 return rv_register_monitor(&rv_this, NULL);
138 }
139
unregister_stall(void)140 static void __exit unregister_stall(void)
141 {
142 rv_unregister_monitor(&rv_this);
143 }
144
145 module_init(register_stall);
146 module_exit(unregister_stall);
147
148 MODULE_LICENSE("GPL");
149 MODULE_AUTHOR("Gabriele Monaco <gmonaco@redhat.com>");
150 MODULE_DESCRIPTION("stall: identify tasks stalled for longer than a threshold.");
151