Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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> | ||||
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 15 | #include <linux/workqueue.h> |
16 | #include <linux/time.h> | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 17 | #include <asm/mutex.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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" | ||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 24 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 | struct oprofile_operations oprofile_ops; |
26 | |||||
27 | unsigned long oprofile_started; | ||||
Robert Richter | bd2172f | 2008-12-16 16:19:54 +0100 | [diff] [blame] | 28 | unsigned long oprofile_backtrace_depth; |
Robert Richter | 4c168ea | 2008-09-24 11:08:52 +0200 | [diff] [blame] | 29 | static unsigned long is_setup; |
30 | static DEFINE_MUTEX(start_mutex); | ||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 | |
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 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); | ||||
Robert Richter | afe1b50f | 2009-07-15 15:19:29 +0200 | [diff] [blame^] | 36 | #define TIME_SLICE_DEFAULT 1 |
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 37 | |
38 | #endif | ||||
39 | |||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | /* timer |
41 | 0 - use performance monitoring hardware if available | ||||
42 | 1 - use the timer int mechanism regardless | ||||
43 | */ | ||||
44 | static int timer = 0; | ||||
45 | |||||
46 | int oprofile_setup(void) | ||||
47 | { | ||||
48 | int err; | ||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 49 | |
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 50 | mutex_lock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | |
52 | if ((err = alloc_cpu_buffers())) | ||||
53 | goto out; | ||||
54 | |||||
55 | if ((err = alloc_event_buffer())) | ||||
56 | goto out1; | ||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 57 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 | if (oprofile_ops.setup && (err = oprofile_ops.setup())) |
59 | goto out2; | ||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 60 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 61 | /* 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 Nelson | 1474855 | 2007-07-20 21:39:53 +0200 | [diff] [blame] | 66 | 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 | } | ||||
79 | do_generic: | ||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 | if ((err = sync_start())) |
81 | goto out3; | ||||
82 | |||||
Bob Nelson | 1474855 | 2007-07-20 21:39:53 +0200 | [diff] [blame] | 83 | post_sync: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | is_setup = 1; |
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 85 | mutex_unlock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 86 | return 0; |
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 87 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 88 | out3: |
89 | if (oprofile_ops.shutdown) | ||||
90 | oprofile_ops.shutdown(); | ||||
91 | out2: | ||||
92 | free_event_buffer(); | ||||
93 | out1: | ||||
94 | free_cpu_buffers(); | ||||
95 | out: | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 96 | mutex_unlock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | return err; |
98 | } | ||||
99 | |||||
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 100 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
101 | |||||
102 | static void start_switch_worker(void) | ||||
103 | { | ||||
Robert Richter | afe1b50f | 2009-07-15 15:19:29 +0200 | [diff] [blame^] | 104 | schedule_delayed_work(&switch_work, oprofile_time_slice); |
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 105 | } |
106 | |||||
107 | static void switch_worker(struct work_struct *work) | ||||
108 | { | ||||
109 | if (!oprofile_ops.switch_events()) | ||||
110 | start_switch_worker(); | ||||
111 | } | ||||
112 | |||||
113 | #endif | ||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 114 | |
115 | /* Actually start profiling (echo 1>/dev/oprofile/enable) */ | ||||
116 | int oprofile_start(void) | ||||
117 | { | ||||
118 | int err = -EINVAL; | ||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 119 | |
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 120 | mutex_lock(&start_mutex); |
Robert Richter | 6a18037 | 2008-10-16 15:01:40 +0200 | [diff] [blame] | 121 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 122 | if (!is_setup) |
123 | goto out; | ||||
124 | |||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 125 | err = 0; |
126 | |||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 127 | if (oprofile_started) |
128 | goto out; | ||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 129 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 130 | oprofile_reset_stats(); |
131 | |||||
132 | if ((err = oprofile_ops.start())) | ||||
133 | goto out; | ||||
134 | |||||
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 135 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
136 | if (oprofile_ops.switch_events) | ||||
137 | start_switch_worker(); | ||||
138 | #endif | ||||
139 | |||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 140 | oprofile_started = 1; |
141 | out: | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 142 | mutex_unlock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | return err; |
144 | } | ||||
145 | |||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 146 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 | /* echo 0>/dev/oprofile/enable */ |
148 | void oprofile_stop(void) | ||||
149 | { | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 150 | mutex_lock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 151 | if (!oprofile_started) |
152 | goto out; | ||||
153 | oprofile_ops.stop(); | ||||
154 | oprofile_started = 0; | ||||
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 155 | |
156 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | ||||
157 | cancel_delayed_work_sync(&switch_work); | ||||
158 | #endif | ||||
159 | |||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 | /* wake up the daemon to read what remains */ |
161 | wake_up_buffer_waiter(); | ||||
162 | out: | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 163 | mutex_unlock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | } |
165 | |||||
166 | |||||
167 | void oprofile_shutdown(void) | ||||
168 | { | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 169 | mutex_lock(&start_mutex); |
Bob Nelson | 1474855 | 2007-07-20 21:39:53 +0200 | [diff] [blame] | 170 | 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 | } | ||||
181 | do_generic: | ||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 182 | sync_stop(); |
Bob Nelson | 1474855 | 2007-07-20 21:39:53 +0200 | [diff] [blame] | 183 | post_sync: |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | if (oprofile_ops.shutdown) |
185 | oprofile_ops.shutdown(); | ||||
186 | is_setup = 0; | ||||
187 | free_event_buffer(); | ||||
188 | free_cpu_buffers(); | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 189 | mutex_unlock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 190 | } |
191 | |||||
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 192 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
193 | |||||
194 | /* User inputs in ms, converts to jiffies */ | ||||
195 | int oprofile_set_timeout(unsigned long val_msec) | ||||
196 | { | ||||
197 | int err = 0; | ||||
Robert Richter | 2051cad | 2009-07-15 15:44:18 +0200 | [diff] [blame] | 198 | unsigned long time_slice; |
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 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 | |||||
Robert Richter | 2051cad | 2009-07-15 15:44:18 +0200 | [diff] [blame] | 212 | time_slice = msecs_to_jiffies(val_msec); |
213 | if (time_slice == MAX_JIFFY_OFFSET) { | ||||
214 | err = -EINVAL; | ||||
215 | goto out; | ||||
216 | } | ||||
217 | |||||
Robert Richter | afe1b50f | 2009-07-15 15:19:29 +0200 | [diff] [blame^] | 218 | oprofile_time_slice = time_slice; |
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 219 | |
220 | out: | ||||
221 | mutex_unlock(&start_mutex); | ||||
222 | return err; | ||||
223 | |||||
224 | } | ||||
225 | |||||
226 | #endif | ||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 227 | |
228 | int oprofile_set_backtrace(unsigned long val) | ||||
229 | { | ||||
230 | int err = 0; | ||||
231 | |||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 232 | mutex_lock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 233 | |
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 Richter | bd2172f | 2008-12-16 16:19:54 +0100 | [diff] [blame] | 244 | oprofile_backtrace_depth = val; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 245 | |
246 | out: | ||||
Markus Armbruster | 59cc185 | 2006-06-25 05:47:33 -0700 | [diff] [blame] | 247 | mutex_unlock(&start_mutex); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 248 | return err; |
249 | } | ||||
250 | |||||
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 251 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
252 | |||||
253 | static void __init oprofile_multiplexing_init(void) | ||||
254 | { | ||||
Robert Richter | afe1b50f | 2009-07-15 15:19:29 +0200 | [diff] [blame^] | 255 | oprofile_time_slice = msecs_to_jiffies(TIME_SLICE_DEFAULT); |
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 256 | } |
257 | |||||
258 | #endif | ||||
259 | |||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 260 | static int __init oprofile_init(void) |
261 | { | ||||
262 | int err; | ||||
263 | |||||
Jason Yeh | 4d4036e | 2009-07-08 13:49:38 +0200 | [diff] [blame] | 264 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
265 | oprofile_multiplexing_init(); | ||||
266 | #endif | ||||
267 | |||||
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 268 | 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 Richter | 4c50d9e | 2009-01-22 14:14:14 +0100 | [diff] [blame] | 276 | if (err) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 277 | oprofile_arch_exit(); |
278 | |||||
279 | return err; | ||||
280 | } | ||||
281 | |||||
282 | |||||
283 | static void __exit oprofile_exit(void) | ||||
284 | { | ||||
285 | oprofilefs_unregister(); | ||||
286 | oprofile_arch_exit(); | ||||
287 | } | ||||
288 | |||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 289 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 290 | module_init(oprofile_init); |
291 | module_exit(oprofile_exit); | ||||
292 | |||||
293 | module_param_named(timer, timer, int, 0644); | ||||
294 | MODULE_PARM_DESC(timer, "force use of timer interrupt"); | ||||
Robert Richter | c92960f | 2008-09-05 17:12:36 +0200 | [diff] [blame] | 295 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | MODULE_LICENSE("GPL"); |
297 | MODULE_AUTHOR("John Levon <levon@movementarian.org>"); | ||||
298 | MODULE_DESCRIPTION("OProfile system profiler"); |