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>
13ad8d75ffSSteven Rostedt #include <trace/events/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);
215fec6ddcSSteven Rostedt static int			sched_stopped;
2235e8e302SSteven Rostedt 
23e309b41dSIngo Molnar static void
24b07c3f19SMathieu Desnoyers probe_sched_switch(struct rq *__rq, struct task_struct *prev,
255b82a1b0SMathieu Desnoyers 			struct task_struct *next)
2635e8e302SSteven Rostedt {
2735e8e302SSteven Rostedt 	struct trace_array_cpu *data;
2835e8e302SSteven Rostedt 	unsigned long flags;
2935e8e302SSteven Rostedt 	int cpu;
3038697053SSteven Rostedt 	int pc;
3135e8e302SSteven Rostedt 
32dcef788eSZhaolei 	if (unlikely(!sched_ref))
33b07c3f19SMathieu Desnoyers 		return;
34b07c3f19SMathieu Desnoyers 
3541bc8144SSteven Rostedt 	tracing_record_cmdline(prev);
3641bc8144SSteven Rostedt 	tracing_record_cmdline(next);
3741bc8144SSteven Rostedt 
38dcef788eSZhaolei 	if (!tracer_enabled || sched_stopped)
3935e8e302SSteven Rostedt 		return;
4035e8e302SSteven Rostedt 
4138697053SSteven Rostedt 	pc = preempt_count();
4218cef379SSteven Rostedt 	local_irq_save(flags);
4335e8e302SSteven Rostedt 	cpu = raw_smp_processor_id();
44b07c3f19SMathieu Desnoyers 	data = ctx_trace->data[cpu];
4535e8e302SSteven Rostedt 
463ea2e6d7SSteven Rostedt 	if (likely(!atomic_read(&data->disabled)))
477be42151SArnaldo Carvalho de Melo 		tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc);
4835e8e302SSteven Rostedt 
4918cef379SSteven Rostedt 	local_irq_restore(flags);
5035e8e302SSteven Rostedt }
5135e8e302SSteven Rostedt 
525b82a1b0SMathieu Desnoyers static void
53468a15bbSPeter Zijlstra probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success)
545b82a1b0SMathieu Desnoyers {
5557422797SIngo Molnar 	struct trace_array_cpu *data;
5657422797SIngo Molnar 	unsigned long flags;
5738697053SSteven Rostedt 	int cpu, pc;
5857422797SIngo Molnar 
59dcef788eSZhaolei 	if (unlikely(!sched_ref))
60dcef788eSZhaolei 		return;
61dcef788eSZhaolei 
62dcef788eSZhaolei 	tracing_record_cmdline(current);
63dcef788eSZhaolei 
64dcef788eSZhaolei 	if (!tracer_enabled || sched_stopped)
6557422797SIngo Molnar 		return;
6657422797SIngo Molnar 
6738697053SSteven Rostedt 	pc = preempt_count();
6857422797SIngo Molnar 	local_irq_save(flags);
6957422797SIngo Molnar 	cpu = raw_smp_processor_id();
70b07c3f19SMathieu Desnoyers 	data = ctx_trace->data[cpu];
7157422797SIngo Molnar 
723ea2e6d7SSteven Rostedt 	if (likely(!atomic_read(&data->disabled)))
737be42151SArnaldo Carvalho de Melo 		tracing_sched_wakeup_trace(ctx_trace, wakee, current,
7438697053SSteven Rostedt 					   flags, pc);
7557422797SIngo Molnar 
7657422797SIngo Molnar 	local_irq_restore(flags);
7757422797SIngo Molnar }
7857422797SIngo Molnar 
795b82a1b0SMathieu Desnoyers static int tracing_sched_register(void)
805b82a1b0SMathieu Desnoyers {
815b82a1b0SMathieu Desnoyers 	int ret;
825b82a1b0SMathieu Desnoyers 
83b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_wakeup(probe_sched_wakeup);
845b82a1b0SMathieu Desnoyers 	if (ret) {
85b07c3f19SMathieu Desnoyers 		pr_info("wakeup trace: Couldn't activate tracepoint"
865b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_wakeup\n");
875b82a1b0SMathieu Desnoyers 		return ret;
885b82a1b0SMathieu Desnoyers 	}
895b82a1b0SMathieu Desnoyers 
90b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
915b82a1b0SMathieu Desnoyers 	if (ret) {
92b07c3f19SMathieu Desnoyers 		pr_info("wakeup trace: Couldn't activate tracepoint"
935b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_wakeup_new\n");
945b82a1b0SMathieu Desnoyers 		goto fail_deprobe;
955b82a1b0SMathieu Desnoyers 	}
965b82a1b0SMathieu Desnoyers 
97b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_switch(probe_sched_switch);
985b82a1b0SMathieu Desnoyers 	if (ret) {
99b07c3f19SMathieu Desnoyers 		pr_info("sched trace: Couldn't activate tracepoint"
10073d8b8bcSWenji Huang 			" probe to kernel_sched_switch\n");
1015b82a1b0SMathieu Desnoyers 		goto fail_deprobe_wake_new;
1025b82a1b0SMathieu Desnoyers 	}
1035b82a1b0SMathieu Desnoyers 
1045b82a1b0SMathieu Desnoyers 	return ret;
1055b82a1b0SMathieu Desnoyers fail_deprobe_wake_new:
106b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
1075b82a1b0SMathieu Desnoyers fail_deprobe:
108b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup(probe_sched_wakeup);
1095b82a1b0SMathieu Desnoyers 	return ret;
1105b82a1b0SMathieu Desnoyers }
1115b82a1b0SMathieu Desnoyers 
1125b82a1b0SMathieu Desnoyers static void tracing_sched_unregister(void)
1135b82a1b0SMathieu Desnoyers {
114b07c3f19SMathieu Desnoyers 	unregister_trace_sched_switch(probe_sched_switch);
115b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
116b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup(probe_sched_wakeup);
1175b82a1b0SMathieu Desnoyers }
1185b82a1b0SMathieu Desnoyers 
119f2252935SIngo Molnar static void tracing_start_sched_switch(void)
1205b82a1b0SMathieu Desnoyers {
121efade6e7SFrederic Weisbecker 	mutex_lock(&sched_register_mutex);
122e168e051SSteven Rostedt 	if (!(sched_ref++))
1235b82a1b0SMathieu Desnoyers 		tracing_sched_register();
124efade6e7SFrederic Weisbecker 	mutex_unlock(&sched_register_mutex);
1255b82a1b0SMathieu Desnoyers }
1265b82a1b0SMathieu Desnoyers 
127f2252935SIngo Molnar static void tracing_stop_sched_switch(void)
1285b82a1b0SMathieu Desnoyers {
129efade6e7SFrederic Weisbecker 	mutex_lock(&sched_register_mutex);
130e168e051SSteven Rostedt 	if (!(--sched_ref))
1315b82a1b0SMathieu Desnoyers 		tracing_sched_unregister();
132efade6e7SFrederic Weisbecker 	mutex_unlock(&sched_register_mutex);
1335b82a1b0SMathieu Desnoyers }
1345b82a1b0SMathieu Desnoyers 
13541bc8144SSteven Rostedt void tracing_start_cmdline_record(void)
13641bc8144SSteven Rostedt {
13741bc8144SSteven Rostedt 	tracing_start_sched_switch();
13841bc8144SSteven Rostedt }
13941bc8144SSteven Rostedt 
14041bc8144SSteven Rostedt void tracing_stop_cmdline_record(void)
14141bc8144SSteven Rostedt {
14241bc8144SSteven Rostedt 	tracing_stop_sched_switch();
14341bc8144SSteven Rostedt }
14441bc8144SSteven Rostedt 
14575f5c47dSSteven Rostedt /**
146e168e051SSteven Rostedt  * tracing_start_sched_switch_record - start tracing context switches
147e168e051SSteven Rostedt  *
148e168e051SSteven Rostedt  * Turns on context switch tracing for a tracer.
149e168e051SSteven Rostedt  */
150e168e051SSteven Rostedt void tracing_start_sched_switch_record(void)
151e168e051SSteven Rostedt {
152e168e051SSteven Rostedt 	if (unlikely(!ctx_trace)) {
153e168e051SSteven Rostedt 		WARN_ON(1);
154e168e051SSteven Rostedt 		return;
155e168e051SSteven Rostedt 	}
156e168e051SSteven Rostedt 
157e168e051SSteven Rostedt 	tracing_start_sched_switch();
158e168e051SSteven Rostedt 
159e168e051SSteven Rostedt 	mutex_lock(&sched_register_mutex);
160e168e051SSteven Rostedt 	tracer_enabled++;
161e168e051SSteven Rostedt 	mutex_unlock(&sched_register_mutex);
162e168e051SSteven Rostedt }
163e168e051SSteven Rostedt 
164e168e051SSteven Rostedt /**
165e168e051SSteven Rostedt  * tracing_stop_sched_switch_record - start tracing context switches
166e168e051SSteven Rostedt  *
167e168e051SSteven Rostedt  * Turns off context switch tracing for a tracer.
168e168e051SSteven Rostedt  */
169e168e051SSteven Rostedt void tracing_stop_sched_switch_record(void)
170e168e051SSteven Rostedt {
171e168e051SSteven Rostedt 	mutex_lock(&sched_register_mutex);
172e168e051SSteven Rostedt 	tracer_enabled--;
173e168e051SSteven Rostedt 	WARN_ON(tracer_enabled < 0);
174e168e051SSteven Rostedt 	mutex_unlock(&sched_register_mutex);
175e168e051SSteven Rostedt 
176e168e051SSteven Rostedt 	tracing_stop_sched_switch();
177e168e051SSteven Rostedt }
178e168e051SSteven Rostedt 
179e168e051SSteven Rostedt /**
180e168e051SSteven Rostedt  * tracing_sched_switch_assign_trace - assign a trace array for ctx switch
18175f5c47dSSteven Rostedt  * @tr: trace array pointer to assign
18275f5c47dSSteven Rostedt  *
18375f5c47dSSteven Rostedt  * Some tracers might want to record the context switches in their
18475f5c47dSSteven Rostedt  * trace. This function lets those tracers assign the trace array
18575f5c47dSSteven Rostedt  * to use.
18675f5c47dSSteven Rostedt  */
187e168e051SSteven Rostedt void tracing_sched_switch_assign_trace(struct trace_array *tr)
18875f5c47dSSteven Rostedt {
18975f5c47dSSteven Rostedt 	ctx_trace = tr;
19075f5c47dSSteven Rostedt }
19175f5c47dSSteven Rostedt 
192e309b41dSIngo Molnar static void stop_sched_trace(struct trace_array *tr)
19335e8e302SSteven Rostedt {
194e168e051SSteven Rostedt 	tracing_stop_sched_switch_record();
19535e8e302SSteven Rostedt }
19635e8e302SSteven Rostedt 
1971c80025aSFrederic Weisbecker static int sched_switch_trace_init(struct trace_array *tr)
19835e8e302SSteven Rostedt {
19935e8e302SSteven Rostedt 	ctx_trace = tr;
2005fec6ddcSSteven Rostedt 	tracing_reset_online_cpus(tr);
201b6f11df2SArnaldo Carvalho de Melo 	tracing_start_sched_switch_record();
2021c80025aSFrederic Weisbecker 	return 0;
20335e8e302SSteven Rostedt }
20435e8e302SSteven Rostedt 
205e309b41dSIngo Molnar static void sched_switch_trace_reset(struct trace_array *tr)
20635e8e302SSteven Rostedt {
207c76f0694SSteven Rostedt 	if (sched_ref)
20835e8e302SSteven Rostedt 		stop_sched_trace(tr);
20935e8e302SSteven Rostedt }
21035e8e302SSteven Rostedt 
2119036990dSSteven Rostedt static void sched_switch_trace_start(struct trace_array *tr)
2129036990dSSteven Rostedt {
2135fec6ddcSSteven Rostedt 	sched_stopped = 0;
2149036990dSSteven Rostedt }
2159036990dSSteven Rostedt 
2169036990dSSteven Rostedt static void sched_switch_trace_stop(struct trace_array *tr)
2179036990dSSteven Rostedt {
2185fec6ddcSSteven Rostedt 	sched_stopped = 1;
2199036990dSSteven Rostedt }
2209036990dSSteven Rostedt 
22175f5c47dSSteven Rostedt static struct tracer sched_switch_trace __read_mostly =
22235e8e302SSteven Rostedt {
22335e8e302SSteven Rostedt 	.name		= "sched_switch",
22435e8e302SSteven Rostedt 	.init		= sched_switch_trace_init,
22535e8e302SSteven Rostedt 	.reset		= sched_switch_trace_reset,
2269036990dSSteven Rostedt 	.start		= sched_switch_trace_start,
2279036990dSSteven Rostedt 	.stop		= sched_switch_trace_stop,
2286eaaa5d5SFrederic Weisbecker 	.wait_pipe	= poll_wait_pipe,
22960a11774SSteven Rostedt #ifdef CONFIG_FTRACE_SELFTEST
23060a11774SSteven Rostedt 	.selftest    = trace_selftest_startup_sched_switch,
23160a11774SSteven Rostedt #endif
23235e8e302SSteven Rostedt };
23335e8e302SSteven Rostedt 
23435e8e302SSteven Rostedt __init static int init_sched_switch_trace(void)
23535e8e302SSteven Rostedt {
23635e8e302SSteven Rostedt 	return register_tracer(&sched_switch_trace);
23735e8e302SSteven Rostedt }
23835e8e302SSteven Rostedt device_initcall(init_sched_switch_trace);
239c71dd42dSIngo Molnar 
240