0709e4f7511404e7fb84d6c6ab6da1b5b1560f51
[linux-2.6.git] / kernel / trace / trace_event_perf.c
1 /*
2  * trace event based perf event profiling/tracing
3  *
4  * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra <pzijlstr@redhat.com>
5  * Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com>
6  */
7
8 #include <linux/module.h>
9 #include <linux/kprobes.h>
10 #include "trace.h"
11
12 DEFINE_PER_CPU(struct pt_regs, perf_trace_regs);
13 EXPORT_PER_CPU_SYMBOL_GPL(perf_trace_regs);
14
15 static char *perf_trace_buf;
16 static char *perf_trace_buf_nmi;
17
18 typedef typeof(char [PERF_MAX_TRACE_SIZE]) perf_trace_t ;
19
20 /* Count the events in use (per event id, not per instance) */
21 static int      total_ref_count;
22
23 static int perf_trace_event_enable(struct ftrace_event_call *event)
24 {
25         char *buf;
26         int ret = -ENOMEM;
27
28         if (event->perf_refcount++ > 0)
29                 return 0;
30
31         if (!total_ref_count) {
32                 buf = (char *)alloc_percpu(perf_trace_t);
33                 if (!buf)
34                         goto fail_buf;
35
36                 rcu_assign_pointer(perf_trace_buf, buf);
37
38                 buf = (char *)alloc_percpu(perf_trace_t);
39                 if (!buf)
40                         goto fail_buf_nmi;
41
42                 rcu_assign_pointer(perf_trace_buf_nmi, buf);
43         }
44
45         ret = event->perf_event_enable(event);
46         if (!ret) {
47                 total_ref_count++;
48                 return 0;
49         }
50
51 fail_buf_nmi:
52         if (!total_ref_count) {
53                 free_percpu(perf_trace_buf_nmi);
54                 free_percpu(perf_trace_buf);
55                 perf_trace_buf_nmi = NULL;
56                 perf_trace_buf = NULL;
57         }
58 fail_buf:
59         event->perf_refcount--;
60
61         return ret;
62 }
63
64 int perf_trace_enable(int event_id)
65 {
66         struct ftrace_event_call *event;
67         int ret = -EINVAL;
68
69         mutex_lock(&event_mutex);
70         list_for_each_entry(event, &ftrace_events, list) {
71                 if (event->id == event_id && event->perf_event_enable &&
72                     try_module_get(event->mod)) {
73                         ret = perf_trace_event_enable(event);
74                         break;
75                 }
76         }
77         mutex_unlock(&event_mutex);
78
79         return ret;
80 }
81
82 static void perf_trace_event_disable(struct ftrace_event_call *event)
83 {
84         char *buf, *nmi_buf;
85
86         if (--event->perf_refcount > 0)
87                 return;
88
89         event->perf_event_disable(event);
90
91         if (!--total_ref_count) {
92                 buf = perf_trace_buf;
93                 rcu_assign_pointer(perf_trace_buf, NULL);
94
95                 nmi_buf = perf_trace_buf_nmi;
96                 rcu_assign_pointer(perf_trace_buf_nmi, NULL);
97
98                 /*
99                  * Ensure every events in profiling have finished before
100                  * releasing the buffers
101                  */
102                 synchronize_sched();
103
104                 free_percpu(buf);
105                 free_percpu(nmi_buf);
106         }
107 }
108
109 void perf_trace_disable(int event_id)
110 {
111         struct ftrace_event_call *event;
112
113         mutex_lock(&event_mutex);
114         list_for_each_entry(event, &ftrace_events, list) {
115                 if (event->id == event_id) {
116                         perf_trace_event_disable(event);
117                         module_put(event->mod);
118                         break;
119                 }
120         }
121         mutex_unlock(&event_mutex);
122 }
123
124 __kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
125                                        int *rctxp, unsigned long *irq_flags)
126 {
127         struct trace_entry *entry;
128         char *trace_buf, *raw_data;
129         int pc, cpu;
130
131         pc = preempt_count();
132
133         /* Protect the per cpu buffer, begin the rcu read side */
134         local_irq_save(*irq_flags);
135
136         *rctxp = perf_swevent_get_recursion_context();
137         if (*rctxp < 0)
138                 goto err_recursion;
139
140         cpu = smp_processor_id();
141
142         if (in_nmi())
143                 trace_buf = rcu_dereference(perf_trace_buf_nmi);
144         else
145                 trace_buf = rcu_dereference(perf_trace_buf);
146
147         if (!trace_buf)
148                 goto err;
149
150         raw_data = per_cpu_ptr(trace_buf, cpu);
151
152         /* zero the dead bytes from align to not leak stack to user */
153         *(u64 *)(&raw_data[size - sizeof(u64)]) = 0ULL;
154
155         entry = (struct trace_entry *)raw_data;
156         tracing_generic_entry_update(entry, *irq_flags, pc);
157         entry->type = type;
158
159         return raw_data;
160 err:
161         perf_swevent_put_recursion_context(*rctxp);
162 err_recursion:
163         local_irq_restore(*irq_flags);
164         return NULL;
165 }
166 EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);