ftrace: preempt disable over interrupt disable
[linux-2.6.git] / kernel / trace / trace_sched_switch.c
1 /*
2  * trace context switch
3  *
4  * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
5  *
6  */
7 #include <linux/module.h>
8 #include <linux/fs.h>
9 #include <linux/debugfs.h>
10 #include <linux/kallsyms.h>
11 #include <linux/uaccess.h>
12 #include <linux/ftrace.h>
13 #include <trace/sched.h>
14
15 #include "trace.h"
16
17 static struct trace_array       *ctx_trace;
18 static int __read_mostly        tracer_enabled;
19 static atomic_t                 sched_ref;
20
21 static void
22 probe_sched_switch(struct rq *__rq, struct task_struct *prev,
23                         struct task_struct *next)
24 {
25         struct trace_array_cpu *data;
26         unsigned long flags;
27         long disabled;
28         int cpu;
29         int pc;
30
31         if (!atomic_read(&sched_ref))
32                 return;
33
34         tracing_record_cmdline(prev);
35         tracing_record_cmdline(next);
36
37         if (!tracer_enabled)
38                 return;
39
40         pc = preempt_count();
41         local_irq_save(flags);
42         cpu = raw_smp_processor_id();
43         data = ctx_trace->data[cpu];
44         disabled = atomic_inc_return(&data->disabled);
45
46         if (likely(disabled == 1))
47                 tracing_sched_switch_trace(ctx_trace, data, prev, next, flags, pc);
48
49         atomic_dec(&data->disabled);
50         local_irq_restore(flags);
51 }
52
53 static void
54 probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee)
55 {
56         struct trace_array_cpu *data;
57         unsigned long flags;
58         long disabled;
59         int cpu, pc;
60
61         if (!likely(tracer_enabled))
62                 return;
63
64         pc = preempt_count();
65         tracing_record_cmdline(current);
66
67         local_irq_save(flags);
68         cpu = raw_smp_processor_id();
69         data = ctx_trace->data[cpu];
70         disabled = atomic_inc_return(&data->disabled);
71
72         if (likely(disabled == 1))
73                 tracing_sched_wakeup_trace(ctx_trace, data, wakee, current,
74                                            flags, pc);
75
76         atomic_dec(&data->disabled);
77         local_irq_restore(flags);
78 }
79
80 static void sched_switch_reset(struct trace_array *tr)
81 {
82         int cpu;
83
84         tr->time_start = ftrace_now(tr->cpu);
85
86         for_each_online_cpu(cpu)
87                 tracing_reset(tr, cpu);
88 }
89
90 static int tracing_sched_register(void)
91 {
92         int ret;
93
94         ret = register_trace_sched_wakeup(probe_sched_wakeup);
95         if (ret) {
96                 pr_info("wakeup trace: Couldn't activate tracepoint"
97                         " probe to kernel_sched_wakeup\n");
98                 return ret;
99         }
100
101         ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
102         if (ret) {
103                 pr_info("wakeup trace: Couldn't activate tracepoint"
104                         " probe to kernel_sched_wakeup_new\n");
105                 goto fail_deprobe;
106         }
107
108         ret = register_trace_sched_switch(probe_sched_switch);
109         if (ret) {
110                 pr_info("sched trace: Couldn't activate tracepoint"
111                         " probe to kernel_sched_schedule\n");
112                 goto fail_deprobe_wake_new;
113         }
114
115         return ret;
116 fail_deprobe_wake_new:
117         unregister_trace_sched_wakeup_new(probe_sched_wakeup);
118 fail_deprobe:
119         unregister_trace_sched_wakeup(probe_sched_wakeup);
120         return ret;
121 }
122
123 static void tracing_sched_unregister(void)
124 {
125         unregister_trace_sched_switch(probe_sched_switch);
126         unregister_trace_sched_wakeup_new(probe_sched_wakeup);
127         unregister_trace_sched_wakeup(probe_sched_wakeup);
128 }
129
130 static void tracing_start_sched_switch(void)
131 {
132         long ref;
133
134         ref = atomic_inc_return(&sched_ref);
135         if (ref == 1)
136                 tracing_sched_register();
137 }
138
139 static void tracing_stop_sched_switch(void)
140 {
141         long ref;
142
143         ref = atomic_dec_and_test(&sched_ref);
144         if (ref)
145                 tracing_sched_unregister();
146 }
147
148 void tracing_start_cmdline_record(void)
149 {
150         tracing_start_sched_switch();
151 }
152
153 void tracing_stop_cmdline_record(void)
154 {
155         tracing_stop_sched_switch();
156 }
157
158 static void start_sched_trace(struct trace_array *tr)
159 {
160         sched_switch_reset(tr);
161         tracing_start_cmdline_record();
162         tracer_enabled = 1;
163 }
164
165 static void stop_sched_trace(struct trace_array *tr)
166 {
167         tracer_enabled = 0;
168         tracing_stop_cmdline_record();
169 }
170
171 static void sched_switch_trace_init(struct trace_array *tr)
172 {
173         ctx_trace = tr;
174
175         if (tr->ctrl)
176                 start_sched_trace(tr);
177 }
178
179 static void sched_switch_trace_reset(struct trace_array *tr)
180 {
181         if (tr->ctrl)
182                 stop_sched_trace(tr);
183 }
184
185 static void sched_switch_trace_ctrl_update(struct trace_array *tr)
186 {
187         /* When starting a new trace, reset the buffers */
188         if (tr->ctrl)
189                 start_sched_trace(tr);
190         else
191                 stop_sched_trace(tr);
192 }
193
194 static struct tracer sched_switch_trace __read_mostly =
195 {
196         .name           = "sched_switch",
197         .init           = sched_switch_trace_init,
198         .reset          = sched_switch_trace_reset,
199         .ctrl_update    = sched_switch_trace_ctrl_update,
200 #ifdef CONFIG_FTRACE_SELFTEST
201         .selftest    = trace_selftest_startup_sched_switch,
202 #endif
203 };
204
205 __init static int init_sched_switch_trace(void)
206 {
207         int ret = 0;
208
209         if (atomic_read(&sched_ref))
210                 ret = tracing_sched_register();
211         if (ret) {
212                 pr_info("error registering scheduler trace\n");
213                 return ret;
214         }
215         return register_tracer(&sched_switch_trace);
216 }
217 device_initcall(init_sched_switch_trace);