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); 250