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