oprofile: oprofile_set_timeout(), return with error for invalid args
[safe/jmp/linux-2.6] / 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         unsigned long time_slice;
200
201         mutex_lock(&start_mutex);
202
203         if (oprofile_started) {
204                 err = -EBUSY;
205                 goto out;
206         }
207
208         if (!oprofile_ops.switch_events) {
209                 err = -EINVAL;
210                 goto out;
211         }
212
213         time_slice = msecs_to_jiffies(val_msec);
214         if (time_slice == MAX_JIFFY_OFFSET) {
215                 err = -EINVAL;
216                 goto out;
217         }
218
219         timeout_jiffies = time_slice;
220
221 out:
222         mutex_unlock(&start_mutex);
223         return err;
224
225 }
226
227 #endif
228
229 int oprofile_set_backtrace(unsigned long val)
230 {
231         int err = 0;
232
233         mutex_lock(&start_mutex);
234
235         if (oprofile_started) {
236                 err = -EBUSY;
237                 goto out;
238         }
239
240         if (!oprofile_ops.backtrace) {
241                 err = -EINVAL;
242                 goto out;
243         }
244
245         oprofile_backtrace_depth = val;
246
247 out:
248         mutex_unlock(&start_mutex);
249         return err;
250 }
251
252 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
253
254 static void __init oprofile_multiplexing_init(void)
255 {
256         timeout_jiffies = msecs_to_jiffies(MULTIPLEXING_TIMER_DEFAULT);
257 }
258
259 #endif
260
261 static int __init oprofile_init(void)
262 {
263         int err;
264
265 #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
266         oprofile_multiplexing_init();
267 #endif
268
269         err = oprofile_arch_init(&oprofile_ops);
270
271         if (err < 0 || timer) {
272                 printk(KERN_INFO "oprofile: using timer interrupt.\n");
273                 oprofile_timer_init(&oprofile_ops);
274         }
275
276         err = oprofilefs_register();
277         if (err)
278                 oprofile_arch_exit();
279
280         return err;
281 }
282
283
284 static void __exit oprofile_exit(void)
285 {
286         oprofilefs_unregister();
287         oprofile_arch_exit();
288 }
289
290
291 module_init(oprofile_init);
292 module_exit(oprofile_exit);
293
294 module_param_named(timer, timer, int, 0644);
295 MODULE_PARM_DESC(timer, "force use of timer interrupt");
296
297 MODULE_LICENSE("GPL");
298 MODULE_AUTHOR("John Levon <levon@movementarian.org>");
299 MODULE_DESCRIPTION("OProfile system profiler");