1 /*
2  * trace context switch
3  *
4  * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
5  *
6  */
7 #include <linux/module.h>
8 #include <linux/fs.h>
9 #include <linux/debugfs.h>
10 #include <linux/kallsyms.h>
11 #include <linux/uaccess.h>
12 #include <linux/ftrace.h>
13 #include <trace/sched.h>
14 
15 #include "trace.h"
16 
17 static struct trace_array	*ctx_trace;
18 static int __read_mostly	tracer_enabled;
19 static int			sched_ref;
20 static DEFINE_MUTEX(sched_register_mutex);
21 static int			sched_stopped;
22 
23 static void
24 probe_sched_switch(struct rq *__rq, struct task_struct *prev,
25 			struct task_struct *next)
26 {
27 	struct trace_array_cpu *data;
28 	unsigned long flags;
29 	int cpu;
30 	int pc;
31 
32 	if (!sched_ref || sched_stopped)
33 		return;
34 
35 	tracing_record_cmdline(prev);
36 	tracing_record_cmdline(next);
37 
38 	if (!tracer_enabled)
39 		return;
40 
41 	pc = preempt_count();
42 	local_irq_save(flags);
43 	cpu = raw_smp_processor_id();
44 	data = ctx_trace->data[cpu];
45 
46 	if (likely(!atomic_read(&data->disabled)))
47 		tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc);
48 
49 	local_irq_restore(flags);
50 }
51 
52 static void
53 probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success)
54 {
55 	struct trace_array_cpu *data;
56 	unsigned long flags;
57 	int cpu, pc;
58 
59 	if (!likely(tracer_enabled))
60 		return;
61 
62 	pc = preempt_count();
63 	tracing_record_cmdline(current);
64 
65 	if (sched_stopped)
66 		return;
67 
68 	local_irq_save(flags);
69 	cpu = raw_smp_processor_id();
70 	data = ctx_trace->data[cpu];
71 
72 	if (likely(!atomic_read(&data->disabled)))
73 		tracing_sched_wakeup_trace(ctx_trace, wakee, current,
74 					   flags, pc);
75 
76 	local_irq_restore(flags);
77 }
78 
79 static int tracing_sched_register(void)
80 {
81 	int ret;
82 
83 	ret = register_trace_sched_wakeup(probe_sched_wakeup);
84 	if (ret) {
85 		pr_info("wakeup trace: Couldn't activate tracepoint"
86 			" probe to kernel_sched_wakeup\n");
87 		return ret;
88 	}
89 
90 	ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
91 	if (ret) {
92 		pr_info("wakeup trace: Couldn't activate tracepoint"
93 			" probe to kernel_sched_wakeup_new\n");
94 		goto fail_deprobe;
95 	}
96 
97 	ret = register_trace_sched_switch(probe_sched_switch);
98 	if (ret) {
99 		pr_info("sched trace: Couldn't activate tracepoint"
100 			" probe to kernel_sched_switch\n");
101 		goto fail_deprobe_wake_new;
102 	}
103 
104 	return ret;
105 fail_deprobe_wake_new:
106 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
107 fail_deprobe:
108 	unregister_trace_sched_wakeup(probe_sched_wakeup);
109 	return ret;
110 }
111 
112 static void tracing_sched_unregister(void)
113 {
114 	unregister_trace_sched_switch(probe_sched_switch);
115 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
116 	unregister_trace_sched_wakeup(probe_sched_wakeup);
117 }
118 
119 static void tracing_start_sched_switch(void)
120 {
121 	mutex_lock(&sched_register_mutex);
122 	if (!(sched_ref++))
123 		tracing_sched_register();
124 	mutex_unlock(&sched_register_mutex);
125 }
126 
127 static void tracing_stop_sched_switch(void)
128 {
129 	mutex_lock(&sched_register_mutex);
130 	if (!(--sched_ref))
131 		tracing_sched_unregister();
132 	mutex_unlock(&sched_register_mutex);
133 }
134 
135 void tracing_start_cmdline_record(void)
136 {
137 	tracing_start_sched_switch();
138 }
139 
140 void tracing_stop_cmdline_record(void)
141 {
142 	tracing_stop_sched_switch();
143 }
144 
145 /**
146  * tracing_start_sched_switch_record - start tracing context switches
147  *
148  * Turns on context switch tracing for a tracer.
149  */
150 void tracing_start_sched_switch_record(void)
151 {
152 	if (unlikely(!ctx_trace)) {
153 		WARN_ON(1);
154 		return;
155 	}
156 
157 	tracing_start_sched_switch();
158 
159 	mutex_lock(&sched_register_mutex);
160 	tracer_enabled++;
161 	mutex_unlock(&sched_register_mutex);
162 }
163 
164 /**
165  * tracing_stop_sched_switch_record - start tracing context switches
166  *
167  * Turns off context switch tracing for a tracer.
168  */
169 void tracing_stop_sched_switch_record(void)
170 {
171 	mutex_lock(&sched_register_mutex);
172 	tracer_enabled--;
173 	WARN_ON(tracer_enabled < 0);
174 	mutex_unlock(&sched_register_mutex);
175 
176 	tracing_stop_sched_switch();
177 }
178 
179 /**
180  * tracing_sched_switch_assign_trace - assign a trace array for ctx switch
181  * @tr: trace array pointer to assign
182  *
183  * Some tracers might want to record the context switches in their
184  * trace. This function lets those tracers assign the trace array
185  * to use.
186  */
187 void tracing_sched_switch_assign_trace(struct trace_array *tr)
188 {
189 	ctx_trace = tr;
190 }
191 
192 static void stop_sched_trace(struct trace_array *tr)
193 {
194 	tracing_stop_sched_switch_record();
195 }
196 
197 static int sched_switch_trace_init(struct trace_array *tr)
198 {
199 	ctx_trace = tr;
200 	tracing_reset_online_cpus(tr);
201 	tracing_start_sched_switch_record();
202 	return 0;
203 }
204 
205 static void sched_switch_trace_reset(struct trace_array *tr)
206 {
207 	if (sched_ref)
208 		stop_sched_trace(tr);
209 }
210 
211 static void sched_switch_trace_start(struct trace_array *tr)
212 {
213 	sched_stopped = 0;
214 }
215 
216 static void sched_switch_trace_stop(struct trace_array *tr)
217 {
218 	sched_stopped = 1;
219 }
220 
221 static struct tracer sched_switch_trace __read_mostly =
222 {
223 	.name		= "sched_switch",
224 	.init		= sched_switch_trace_init,
225 	.reset		= sched_switch_trace_reset,
226 	.start		= sched_switch_trace_start,
227 	.stop		= sched_switch_trace_stop,
228 	.wait_pipe	= poll_wait_pipe,
229 #ifdef CONFIG_FTRACE_SELFTEST
230 	.selftest    = trace_selftest_startup_sched_switch,
231 #endif
232 };
233 
234 __init static int init_sched_switch_trace(void)
235 {
236 	return register_tracer(&sched_switch_trace);
237 }
238 device_initcall(init_sched_switch_trace);
239 
240