xref: /linux/kernel/trace/rv/monitors/stall/stall.c (revision fdbfee9fc56e13a1307868829d438ad66ab308a4)
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