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;
195b82a1b0SMathieu Desnoyers static atomic_t			sched_ref;
2035e8e302SSteven Rostedt 
21e309b41dSIngo Molnar static void
22b07c3f19SMathieu Desnoyers probe_sched_switch(struct rq *__rq, struct task_struct *prev,
235b82a1b0SMathieu Desnoyers 			struct task_struct *next)
2435e8e302SSteven Rostedt {
2535e8e302SSteven Rostedt 	struct trace_array_cpu *data;
2635e8e302SSteven Rostedt 	unsigned long flags;
2735e8e302SSteven Rostedt 	long disabled;
2835e8e302SSteven Rostedt 	int cpu;
2938697053SSteven Rostedt 	int pc;
3035e8e302SSteven Rostedt 
31b07c3f19SMathieu Desnoyers 	if (!atomic_read(&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 	disabled = atomic_inc_return(&data->disabled);
4535e8e302SSteven Rostedt 
464d9493c9SIngo Molnar 	if (likely(disabled == 1))
4738697053SSteven Rostedt 		tracing_sched_switch_trace(ctx_trace, data, prev, next, flags, pc);
4835e8e302SSteven Rostedt 
4935e8e302SSteven Rostedt 	atomic_dec(&data->disabled);
5018cef379SSteven Rostedt 	local_irq_restore(flags);
5135e8e302SSteven Rostedt }
5235e8e302SSteven Rostedt 
535b82a1b0SMathieu Desnoyers static void
54b07c3f19SMathieu Desnoyers probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee)
555b82a1b0SMathieu Desnoyers {
5657422797SIngo Molnar 	struct trace_array_cpu *data;
5757422797SIngo Molnar 	unsigned long flags;
5857422797SIngo Molnar 	long disabled;
5938697053SSteven Rostedt 	int cpu, pc;
6057422797SIngo Molnar 
61b07c3f19SMathieu Desnoyers 	if (!likely(tracer_enabled))
6257422797SIngo Molnar 		return;
6357422797SIngo Molnar 
6438697053SSteven Rostedt 	pc = preempt_count();
65b07c3f19SMathieu Desnoyers 	tracing_record_cmdline(current);
66d9af56fbSIngo Molnar 
6757422797SIngo Molnar 	local_irq_save(flags);
6857422797SIngo Molnar 	cpu = raw_smp_processor_id();
69b07c3f19SMathieu Desnoyers 	data = ctx_trace->data[cpu];
7057422797SIngo Molnar 	disabled = atomic_inc_return(&data->disabled);
7157422797SIngo Molnar 
724d9493c9SIngo Molnar 	if (likely(disabled == 1))
73b07c3f19SMathieu Desnoyers 		tracing_sched_wakeup_trace(ctx_trace, data, wakee, current,
7438697053SSteven Rostedt 					   flags, pc);
7557422797SIngo Molnar 
7657422797SIngo Molnar 	atomic_dec(&data->disabled);
7757422797SIngo Molnar 	local_irq_restore(flags);
7857422797SIngo Molnar }
7957422797SIngo Molnar 
80e309b41dSIngo Molnar static void sched_switch_reset(struct trace_array *tr)
8135e8e302SSteven Rostedt {
8235e8e302SSteven Rostedt 	int cpu;
8335e8e302SSteven Rostedt 
84750ed1a4SIngo Molnar 	tr->time_start = ftrace_now(tr->cpu);
8535e8e302SSteven Rostedt 
8635e8e302SSteven Rostedt 	for_each_online_cpu(cpu)
873928a8a2SSteven Rostedt 		tracing_reset(tr, cpu);
8835e8e302SSteven Rostedt }
8935e8e302SSteven Rostedt 
905b82a1b0SMathieu Desnoyers static int tracing_sched_register(void)
915b82a1b0SMathieu Desnoyers {
925b82a1b0SMathieu Desnoyers 	int ret;
935b82a1b0SMathieu Desnoyers 
94b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_wakeup(probe_sched_wakeup);
955b82a1b0SMathieu Desnoyers 	if (ret) {
96b07c3f19SMathieu Desnoyers 		pr_info("wakeup trace: Couldn't activate tracepoint"
975b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_wakeup\n");
985b82a1b0SMathieu Desnoyers 		return ret;
995b82a1b0SMathieu Desnoyers 	}
1005b82a1b0SMathieu Desnoyers 
101b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
1025b82a1b0SMathieu Desnoyers 	if (ret) {
103b07c3f19SMathieu Desnoyers 		pr_info("wakeup trace: Couldn't activate tracepoint"
1045b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_wakeup_new\n");
1055b82a1b0SMathieu Desnoyers 		goto fail_deprobe;
1065b82a1b0SMathieu Desnoyers 	}
1075b82a1b0SMathieu Desnoyers 
108b07c3f19SMathieu Desnoyers 	ret = register_trace_sched_switch(probe_sched_switch);
1095b82a1b0SMathieu Desnoyers 	if (ret) {
110b07c3f19SMathieu Desnoyers 		pr_info("sched trace: Couldn't activate tracepoint"
1115b82a1b0SMathieu Desnoyers 			" probe to kernel_sched_schedule\n");
1125b82a1b0SMathieu Desnoyers 		goto fail_deprobe_wake_new;
1135b82a1b0SMathieu Desnoyers 	}
1145b82a1b0SMathieu Desnoyers 
1155b82a1b0SMathieu Desnoyers 	return ret;
1165b82a1b0SMathieu Desnoyers fail_deprobe_wake_new:
117b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
1185b82a1b0SMathieu Desnoyers fail_deprobe:
119b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup(probe_sched_wakeup);
1205b82a1b0SMathieu Desnoyers 	return ret;
1215b82a1b0SMathieu Desnoyers }
1225b82a1b0SMathieu Desnoyers 
1235b82a1b0SMathieu Desnoyers static void tracing_sched_unregister(void)
1245b82a1b0SMathieu Desnoyers {
125b07c3f19SMathieu Desnoyers 	unregister_trace_sched_switch(probe_sched_switch);
126b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup_new(probe_sched_wakeup);
127b07c3f19SMathieu Desnoyers 	unregister_trace_sched_wakeup(probe_sched_wakeup);
1285b82a1b0SMathieu Desnoyers }
1295b82a1b0SMathieu Desnoyers 
130f2252935SIngo Molnar static void tracing_start_sched_switch(void)
1315b82a1b0SMathieu Desnoyers {
1325b82a1b0SMathieu Desnoyers 	long ref;
1335b82a1b0SMathieu Desnoyers 
1345b82a1b0SMathieu Desnoyers 	ref = atomic_inc_return(&sched_ref);
1355b82a1b0SMathieu Desnoyers 	if (ref == 1)
1365b82a1b0SMathieu Desnoyers 		tracing_sched_register();
1375b82a1b0SMathieu Desnoyers }
1385b82a1b0SMathieu Desnoyers 
139f2252935SIngo Molnar static void tracing_stop_sched_switch(void)
1405b82a1b0SMathieu Desnoyers {
1415b82a1b0SMathieu Desnoyers 	long ref;
1425b82a1b0SMathieu Desnoyers 
1435b82a1b0SMathieu Desnoyers 	ref = atomic_dec_and_test(&sched_ref);
1445b82a1b0SMathieu Desnoyers 	if (ref)
1455b82a1b0SMathieu Desnoyers 		tracing_sched_unregister();
1465b82a1b0SMathieu Desnoyers }
1475b82a1b0SMathieu Desnoyers 
14841bc8144SSteven Rostedt void tracing_start_cmdline_record(void)
14941bc8144SSteven Rostedt {
15041bc8144SSteven Rostedt 	tracing_start_sched_switch();
15141bc8144SSteven Rostedt }
15241bc8144SSteven Rostedt 
15341bc8144SSteven Rostedt void tracing_stop_cmdline_record(void)
15441bc8144SSteven Rostedt {
15541bc8144SSteven Rostedt 	tracing_stop_sched_switch();
15641bc8144SSteven Rostedt }
15741bc8144SSteven Rostedt 
158e309b41dSIngo Molnar static void start_sched_trace(struct trace_array *tr)
15935e8e302SSteven Rostedt {
16035e8e302SSteven Rostedt 	sched_switch_reset(tr);
16141bc8144SSteven Rostedt 	tracing_start_cmdline_record();
162007c05d4SSteven Rostedt 	tracer_enabled = 1;
16335e8e302SSteven Rostedt }
16435e8e302SSteven Rostedt 
165e309b41dSIngo Molnar static void stop_sched_trace(struct trace_array *tr)
16635e8e302SSteven Rostedt {
16735e8e302SSteven Rostedt 	tracer_enabled = 0;
168007c05d4SSteven Rostedt 	tracing_stop_cmdline_record();
16935e8e302SSteven Rostedt }
17035e8e302SSteven Rostedt 
171e309b41dSIngo Molnar static void sched_switch_trace_init(struct trace_array *tr)
17235e8e302SSteven Rostedt {
17335e8e302SSteven Rostedt 	ctx_trace = tr;
17435e8e302SSteven Rostedt 
17535e8e302SSteven Rostedt 	if (tr->ctrl)
17635e8e302SSteven Rostedt 		start_sched_trace(tr);
17735e8e302SSteven Rostedt }
17835e8e302SSteven Rostedt 
179e309b41dSIngo Molnar static void sched_switch_trace_reset(struct trace_array *tr)
18035e8e302SSteven Rostedt {
18135e8e302SSteven Rostedt 	if (tr->ctrl)
18235e8e302SSteven Rostedt 		stop_sched_trace(tr);
18335e8e302SSteven Rostedt }
18435e8e302SSteven Rostedt 
18535e8e302SSteven Rostedt static void sched_switch_trace_ctrl_update(struct trace_array *tr)
18635e8e302SSteven Rostedt {
18735e8e302SSteven Rostedt 	/* When starting a new trace, reset the buffers */
18835e8e302SSteven Rostedt 	if (tr->ctrl)
18935e8e302SSteven Rostedt 		start_sched_trace(tr);
19035e8e302SSteven Rostedt 	else
19135e8e302SSteven Rostedt 		stop_sched_trace(tr);
19235e8e302SSteven Rostedt }
19335e8e302SSteven Rostedt 
19435e8e302SSteven Rostedt static struct tracer sched_switch_trace __read_mostly =
19535e8e302SSteven Rostedt {
19635e8e302SSteven Rostedt 	.name		= "sched_switch",
19735e8e302SSteven Rostedt 	.init		= sched_switch_trace_init,
19835e8e302SSteven Rostedt 	.reset		= sched_switch_trace_reset,
19935e8e302SSteven Rostedt 	.ctrl_update	= sched_switch_trace_ctrl_update,
20060a11774SSteven Rostedt #ifdef CONFIG_FTRACE_SELFTEST
20160a11774SSteven Rostedt 	.selftest    = trace_selftest_startup_sched_switch,
20260a11774SSteven Rostedt #endif
20335e8e302SSteven Rostedt };
20435e8e302SSteven Rostedt 
20535e8e302SSteven Rostedt __init static int init_sched_switch_trace(void)
20635e8e302SSteven Rostedt {
2075b82a1b0SMathieu Desnoyers 	int ret = 0;
2085b82a1b0SMathieu Desnoyers 
2095b82a1b0SMathieu Desnoyers 	if (atomic_read(&sched_ref))
2105b82a1b0SMathieu Desnoyers 		ret = tracing_sched_register();
2115b82a1b0SMathieu Desnoyers 	if (ret) {
2125b82a1b0SMathieu Desnoyers 		pr_info("error registering scheduler trace\n");
2135b82a1b0SMathieu Desnoyers 		return ret;
2145b82a1b0SMathieu Desnoyers 	}
21535e8e302SSteven Rostedt 	return register_tracer(&sched_switch_trace);
21635e8e302SSteven Rostedt }
21735e8e302SSteven Rostedt device_initcall(init_sched_switch_trace);
218