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