Merge branch 'fix/hda' into for-linus
[pandora-kernel.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/events/sched.h>
14
15 #include "trace.h"
16
17 static struct trace_array       *ctx_trace;
18 static int __read_mostly        tracer_enabled;
19 static int                      sched_ref;
20 static DEFINE_MUTEX(sched_register_mutex);
21 static int                      sched_stopped;
22
23
24 void
25 tracing_sched_switch_trace(struct trace_array *tr,
26                            struct task_struct *prev,
27                            struct task_struct *next,
28                            unsigned long flags, int pc)
29 {
30         struct ftrace_event_call *call = &event_context_switch;
31         struct ring_buffer *buffer = tr->buffer;
32         struct ring_buffer_event *event;
33         struct ctx_switch_entry *entry;
34
35         event = trace_buffer_lock_reserve(buffer, TRACE_CTX,
36                                           sizeof(*entry), flags, pc);
37         if (!event)
38                 return;
39         entry   = ring_buffer_event_data(event);
40         entry->prev_pid                 = prev->pid;
41         entry->prev_prio                = prev->prio;
42         entry->prev_state               = prev->state;
43         entry->next_pid                 = next->pid;
44         entry->next_prio                = next->prio;
45         entry->next_state               = next->state;
46         entry->next_cpu = task_cpu(next);
47
48         if (!filter_check_discard(call, entry, buffer, event))
49                 trace_buffer_unlock_commit(buffer, event, flags, pc);
50 }
51
52 static void
53 probe_sched_switch(struct rq *__rq, struct task_struct *prev,
54                         struct task_struct *next)
55 {
56         struct trace_array_cpu *data;
57         unsigned long flags;
58         int cpu;
59         int pc;
60
61         if (unlikely(!sched_ref))
62                 return;
63
64         tracing_record_cmdline(prev);
65         tracing_record_cmdline(next);
66
67         if (!tracer_enabled || sched_stopped)
68                 return;
69
70         pc = preempt_count();
71         local_irq_save(flags);
72         cpu = raw_smp_processor_id();
73         data = ctx_trace->data[cpu];
74
75         if (likely(!atomic_read(&data->disabled)))
76                 tracing_sched_switch_trace(ctx_trace, prev, next, flags, pc);
77
78         local_irq_restore(flags);
79 }
80
81 void
82 tracing_sched_wakeup_trace(struct trace_array *tr,
83                            struct task_struct *wakee,
84                            struct task_struct *curr,
85                            unsigned long flags, int pc)
86 {
87         struct ftrace_event_call *call = &event_wakeup;
88         struct ring_buffer_event *event;
89         struct ctx_switch_entry *entry;
90         struct ring_buffer *buffer = tr->buffer;
91
92         event = trace_buffer_lock_reserve(buffer, TRACE_WAKE,
93                                           sizeof(*entry), flags, pc);
94         if (!event)
95                 return;
96         entry   = ring_buffer_event_data(event);
97         entry->prev_pid                 = curr->pid;
98         entry->prev_prio                = curr->prio;
99         entry->prev_state               = curr->state;
100         entry->next_pid                 = wakee->pid;
101         entry->next_prio                = wakee->prio;
102         entry->next_state               = wakee->state;
103         entry->next_cpu                 = task_cpu(wakee);
104
105         if (!filter_check_discard(call, entry, buffer, event))
106                 ring_buffer_unlock_commit(buffer, event);
107         ftrace_trace_stack(tr->buffer, flags, 6, pc);
108         ftrace_trace_userstack(tr->buffer, flags, pc);
109 }
110
111 static void
112 probe_sched_wakeup(struct rq *__rq, struct task_struct *wakee, int success)
113 {
114         struct trace_array_cpu *data;
115         unsigned long flags;
116         int cpu, pc;
117
118         if (unlikely(!sched_ref))
119                 return;
120
121         tracing_record_cmdline(current);
122
123         if (!tracer_enabled || sched_stopped)
124                 return;
125
126         pc = preempt_count();
127         local_irq_save(flags);
128         cpu = raw_smp_processor_id();
129         data = ctx_trace->data[cpu];
130
131         if (likely(!atomic_read(&data->disabled)))
132                 tracing_sched_wakeup_trace(ctx_trace, wakee, current,
133                                            flags, pc);
134
135         local_irq_restore(flags);
136 }
137
138 static int tracing_sched_register(void)
139 {
140         int ret;
141
142         ret = register_trace_sched_wakeup(probe_sched_wakeup);
143         if (ret) {
144                 pr_info("wakeup trace: Couldn't activate tracepoint"
145                         " probe to kernel_sched_wakeup\n");
146                 return ret;
147         }
148
149         ret = register_trace_sched_wakeup_new(probe_sched_wakeup);
150         if (ret) {
151                 pr_info("wakeup trace: Couldn't activate tracepoint"
152                         " probe to kernel_sched_wakeup_new\n");
153                 goto fail_deprobe;
154         }
155
156         ret = register_trace_sched_switch(probe_sched_switch);
157         if (ret) {
158                 pr_info("sched trace: Couldn't activate tracepoint"
159                         " probe to kernel_sched_switch\n");
160                 goto fail_deprobe_wake_new;
161         }
162
163         return ret;
164 fail_deprobe_wake_new:
165         unregister_trace_sched_wakeup_new(probe_sched_wakeup);
166 fail_deprobe:
167         unregister_trace_sched_wakeup(probe_sched_wakeup);
168         return ret;
169 }
170
171 static void tracing_sched_unregister(void)
172 {
173         unregister_trace_sched_switch(probe_sched_switch);
174         unregister_trace_sched_wakeup_new(probe_sched_wakeup);
175         unregister_trace_sched_wakeup(probe_sched_wakeup);
176 }
177
178 static void tracing_start_sched_switch(void)
179 {
180         mutex_lock(&sched_register_mutex);
181         if (!(sched_ref++))
182                 tracing_sched_register();
183         mutex_unlock(&sched_register_mutex);
184 }
185
186 static void tracing_stop_sched_switch(void)
187 {
188         mutex_lock(&sched_register_mutex);
189         if (!(--sched_ref))
190                 tracing_sched_unregister();
191         mutex_unlock(&sched_register_mutex);
192 }
193
194 void tracing_start_cmdline_record(void)
195 {
196         tracing_start_sched_switch();
197 }
198
199 void tracing_stop_cmdline_record(void)
200 {
201         tracing_stop_sched_switch();
202 }
203
204 /**
205  * tracing_start_sched_switch_record - start tracing context switches
206  *
207  * Turns on context switch tracing for a tracer.
208  */
209 void tracing_start_sched_switch_record(void)
210 {
211         if (unlikely(!ctx_trace)) {
212                 WARN_ON(1);
213                 return;
214         }
215
216         tracing_start_sched_switch();
217
218         mutex_lock(&sched_register_mutex);
219         tracer_enabled++;
220         mutex_unlock(&sched_register_mutex);
221 }
222
223 /**
224  * tracing_stop_sched_switch_record - start tracing context switches
225  *
226  * Turns off context switch tracing for a tracer.
227  */
228 void tracing_stop_sched_switch_record(void)
229 {
230         mutex_lock(&sched_register_mutex);
231         tracer_enabled--;
232         WARN_ON(tracer_enabled < 0);
233         mutex_unlock(&sched_register_mutex);
234
235         tracing_stop_sched_switch();
236 }
237
238 /**
239  * tracing_sched_switch_assign_trace - assign a trace array for ctx switch
240  * @tr: trace array pointer to assign
241  *
242  * Some tracers might want to record the context switches in their
243  * trace. This function lets those tracers assign the trace array
244  * to use.
245  */
246 void tracing_sched_switch_assign_trace(struct trace_array *tr)
247 {
248         ctx_trace = tr;
249 }
250
251 static void stop_sched_trace(struct trace_array *tr)
252 {
253         tracing_stop_sched_switch_record();
254 }
255
256 static int sched_switch_trace_init(struct trace_array *tr)
257 {
258         ctx_trace = tr;
259         tracing_reset_online_cpus(tr);
260         tracing_start_sched_switch_record();
261         return 0;
262 }
263
264 static void sched_switch_trace_reset(struct trace_array *tr)
265 {
266         if (sched_ref)
267                 stop_sched_trace(tr);
268 }
269
270 static void sched_switch_trace_start(struct trace_array *tr)
271 {
272         sched_stopped = 0;
273 }
274
275 static void sched_switch_trace_stop(struct trace_array *tr)
276 {
277         sched_stopped = 1;
278 }
279
280 static struct tracer sched_switch_trace __read_mostly =
281 {
282         .name           = "sched_switch",
283         .init           = sched_switch_trace_init,
284         .reset          = sched_switch_trace_reset,
285         .start          = sched_switch_trace_start,
286         .stop           = sched_switch_trace_stop,
287         .wait_pipe      = poll_wait_pipe,
288 #ifdef CONFIG_FTRACE_SELFTEST
289         .selftest    = trace_selftest_startup_sched_switch,
290 #endif
291 };
292
293 __init static int init_sched_switch_trace(void)
294 {
295         return register_tracer(&sched_switch_trace);
296 }
297 device_initcall(init_sched_switch_trace);
298