135e8e302SSteven Rostedt /*
235e8e302SSteven Rostedt  * trace context switch
335e8e302SSteven Rostedt  *
435e8e302SSteven Rostedt  * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
535e8e302SSteven Rostedt  *
635e8e302SSteven Rostedt  */
735e8e302SSteven Rostedt #include <linux/module.h>
835e8e302SSteven Rostedt #include <linux/fs.h>
935e8e302SSteven Rostedt #include <linux/debugfs.h>
1035e8e302SSteven Rostedt #include <linux/kallsyms.h>
1135e8e302SSteven Rostedt #include <linux/uaccess.h>
1235e8e302SSteven Rostedt #include <linux/ftrace.h>
13b07c3f19SMathieu Desnoyers #include <trace/sched.h>
1435e8e302SSteven Rostedt 
1535e8e302SSteven Rostedt #include "trace.h"
1635e8e302SSteven Rostedt 
1735e8e302SSteven Rostedt static struct trace_array	*ctx_trace;
1835e8e302SSteven Rostedt static int __read_mostly	tracer_enabled;
19efade6e7SFrederic Weisbecker static int			sched_ref;
20efade6e7SFrederic Weisbecker static DEFINE_MUTEX(sched_register_mutex);
2135e8e302SSteven Rostedt 
22e309b41dSIngo Molnar static void
23b07c3f19SMathieu Desnoyers probe_sched_switch(struct rq *__rq, struct task_struct *prev,
245b82a1b0SMathieu Desnoyers 			struct task_struct *next)
2535e8e302SSteven Rostedt {
2635e8e302SSteven Rostedt 	struct trace_array_cpu *data;
2735e8e302SSteven Rostedt 	unsigned long flags;
2835e8e302SSteven Rostedt 	int cpu;
2938697053SSteven Rostedt 	int pc;
3035e8e302SSteven Rostedt 
31efade6e7SFrederic Weisbecker 	if (!sched_ref)
32b07c3f19SMathieu Desnoyers 		return;
33b07c3f19SMathieu Desnoyers 
3441bc8144SSteven Rostedt 	tracing_record_cmdline(prev);
3541bc8144SSteven Rostedt 	tracing_record_cmdline(next);
3641bc8144SSteven Rostedt 
3735e8e302SSteven Rostedt 	if (!tracer_enabled)
3835e8e302SSteven Rostedt 		return;
3935e8e302SSteven Rostedt 
4038697053SSteven Rostedt 	pc = preempt_count();
4118cef379SSteven Rostedt 	local_irq_save(flags);
4235e8e302SSteven Rostedt 	cpu = raw_smp_processor_id();
43b07c3f19SMathieu Desnoyers 	data = ctx_trace->data[cpu];
4435e8e302SSteven Rostedt 
453ea2e6d7SSteven Rostedt 	if (likely(!atomic_read(&data->disabled)))
4638697053SSteven Rostedt 		tracing_sched_switch_trace(ctx_trace, data, prev, next, flags, pc);
4735e8e302SSteven Rostedt 
4818cef379SSteven Rostedt 	local_irq_restore(flags);
4935e8e302SSteven Rostedt }
5035e8e302SSteven Rostedt 
515b82a1b0SMathieu Desnoyers static void
52b07c3f19SMathieu Desnoyers probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee)
535b82a1b0SMathieu Desnoyers {
5457422797SIngo Molnar 	struct trace_array_cpu *data;
5557422797SIngo Molnar 	unsigned long flags;
5638697053SSteven Rostedt 	int cpu, pc;
5757422797SIngo Molnar 
58b07c3f19SMathieu Desnoyers 	if (!likely(tracer_enabled))
5957422797SIngo Molnar 		return;
6057422797SIngo Molnar 
6138697053SSteven Rostedt 	pc = preempt_count();
62b07c3f19SMathieu Desnoyers 	tracing_record_cmdline(current);
63d9af56fbSIngo Molnar 
6457422797SIngo Molnar 	local_irq_save(flags);
6557422797SIngo Molnar 	cpu = raw_smp_processor_id();
66b07c3f19SMathieu Desnoyers 	data = ctx_trace->data[cpu];
6757422797SIngo Molnar 
683ea2e6d7SSteven Rostedt 	if (likely(!atomic_read(&data->disabled)))
69b07c3f19SMathieu Desnoyers 		tracing_sched_wakeup_trace(ctx_trace, data, wakee, current,
7038697053SSteven Rostedt 					   flags, pc);
7157422797SIngo Molnar 
7257422797SIngo Molnar 	local_irq_restore(flags);
7357422797SIngo Molnar }
7457422797SIngo Molnar 
75e309b41dSIngo Molnar static void sched_switch_reset(struct trace_array *tr)
7635e8e302SSteven Rostedt {
7735e8e302SSteven Rostedt 	int cpu;
7835e8e302SSteven Rostedt 
79750ed1a4SIngo Molnar 	tr->time_start = ftrace_now(tr->cpu);
8035e8e302SSteven Rostedt 
8135e8e302SSteven Rostedt 	for_each_online_cpu(cpu)
823928a8a2SSteven Rostedt 		tracing_reset(tr, cpu);
8335e8e302SSteven Rostedt }
8435e8e302SSteven Rostedt 
855b82a1b0SMathieu Desnoyers static int tracing_sched_register(void)
865b82a1b0SMathieu Desnoyers {
875b82a1b0SMathieu Desnoyers 	int ret;
885b82a1b0SMathieu Desnoyers 
89b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_wakeup(probe_sched_wakeup);
905b82a1b0SMathieu Desnoyers 	if (ret) {
91b07c3f19SMathieu Desnoyers 		pr_info("wakeup trace: Couldn't activate tracepoint"
925b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_wakeup\n");
935b82a1b0SMathieu Desnoyers 		return ret;
945b82a1b0SMathieu Desnoyers 	}
955b82a1b0SMathieu Desnoyers 
96b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
975b82a1b0SMathieu Desnoyers 	if (ret) {
98b07c3f19SMathieu Desnoyers 		pr_info("wakeup trace: Couldn't activate tracepoint"
995b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_wakeup_new\n");
1005b82a1b0SMathieu Desnoyers 		goto fail_deprobe;
1015b82a1b0SMathieu Desnoyers 	}
1025b82a1b0SMathieu Desnoyers 
103b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_switch(probe_sched_switch);
1045b82a1b0SMathieu Desnoyers 	if (ret) {
105b07c3f19SMathieu Desnoyers 		pr_info("sched trace: Couldn't activate tracepoint"
1065b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_schedule\n");
1075b82a1b0SMathieu Desnoyers 		goto fail_deprobe_wake_new;
1085b82a1b0SMathieu Desnoyers 	}
1095b82a1b0SMathieu Desnoyers 
1105b82a1b0SMathieu Desnoyers 	return ret;
1115b82a1b0SMathieu Desnoyers fail_deprobe_wake_new:
112b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
1135b82a1b0SMathieu Desnoyers fail_deprobe:
114b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup(probe_sched_wakeup);
1155b82a1b0SMathieu Desnoyers 	return ret;
1165b82a1b0SMathieu Desnoyers }
1175b82a1b0SMathieu Desnoyers 
1185b82a1b0SMathieu Desnoyers static void tracing_sched_unregister(void)
1195b82a1b0SMathieu Desnoyers {
120b07c3f19SMathieu Desnoyers 	unregister_trace_sched_switch(probe_sched_switch);
121b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
122b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup(probe_sched_wakeup);
1235b82a1b0SMathieu Desnoyers }
1245b82a1b0SMathieu Desnoyers 
125f2252935SIngo Molnar static void tracing_start_sched_switch(void)
1265b82a1b0SMathieu Desnoyers {
127efade6e7SFrederic Weisbecker 	mutex_lock(&sched_register_mutex);
128e168e051SSteven Rostedt 	if (!(sched_ref++))
1295b82a1b0SMathieu Desnoyers 		tracing_sched_register();
130efade6e7SFrederic Weisbecker 	mutex_unlock(&sched_register_mutex);
1315b82a1b0SMathieu Desnoyers }
1325b82a1b0SMathieu Desnoyers 
133f2252935SIngo Molnar static void tracing_stop_sched_switch(void)
1345b82a1b0SMathieu Desnoyers {
135efade6e7SFrederic Weisbecker 	mutex_lock(&sched_register_mutex);
136e168e051SSteven Rostedt 	if (!(--sched_ref))
1375b82a1b0SMathieu Desnoyers 		tracing_sched_unregister();
138efade6e7SFrederic Weisbecker 	mutex_unlock(&sched_register_mutex);
1395b82a1b0SMathieu Desnoyers }
1405b82a1b0SMathieu Desnoyers 
14141bc8144SSteven Rostedt void tracing_start_cmdline_record(void)
14241bc8144SSteven Rostedt {
14341bc8144SSteven Rostedt 	tracing_start_sched_switch();
14441bc8144SSteven Rostedt }
14541bc8144SSteven Rostedt 
14641bc8144SSteven Rostedt void tracing_stop_cmdline_record(void)
14741bc8144SSteven Rostedt {
14841bc8144SSteven Rostedt 	tracing_stop_sched_switch();
14941bc8144SSteven Rostedt }
15041bc8144SSteven Rostedt 
15175f5c47dSSteven Rostedt /**
152e168e051SSteven Rostedt  * tracing_start_sched_switch_record - start tracing context switches
153e168e051SSteven Rostedt  *
154e168e051SSteven Rostedt  * Turns on context switch tracing for a tracer.
155e168e051SSteven Rostedt  */
156e168e051SSteven Rostedt void tracing_start_sched_switch_record(void)
157e168e051SSteven Rostedt {
158e168e051SSteven Rostedt 	if (unlikely(!ctx_trace)) {
159e168e051SSteven Rostedt 		WARN_ON(1);
160e168e051SSteven Rostedt 		return;
161e168e051SSteven Rostedt 	}
162e168e051SSteven Rostedt 
163e168e051SSteven Rostedt 	tracing_start_sched_switch();
164e168e051SSteven Rostedt 
165e168e051SSteven Rostedt 	mutex_lock(&sched_register_mutex);
166e168e051SSteven Rostedt 	tracer_enabled++;
167e168e051SSteven Rostedt 	mutex_unlock(&sched_register_mutex);
168e168e051SSteven Rostedt }
169e168e051SSteven Rostedt 
170e168e051SSteven Rostedt /**
171e168e051SSteven Rostedt  * tracing_stop_sched_switch_record - start tracing context switches
172e168e051SSteven Rostedt  *
173e168e051SSteven Rostedt  * Turns off context switch tracing for a tracer.
174e168e051SSteven Rostedt  */
175e168e051SSteven Rostedt void tracing_stop_sched_switch_record(void)
176e168e051SSteven Rostedt {
177e168e051SSteven Rostedt 	mutex_lock(&sched_register_mutex);
178e168e051SSteven Rostedt 	tracer_enabled--;
179e168e051SSteven Rostedt 	WARN_ON(tracer_enabled < 0);
180e168e051SSteven Rostedt 	mutex_unlock(&sched_register_mutex);
181e168e051SSteven Rostedt 
182e168e051SSteven Rostedt 	tracing_stop_sched_switch();
183e168e051SSteven Rostedt }
184e168e051SSteven Rostedt 
185e168e051SSteven Rostedt /**
186e168e051SSteven Rostedt  * tracing_sched_switch_assign_trace - assign a trace array for ctx switch
18775f5c47dSSteven Rostedt  * @tr: trace array pointer to assign
18875f5c47dSSteven Rostedt  *
18975f5c47dSSteven Rostedt  * Some tracers might want to record the context switches in their
19075f5c47dSSteven Rostedt  * trace. This function lets those tracers assign the trace array
19175f5c47dSSteven Rostedt  * to use.
19275f5c47dSSteven Rostedt  */
193e168e051SSteven Rostedt void tracing_sched_switch_assign_trace(struct trace_array *tr)
19475f5c47dSSteven Rostedt {
19575f5c47dSSteven Rostedt 	ctx_trace = tr;
19675f5c47dSSteven Rostedt }
19775f5c47dSSteven Rostedt 
198e309b41dSIngo Molnar static void start_sched_trace(struct trace_array *tr)
19935e8e302SSteven Rostedt {
20035e8e302SSteven Rostedt 	sched_switch_reset(tr);
201e168e051SSteven Rostedt 	tracing_start_sched_switch_record();
20235e8e302SSteven Rostedt }
20335e8e302SSteven Rostedt 
204e309b41dSIngo Molnar static void stop_sched_trace(struct trace_array *tr)
20535e8e302SSteven Rostedt {
206e168e051SSteven Rostedt 	tracing_stop_sched_switch_record();
20735e8e302SSteven Rostedt }
20835e8e302SSteven Rostedt 
2091c80025aSFrederic Weisbecker static int sched_switch_trace_init(struct trace_array *tr)
21035e8e302SSteven Rostedt {
21135e8e302SSteven Rostedt 	ctx_trace = tr;
21235e8e302SSteven Rostedt 	start_sched_trace(tr);
2131c80025aSFrederic Weisbecker 	return 0;
21435e8e302SSteven Rostedt }
21535e8e302SSteven Rostedt 
216e309b41dSIngo Molnar static void sched_switch_trace_reset(struct trace_array *tr)
21735e8e302SSteven Rostedt {
218c76f0694SSteven Rostedt 	if (sched_ref)
21935e8e302SSteven Rostedt 		stop_sched_trace(tr);
22035e8e302SSteven Rostedt }
22135e8e302SSteven Rostedt 
2229036990dSSteven Rostedt static void sched_switch_trace_start(struct trace_array *tr)
2239036990dSSteven Rostedt {
2249036990dSSteven Rostedt 	sched_switch_reset(tr);
2259036990dSSteven Rostedt 	tracing_start_sched_switch();
2269036990dSSteven Rostedt }
2279036990dSSteven Rostedt 
2289036990dSSteven Rostedt static void sched_switch_trace_stop(struct trace_array *tr)
2299036990dSSteven Rostedt {
2309036990dSSteven Rostedt 	tracing_stop_sched_switch();
2319036990dSSteven Rostedt }
2329036990dSSteven Rostedt 
23375f5c47dSSteven Rostedt static struct tracer sched_switch_trace __read_mostly =
23435e8e302SSteven Rostedt {
23535e8e302SSteven Rostedt 	.name		= "sched_switch",
23635e8e302SSteven Rostedt 	.init		= sched_switch_trace_init,
23735e8e302SSteven Rostedt 	.reset		= sched_switch_trace_reset,
2389036990dSSteven Rostedt 	.start		= sched_switch_trace_start,
2399036990dSSteven Rostedt 	.stop		= sched_switch_trace_stop,
24060a11774SSteven Rostedt #ifdef CONFIG_FTRACE_SELFTEST
24160a11774SSteven Rostedt 	.selftest    = trace_selftest_startup_sched_switch,
24260a11774SSteven Rostedt #endif
24335e8e302SSteven Rostedt };
24435e8e302SSteven Rostedt 
24535e8e302SSteven Rostedt __init static int init_sched_switch_trace(void)
24635e8e302SSteven Rostedt {
24735e8e302SSteven Rostedt 	return register_tracer(&sched_switch_trace);
24835e8e302SSteven Rostedt }
24935e8e302SSteven Rostedt device_initcall(init_sched_switch_trace);
250c71dd42dSIngo Molnar 
251