blob: 2b33de7164123956b852461510a2901df376f17e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/**
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>
Jason Yeh4d4036e2009-07-08 13:49:38 +020015#include <linux/workqueue.h>
16#include <linux/time.h>
Markus Armbruster59cc1852006-06-25 05:47:33 -070017#include <asm/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018
19#include "oprof.h"
20#include "event_buffer.h"
21#include "cpu_buffer.h"
22#include "buffer_sync.h"
23#include "oprofile_stats.h"
Robert Richterc92960f2008-09-05 17:12:36 +020024
Linus Torvalds1da177e2005-04-16 15:20:36 -070025struct oprofile_operations oprofile_ops;
26
27unsigned long oprofile_started;
Robert Richterbd2172f2008-12-16 16:19:54 +010028unsigned long oprofile_backtrace_depth;
Robert Richter4c168ea2008-09-24 11:08:52 +020029static unsigned long is_setup;
30static DEFINE_MUTEX(start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Jason Yeh4d4036e2009-07-08 13:49:38 +020032#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
33
34static void switch_worker(struct work_struct *work);
35static DECLARE_DELAYED_WORK(switch_work, switch_worker);
Robert Richterafe1b50f2009-07-15 15:19:29 +020036#define TIME_SLICE_DEFAULT 1
Jason Yeh4d4036e2009-07-08 13:49:38 +020037
38#endif
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040/* timer
41 0 - use performance monitoring hardware if available
42 1 - use the timer int mechanism regardless
43 */
44static int timer = 0;
45
46int oprofile_setup(void)
47{
48 int err;
Robert Richterc92960f2008-09-05 17:12:36 +020049
Markus Armbruster59cc1852006-06-25 05:47:33 -070050 mutex_lock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52 if ((err = alloc_cpu_buffers()))
53 goto out;
54
55 if ((err = alloc_event_buffer()))
56 goto out1;
Robert Richterc92960f2008-09-05 17:12:36 +020057
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 if (oprofile_ops.setup && (err = oprofile_ops.setup()))
59 goto out2;
Robert Richterc92960f2008-09-05 17:12:36 +020060
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 /* Note even though this starts part of the
62 * profiling overhead, it's necessary to prevent
63 * us missing task deaths and eventually oopsing
64 * when trying to process the event buffer.
65 */
Bob Nelson14748552007-07-20 21:39:53 +020066 if (oprofile_ops.sync_start) {
67 int sync_ret = oprofile_ops.sync_start();
68 switch (sync_ret) {
69 case 0:
70 goto post_sync;
71 case 1:
72 goto do_generic;
73 case -1:
74 goto out3;
75 default:
76 goto out3;
77 }
78 }
79do_generic:
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 if ((err = sync_start()))
81 goto out3;
82
Bob Nelson14748552007-07-20 21:39:53 +020083post_sync:
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 is_setup = 1;
Markus Armbruster59cc1852006-06-25 05:47:33 -070085 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 return 0;
Robert Richterc92960f2008-09-05 17:12:36 +020087
Linus Torvalds1da177e2005-04-16 15:20:36 -070088out3:
89 if (oprofile_ops.shutdown)
90 oprofile_ops.shutdown();
91out2:
92 free_event_buffer();
93out1:
94 free_cpu_buffers();
95out:
Markus Armbruster59cc1852006-06-25 05:47:33 -070096 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 return err;
98}
99
Jason Yeh4d4036e2009-07-08 13:49:38 +0200100#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
101
102static void start_switch_worker(void)
103{
Robert Richterafe1b50f2009-07-15 15:19:29 +0200104 schedule_delayed_work(&switch_work, oprofile_time_slice);
Jason Yeh4d4036e2009-07-08 13:49:38 +0200105}
106
107static void switch_worker(struct work_struct *work)
108{
109 if (!oprofile_ops.switch_events())
110 start_switch_worker();
111}
112
113#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
115/* Actually start profiling (echo 1>/dev/oprofile/enable) */
116int oprofile_start(void)
117{
118 int err = -EINVAL;
Robert Richterc92960f2008-09-05 17:12:36 +0200119
Markus Armbruster59cc1852006-06-25 05:47:33 -0700120 mutex_lock(&start_mutex);
Robert Richter6a180372008-10-16 15:01:40 +0200121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 if (!is_setup)
123 goto out;
124
Robert Richterc92960f2008-09-05 17:12:36 +0200125 err = 0;
126
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 if (oprofile_started)
128 goto out;
Robert Richterc92960f2008-09-05 17:12:36 +0200129
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 oprofile_reset_stats();
131
132 if ((err = oprofile_ops.start()))
133 goto out;
134
Jason Yeh4d4036e2009-07-08 13:49:38 +0200135#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
136 if (oprofile_ops.switch_events)
137 start_switch_worker();
138#endif
139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 oprofile_started = 1;
141out:
Markus Armbruster59cc1852006-06-25 05:47:33 -0700142 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 return err;
144}
145
Robert Richterc92960f2008-09-05 17:12:36 +0200146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147/* echo 0>/dev/oprofile/enable */
148void oprofile_stop(void)
149{
Markus Armbruster59cc1852006-06-25 05:47:33 -0700150 mutex_lock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 if (!oprofile_started)
152 goto out;
153 oprofile_ops.stop();
154 oprofile_started = 0;
Jason Yeh4d4036e2009-07-08 13:49:38 +0200155
156#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
157 cancel_delayed_work_sync(&switch_work);
158#endif
159
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 /* wake up the daemon to read what remains */
161 wake_up_buffer_waiter();
162out:
Markus Armbruster59cc1852006-06-25 05:47:33 -0700163 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164}
165
166
167void oprofile_shutdown(void)
168{
Markus Armbruster59cc1852006-06-25 05:47:33 -0700169 mutex_lock(&start_mutex);
Bob Nelson14748552007-07-20 21:39:53 +0200170 if (oprofile_ops.sync_stop) {
171 int sync_ret = oprofile_ops.sync_stop();
172 switch (sync_ret) {
173 case 0:
174 goto post_sync;
175 case 1:
176 goto do_generic;
177 default:
178 goto post_sync;
179 }
180 }
181do_generic:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 sync_stop();
Bob Nelson14748552007-07-20 21:39:53 +0200183post_sync:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 if (oprofile_ops.shutdown)
185 oprofile_ops.shutdown();
186 is_setup = 0;
187 free_event_buffer();
188 free_cpu_buffers();
Markus Armbruster59cc1852006-06-25 05:47:33 -0700189 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190}
191
Jason Yeh4d4036e2009-07-08 13:49:38 +0200192#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
193
194/* User inputs in ms, converts to jiffies */
195int oprofile_set_timeout(unsigned long val_msec)
196{
197 int err = 0;
Robert Richter2051cad2009-07-15 15:44:18 +0200198 unsigned long time_slice;
Jason Yeh4d4036e2009-07-08 13:49:38 +0200199
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
Robert Richter2051cad2009-07-15 15:44:18 +0200212 time_slice = msecs_to_jiffies(val_msec);
213 if (time_slice == MAX_JIFFY_OFFSET) {
214 err = -EINVAL;
215 goto out;
216 }
217
Robert Richterafe1b50f2009-07-15 15:19:29 +0200218 oprofile_time_slice = time_slice;
Jason Yeh4d4036e2009-07-08 13:49:38 +0200219
220out:
221 mutex_unlock(&start_mutex);
222 return err;
223
224}
225
226#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
228int oprofile_set_backtrace(unsigned long val)
229{
230 int err = 0;
231
Markus Armbruster59cc1852006-06-25 05:47:33 -0700232 mutex_lock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
234 if (oprofile_started) {
235 err = -EBUSY;
236 goto out;
237 }
238
239 if (!oprofile_ops.backtrace) {
240 err = -EINVAL;
241 goto out;
242 }
243
Robert Richterbd2172f2008-12-16 16:19:54 +0100244 oprofile_backtrace_depth = val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
246out:
Markus Armbruster59cc1852006-06-25 05:47:33 -0700247 mutex_unlock(&start_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 return err;
249}
250
Jason Yeh4d4036e2009-07-08 13:49:38 +0200251#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
252
253static void __init oprofile_multiplexing_init(void)
254{
Robert Richterafe1b50f2009-07-15 15:19:29 +0200255 oprofile_time_slice = msecs_to_jiffies(TIME_SLICE_DEFAULT);
Jason Yeh4d4036e2009-07-08 13:49:38 +0200256}
257
258#endif
259
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260static int __init oprofile_init(void)
261{
262 int err;
263
Jason Yeh4d4036e2009-07-08 13:49:38 +0200264#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
265 oprofile_multiplexing_init();
266#endif
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 err = oprofile_arch_init(&oprofile_ops);
269
270 if (err < 0 || timer) {
271 printk(KERN_INFO "oprofile: using timer interrupt.\n");
272 oprofile_timer_init(&oprofile_ops);
273 }
274
275 err = oprofilefs_register();
Robert Richter4c50d9e2009-01-22 14:14:14 +0100276 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 oprofile_arch_exit();
278
279 return err;
280}
281
282
283static void __exit oprofile_exit(void)
284{
285 oprofilefs_unregister();
286 oprofile_arch_exit();
287}
288
Robert Richterc92960f2008-09-05 17:12:36 +0200289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290module_init(oprofile_init);
291module_exit(oprofile_exit);
292
293module_param_named(timer, timer, int, 0644);
294MODULE_PARM_DESC(timer, "force use of timer interrupt");
Robert Richterc92960f2008-09-05 17:12:36 +0200295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296MODULE_LICENSE("GPL");
297MODULE_AUTHOR("John Levon <levon@movementarian.org>");
298MODULE_DESCRIPTION("OProfile system profiler");