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/marker.h>
1335e8e302SSteven Rostedt #include <linux/ftrace.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;
1935e8e302SSteven Rostedt 
20e309b41dSIngo Molnar static void
2135e8e302SSteven Rostedt ctx_switch_func(struct task_struct *prev, struct task_struct *next)
2235e8e302SSteven Rostedt {
2335e8e302SSteven Rostedt 	struct trace_array *tr = ctx_trace;
2435e8e302SSteven Rostedt 	struct trace_array_cpu *data;
2535e8e302SSteven Rostedt 	unsigned long flags;
2635e8e302SSteven Rostedt 	long disabled;
2735e8e302SSteven Rostedt 	int cpu;
2835e8e302SSteven Rostedt 
2935e8e302SSteven Rostedt 	if (!tracer_enabled)
3035e8e302SSteven Rostedt 		return;
3135e8e302SSteven Rostedt 
3218cef379SSteven Rostedt 	local_irq_save(flags);
3335e8e302SSteven Rostedt 	cpu = raw_smp_processor_id();
3435e8e302SSteven Rostedt 	data = tr->data[cpu];
3535e8e302SSteven Rostedt 	disabled = atomic_inc_return(&data->disabled);
3635e8e302SSteven Rostedt 
3735e8e302SSteven Rostedt 	if (likely(disabled == 1))
3835e8e302SSteven Rostedt 		tracing_sched_switch_trace(tr, data, prev, next, flags);
3935e8e302SSteven Rostedt 
4035e8e302SSteven Rostedt 	atomic_dec(&data->disabled);
4118cef379SSteven Rostedt 	local_irq_restore(flags);
4235e8e302SSteven Rostedt }
4335e8e302SSteven Rostedt 
4435e8e302SSteven Rostedt void ftrace_ctx_switch(struct task_struct *prev, struct task_struct *next)
4535e8e302SSteven Rostedt {
4635e8e302SSteven Rostedt 	tracing_record_cmdline(prev);
4735e8e302SSteven Rostedt 
4835e8e302SSteven Rostedt 	/*
4935e8e302SSteven Rostedt 	 * If tracer_switch_func only points to the local
5035e8e302SSteven Rostedt 	 * switch func, it still needs the ptr passed to it.
5135e8e302SSteven Rostedt 	 */
5235e8e302SSteven Rostedt 	ctx_switch_func(prev, next);
5335e8e302SSteven Rostedt 
5435e8e302SSteven Rostedt 	/*
5535e8e302SSteven Rostedt 	 * Chain to the wakeup tracer (this is a NOP if disabled):
5635e8e302SSteven Rostedt 	 */
5735e8e302SSteven Rostedt 	wakeup_sched_switch(prev, next);
5835e8e302SSteven Rostedt }
5935e8e302SSteven Rostedt 
60e309b41dSIngo Molnar static void sched_switch_reset(struct trace_array *tr)
6135e8e302SSteven Rostedt {
6235e8e302SSteven Rostedt 	int cpu;
6335e8e302SSteven Rostedt 
64750ed1a4SIngo Molnar 	tr->time_start = ftrace_now(tr->cpu);
6535e8e302SSteven Rostedt 
6635e8e302SSteven Rostedt 	for_each_online_cpu(cpu)
6735e8e302SSteven Rostedt 		tracing_reset(tr->data[cpu]);
6835e8e302SSteven Rostedt }
6935e8e302SSteven Rostedt 
70e309b41dSIngo Molnar static void start_sched_trace(struct trace_array *tr)
7135e8e302SSteven Rostedt {
7235e8e302SSteven Rostedt 	sched_switch_reset(tr);
7335e8e302SSteven Rostedt 	tracer_enabled = 1;
7435e8e302SSteven Rostedt }
7535e8e302SSteven Rostedt 
76e309b41dSIngo Molnar static void stop_sched_trace(struct trace_array *tr)
7735e8e302SSteven Rostedt {
7835e8e302SSteven Rostedt 	tracer_enabled = 0;
7935e8e302SSteven Rostedt }
8035e8e302SSteven Rostedt 
81e309b41dSIngo Molnar static void sched_switch_trace_init(struct trace_array *tr)
8235e8e302SSteven Rostedt {
8335e8e302SSteven Rostedt 	ctx_trace = tr;
8435e8e302SSteven Rostedt 
8535e8e302SSteven Rostedt 	if (tr->ctrl)
8635e8e302SSteven Rostedt 		start_sched_trace(tr);
8735e8e302SSteven Rostedt }
8835e8e302SSteven Rostedt 
89e309b41dSIngo Molnar static void sched_switch_trace_reset(struct trace_array *tr)
9035e8e302SSteven Rostedt {
9135e8e302SSteven Rostedt 	if (tr->ctrl)
9235e8e302SSteven Rostedt 		stop_sched_trace(tr);
9335e8e302SSteven Rostedt }
9435e8e302SSteven Rostedt 
9535e8e302SSteven Rostedt static void sched_switch_trace_ctrl_update(struct trace_array *tr)
9635e8e302SSteven Rostedt {
9735e8e302SSteven Rostedt 	/* When starting a new trace, reset the buffers */
9835e8e302SSteven Rostedt 	if (tr->ctrl)
9935e8e302SSteven Rostedt 		start_sched_trace(tr);
10035e8e302SSteven Rostedt 	else
10135e8e302SSteven Rostedt 		stop_sched_trace(tr);
10235e8e302SSteven Rostedt }
10335e8e302SSteven Rostedt 
10435e8e302SSteven Rostedt static struct tracer sched_switch_trace __read_mostly =
10535e8e302SSteven Rostedt {
10635e8e302SSteven Rostedt 	.name		= "sched_switch",
10735e8e302SSteven Rostedt 	.init		= sched_switch_trace_init,
10835e8e302SSteven Rostedt 	.reset		= sched_switch_trace_reset,
10935e8e302SSteven Rostedt 	.ctrl_update	= sched_switch_trace_ctrl_update,
11060a11774SSteven Rostedt #ifdef CONFIG_FTRACE_SELFTEST
11160a11774SSteven Rostedt 	.selftest    = trace_selftest_startup_sched_switch,
11260a11774SSteven Rostedt #endif
11335e8e302SSteven Rostedt };
11435e8e302SSteven Rostedt 
11535e8e302SSteven Rostedt __init static int init_sched_switch_trace(void)
11635e8e302SSteven Rostedt {
11735e8e302SSteven Rostedt 	return register_tracer(&sched_switch_trace);
11835e8e302SSteven Rostedt }
11935e8e302SSteven Rostedt device_initcall(init_sched_switch_trace);
120