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