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