perf: Fix inconsistency between IP and callchain sampling
[linux-2.6-block.git] / kernel / trace / trace_event_profile.c
CommitLineData
ac199db0
PZ
1/*
2 * trace event based perf counter profiling
3 *
4 * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra <pzijlstr@redhat.com>
5 *
6 */
7
558e6547 8#include <linux/module.h>
ac199db0
PZ
9#include "trace.h"
10
20ab4425 11
ce71b9df 12char *perf_trace_buf;
444a2a3b 13EXPORT_SYMBOL_GPL(perf_trace_buf);
05bafda8 14
ce71b9df 15char *perf_trace_buf_nmi;
444a2a3b 16EXPORT_SYMBOL_GPL(perf_trace_buf_nmi);
20ab4425 17
ce71b9df
FW
18typedef typeof(char [FTRACE_MAX_PROFILE_SIZE]) perf_trace_t ;
19
20ab4425
FW
20/* Count the events in use (per event id, not per instance) */
21static int total_profile_count;
22
e5e25cf4
FW
23static int ftrace_profile_enable_event(struct ftrace_event_call *event)
24{
ce71b9df 25 char *buf;
20ab4425
FW
26 int ret = -ENOMEM;
27
e00bf2ec 28 if (event->profile_count++ > 0)
e5e25cf4
FW
29 return 0;
30
fe8e5b5a 31 if (!total_profile_count) {
ce71b9df 32 buf = (char *)alloc_percpu(perf_trace_t);
20ab4425
FW
33 if (!buf)
34 goto fail_buf;
35
444a2a3b 36 rcu_assign_pointer(perf_trace_buf, buf);
20ab4425 37
ce71b9df 38 buf = (char *)alloc_percpu(perf_trace_t);
20ab4425
FW
39 if (!buf)
40 goto fail_buf_nmi;
41
444a2a3b 42 rcu_assign_pointer(perf_trace_buf_nmi, buf);
20ab4425
FW
43 }
44
d7a4b414 45 ret = event->profile_enable(event);
fe8e5b5a
FW
46 if (!ret) {
47 total_profile_count++;
20ab4425 48 return 0;
fe8e5b5a 49 }
20ab4425 50
20ab4425 51fail_buf_nmi:
fe8e5b5a 52 if (!total_profile_count) {
444a2a3b
FW
53 free_percpu(perf_trace_buf_nmi);
54 free_percpu(perf_trace_buf);
55 perf_trace_buf_nmi = NULL;
56 perf_trace_buf = NULL;
fe8e5b5a 57 }
20ab4425 58fail_buf:
e00bf2ec 59 event->profile_count--;
20ab4425
FW
60
61 return ret;
e5e25cf4
FW
62}
63
ac199db0
PZ
64int ftrace_profile_enable(int event_id)
65{
66 struct ftrace_event_call *event;
20c8928a 67 int ret = -EINVAL;
ac199db0 68
20c8928a 69 mutex_lock(&event_mutex);
a59fd602 70 list_for_each_entry(event, &ftrace_events, list) {
558e6547
LZ
71 if (event->id == event_id && event->profile_enable &&
72 try_module_get(event->mod)) {
e5e25cf4 73 ret = ftrace_profile_enable_event(event);
20c8928a
LZ
74 break;
75 }
ac199db0 76 }
20c8928a 77 mutex_unlock(&event_mutex);
ac199db0 78
20c8928a 79 return ret;
ac199db0
PZ
80}
81
e5e25cf4
FW
82static void ftrace_profile_disable_event(struct ftrace_event_call *event)
83{
ce71b9df 84 char *buf, *nmi_buf;
20ab4425 85
e00bf2ec 86 if (--event->profile_count > 0)
e5e25cf4
FW
87 return;
88
d7a4b414 89 event->profile_disable(event);
20ab4425
FW
90
91 if (!--total_profile_count) {
444a2a3b
FW
92 buf = perf_trace_buf;
93 rcu_assign_pointer(perf_trace_buf, NULL);
20ab4425 94
444a2a3b
FW
95 nmi_buf = perf_trace_buf_nmi;
96 rcu_assign_pointer(perf_trace_buf_nmi, NULL);
20ab4425
FW
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 }
e5e25cf4
FW
107}
108
ac199db0
PZ
109void ftrace_profile_disable(int event_id)
110{
111 struct ftrace_event_call *event;
112
20c8928a 113 mutex_lock(&event_mutex);
a59fd602 114 list_for_each_entry(event, &ftrace_events, list) {
20c8928a 115 if (event->id == event_id) {
e5e25cf4 116 ftrace_profile_disable_event(event);
558e6547 117 module_put(event->mod);
20c8928a
LZ
118 break;
119 }
ac199db0 120 }
20c8928a 121 mutex_unlock(&event_mutex);
ac199db0 122}