oprofile: Implement performance counter multiplexing
[linux-2.6.git] / drivers / oprofile / oprof.c
1 /**
2  * @file oprof.c
3  *
4  * @remark Copyright 2002 OProfile authors
5  * @remark Read the file COPYING
6  *
7  * @author John Levon <levon@movementarian.org>
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/oprofile.h>
14 #include <linux/moduleparam.h>
15 #include <linux/workqueue.h>
16 #include <linux/time.h>
17 #include <asm/mutex.h>
18
19 #include "oprof.h"
20 #include "event_buffer.h"
21 #include "cpu_buffer.h"
22 #include "buffer_sync.h"
23 #include "oprofile_stats.h"
24
25 struct oprofile_operations oprofile_ops;
26
27 unsigned long oprofile_started;
28 unsigned long oprofile_backtrace_depth;
29 static unsigned long is_setup;
30 static DEFINE_MUTEX(start_mutex);
31
32 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
33
34 static void switch_worker(struct work_struct *work);
35 static DECLARE_DELAYED_WORK(switch_work, switch_worker);
36 unsigned long timeout_jiffies;
37 #define MULTIPLEXING_TIMER_DEFAULT 1
38
39 #endif
40
41 /* timer
42    0 - use performance monitoring hardware if available
43    1 - use the timer int mechanism regardless
44  */
45 static int timer = 0;
46
47 int oprofile_setup(void)
48 {
49         int err;
50
51         mutex_lock(&start_mutex);
52
53         if ((err = alloc_cpu_buffers()))
54                 goto out;
55
56         if ((err = alloc_event_buffer()))
57                 goto out1;
58
59         if (oprofile_ops.setup && (err = oprofile_ops.setup()))
60                 goto out2;
61
62         /* Note even though this starts part of the
63          * profiling overhead, it's necessary to prevent
64          * us missing task deaths and eventually oopsing
65          * when trying to process the event buffer.
66          */
67         if (oprofile_ops.sync_start) {
68                 int sync_ret = oprofile_ops.sync_start();
69                 switch (sync_ret) {
70                 case 0:
71                         goto post_sync;
72                 case 1:
73                         goto do_generic;
74                 case -1:
75                         goto out3;
76                 default:
77                         goto out3;
78                 }
79         }
80 do_generic:
81         if ((err = sync_start()))
82                 goto out3;
83
84 post_sync:
85         is_setup = 1;
86         mutex_unlock(&start_mutex);
87         return 0;
88
89 out3:
90         if (oprofile_ops.shutdown)
91                 oprofile_ops.shutdown();
92 out2:
93         free_event_buffer();
94 out1:
95         free_cpu_buffers();
96 out:
97         mutex_unlock(&start_mutex);
98         return err;
99 }
100
101 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
102
103 static void start_switch_worker(void)
104 {
105         schedule_delayed_work(&switch_work, timeout_jiffies);
106 }
107
108 static void switch_worker(struct work_struct *work)
109 {
110         if (!oprofile_ops.switch_events())
111                 start_switch_worker();
112 }
113
114 #endif
115
116 /* Actually start profiling (echo 1>/dev/oprofile/enable) */
117 int oprofile_start(void)
118 {
119         int err = -EINVAL;
120
121         mutex_lock(&start_mutex);
122
123         if (!is_setup)
124                 goto out;
125
126         err = 0;
127
128         if (oprofile_started)
129                 goto out;
130
131         oprofile_reset_stats();
132
133         if ((err = oprofile_ops.start()))
134                 goto out;
135
136 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
137         if (oprofile_ops.switch_events)
138                 start_switch_worker();
139 #endif
140
141         oprofile_started = 1;
142 out:
143         mutex_unlock(&start_mutex);
144         return err;
145 }
146
147
148 /* echo 0>/dev/oprofile/enable */
149 void oprofile_stop(void)
150 {
151         mutex_lock(&start_mutex);
152         if (!oprofile_started)
153                 goto out;
154         oprofile_ops.stop();
155         oprofile_started = 0;
156
157 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
158         cancel_delayed_work_sync(&switch_work);
159 #endif
160
161         /* wake up the daemon to read what remains */
162         wake_up_buffer_waiter();
163 out:
164         mutex_unlock(&start_mutex);
165 }
166
167
168 void oprofile_shutdown(void)
169 {
170         mutex_lock(&start_mutex);
171         if (oprofile_ops.sync_stop) {
172                 int sync_ret = oprofile_ops.sync_stop();
173                 switch (sync_ret) {
174                 case 0:
175                         goto post_sync;
176                 case 1:
177                         goto do_generic;
178                 default:
179                         goto post_sync;
180                 }
181         }
182 do_generic:
183         sync_stop();
184 post_sync:
185         if (oprofile_ops.shutdown)
186                 oprofile_ops.shutdown();
187         is_setup = 0;
188         free_event_buffer();
189         free_cpu_buffers();
190         mutex_unlock(&start_mutex);
191 }
192
193 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
194
195 /* User inputs in ms, converts to jiffies */
196 int oprofile_set_timeout(unsigned long val_msec)
197 {
198         int err = 0;
199
200         mutex_lock(&start_mutex);
201
202         if (oprofile_started) {
203                 err = -EBUSY;
204                 goto out;
205         }
206
207         if (!oprofile_ops.switch_events) {
208                 err = -EINVAL;
209                 goto out;
210         }
211
212         timeout_jiffies = msecs_to_jiffies(val_msec);
213         if (timeout_jiffies == MAX_JIFFY_OFFSET)
214                 timeout_jiffies = msecs_to_jiffies(MULTIPLEXING_TIMER_DEFAULT);
215
216 out:
217         mutex_unlock(&start_mutex);
218         return err;
219
220 }
221
222 #endif
223
224 int oprofile_set_backtrace(unsigned long val)
225 {
226         int err = 0;
227
228         mutex_lock(&start_mutex);
229
230         if (oprofile_started) {
231                 err = -EBUSY;
232                 goto out;
233         }
234
235         if (!oprofile_ops.backtrace) {
236                 err = -EINVAL;
237                 goto out;
238         }
239
240         oprofile_backtrace_depth = val;
241
242 out:
243         mutex_unlock(&start_mutex);
244         return err;
245 }
246
247 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
248
249 static void __init oprofile_multiplexing_init(void)
250 {
251         timeout_jiffies = msecs_to_jiffies(MULTIPLEXING_TIMER_DEFAULT);
252 }
253
254 #endif
255
256 static int __init oprofile_init(void)
257 {
258         int err;
259
260 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
261         oprofile_multiplexing_init();
262 #endif
263
264         err = oprofile_arch_init(&oprofile_ops);
265
266         if (err < 0 || timer) {
267                 printk(KERN_INFO "oprofile: using timer interrupt.\n");
268                 oprofile_timer_init(&oprofile_ops);
269         }
270
271         err = oprofilefs_register();
272         if (err)
273                 oprofile_arch_exit();
274
275         return err;
276 }
277
278
279 static void __exit oprofile_exit(void)
280 {
281         oprofilefs_unregister();
282         oprofile_arch_exit();
283 }
284
285
286 module_init(oprofile_init);
287 module_exit(oprofile_exit);
288
289 module_param_named(timer, timer, int, 0644);
290 MODULE_PARM_DESC(timer, "force use of timer interrupt");
291
292 MODULE_LICENSE("GPL");
293 MODULE_AUTHOR("John Levon <levon@movementarian.org>");
294 MODULE_DESCRIPTION("OProfile system profiler");