Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  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
 25struct oprofile_operations oprofile_ops;
 26
 27unsigned long oprofile_started;
 28unsigned long oprofile_backtrace_depth;
 29static unsigned long is_setup;
 30static DEFINE_MUTEX(start_mutex);
 31
 32/* timer
 33   0 - use performance monitoring hardware if available
 34   1 - use the timer int mechanism regardless
 35 */
 36static int timer = 0;
 37
 38int 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	}
 71do_generic:
 72	if ((err = sync_start()))
 73		goto out3;
 74
 75post_sync:
 76	is_setup = 1;
 77	mutex_unlock(&start_mutex);
 78	return 0;
 79
 80out3:
 81	if (oprofile_ops.shutdown)
 82		oprofile_ops.shutdown();
 83out2:
 84	free_event_buffer();
 85out1:
 86	free_cpu_buffers();
 87out:
 88	mutex_unlock(&start_mutex);
 89	return err;
 90}
 91
 92#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
 93
 94static void switch_worker(struct work_struct *work);
 95static DECLARE_DELAYED_WORK(switch_work, switch_worker);
 96
 97static void start_switch_worker(void)
 98{
 99	if (oprofile_ops.switch_events)
100		schedule_delayed_work(&switch_work, oprofile_time_slice);
101}
102
103static void stop_switch_worker(void)
104{
105	cancel_delayed_work_sync(&switch_work);
106}
107
108static 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 */
118int 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
143out:
144	mutex_unlock(&start_mutex);
145	return err;
146
147}
148
149#else
150
151static inline void start_switch_worker(void) { }
152static inline void stop_switch_worker(void) { }
153
154#endif
155
156/* Actually start profiling (echo 1>/dev/oprofile/enable) */
157int 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;
179out:
180	mutex_unlock(&start_mutex);
181	return err;
182}
183
184
185/* echo 0>/dev/oprofile/enable */
186void 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();
198out:
199	mutex_unlock(&start_mutex);
200}
201
202
203void 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	}
217do_generic:
218	sync_stop();
219post_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
228int 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
242static int timer_mode;
243
244static 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
270static void __exit oprofile_exit(void)
271{
272	oprofilefs_unregister();
273	if (!timer_mode)
274		oprofile_arch_exit();
275}
276
277
278module_init(oprofile_init);
279module_exit(oprofile_exit);
280
281module_param_named(timer, timer, int, 0644);
282MODULE_PARM_DESC(timer, "force use of timer interrupt");
283
284MODULE_LICENSE("GPL");
285MODULE_AUTHOR("John Levon <levon@movementarian.org>");
286MODULE_DESCRIPTION("OProfile system profiler");