blob: e812f1c1264cffb4ca36803dd0b11fc87f90d450 [file] [log] [blame]
Peter Zijlstraac199db2009-03-19 20:26:15 +01001/*
2 * trace event based perf counter profiling
3 *
4 * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra <pzijlstr@redhat.com>
5 *
6 */
7
Li Zefan558e6542009-08-24 12:19:47 +08008#include <linux/module.h>
Peter Zijlstraac199db2009-03-19 20:26:15 +01009#include "trace.h"
10
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020011/*
12 * We can't use a size but a type in alloc_percpu()
13 * So let's create a dummy type that matches the desired size
14 */
15typedef struct {char buf[FTRACE_MAX_PROFILE_SIZE];} profile_buf_t;
16
17char *trace_profile_buf;
Peter Zijlstra05bafda2009-09-20 12:34:38 +020018EXPORT_SYMBOL_GPL(trace_profile_buf);
19
20char *trace_profile_buf_nmi;
21EXPORT_SYMBOL_GPL(trace_profile_buf_nmi);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020022
23/* Count the events in use (per event id, not per instance) */
24static int total_profile_count;
25
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020026static int ftrace_profile_enable_event(struct ftrace_event_call *event)
27{
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020028 char *buf;
29 int ret = -ENOMEM;
30
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020031 if (atomic_inc_return(&event->profile_count))
32 return 0;
33
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020034 if (!total_profile_count++) {
35 buf = (char *)alloc_percpu(profile_buf_t);
36 if (!buf)
37 goto fail_buf;
38
39 rcu_assign_pointer(trace_profile_buf, buf);
40
41 buf = (char *)alloc_percpu(profile_buf_t);
42 if (!buf)
43 goto fail_buf_nmi;
44
45 rcu_assign_pointer(trace_profile_buf_nmi, buf);
46 }
47
Frederic Weisbeckerd7a4b412009-09-23 23:08:43 +020048 ret = event->profile_enable(event);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020049 if (!ret)
50 return 0;
51
52 kfree(trace_profile_buf_nmi);
53fail_buf_nmi:
54 kfree(trace_profile_buf);
55fail_buf:
56 total_profile_count--;
57 atomic_dec(&event->profile_count);
58
59 return ret;
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020060}
61
Peter Zijlstraac199db2009-03-19 20:26:15 +010062int ftrace_profile_enable(int event_id)
63{
64 struct ftrace_event_call *event;
Li Zefan20c89282009-05-06 10:33:45 +080065 int ret = -EINVAL;
Peter Zijlstraac199db2009-03-19 20:26:15 +010066
Li Zefan20c89282009-05-06 10:33:45 +080067 mutex_lock(&event_mutex);
Steven Rostedta59fd602009-04-10 13:52:20 -040068 list_for_each_entry(event, &ftrace_events, list) {
Li Zefan558e6542009-08-24 12:19:47 +080069 if (event->id == event_id && event->profile_enable &&
70 try_module_get(event->mod)) {
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020071 ret = ftrace_profile_enable_event(event);
Li Zefan20c89282009-05-06 10:33:45 +080072 break;
73 }
Peter Zijlstraac199db2009-03-19 20:26:15 +010074 }
Li Zefan20c89282009-05-06 10:33:45 +080075 mutex_unlock(&event_mutex);
Peter Zijlstraac199db2009-03-19 20:26:15 +010076
Li Zefan20c89282009-05-06 10:33:45 +080077 return ret;
Peter Zijlstraac199db2009-03-19 20:26:15 +010078}
79
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020080static void ftrace_profile_disable_event(struct ftrace_event_call *event)
81{
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020082 char *buf, *nmi_buf;
83
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020084 if (!atomic_add_negative(-1, &event->profile_count))
85 return;
86
Frederic Weisbeckerd7a4b412009-09-23 23:08:43 +020087 event->profile_disable(event);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020088
89 if (!--total_profile_count) {
90 buf = trace_profile_buf;
91 rcu_assign_pointer(trace_profile_buf, NULL);
92
93 nmi_buf = trace_profile_buf_nmi;
94 rcu_assign_pointer(trace_profile_buf_nmi, NULL);
95
96 /*
97 * Ensure every events in profiling have finished before
98 * releasing the buffers
99 */
100 synchronize_sched();
101
102 free_percpu(buf);
103 free_percpu(nmi_buf);
104 }
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +0200105}
106
Peter Zijlstraac199db2009-03-19 20:26:15 +0100107void ftrace_profile_disable(int event_id)
108{
109 struct ftrace_event_call *event;
110
Li Zefan20c89282009-05-06 10:33:45 +0800111 mutex_lock(&event_mutex);
Steven Rostedta59fd602009-04-10 13:52:20 -0400112 list_for_each_entry(event, &ftrace_events, list) {
Li Zefan20c89282009-05-06 10:33:45 +0800113 if (event->id == event_id) {
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +0200114 ftrace_profile_disable_event(event);
Li Zefan558e6542009-08-24 12:19:47 +0800115 module_put(event->mod);
Li Zefan20c89282009-05-06 10:33:45 +0800116 break;
117 }
Peter Zijlstraac199db2009-03-19 20:26:15 +0100118 }
Li Zefan20c89282009-05-06 10:33:45 +0800119 mutex_unlock(&event_mutex);
Peter Zijlstraac199db2009-03-19 20:26:15 +0100120}