123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /**
- * @file oprof.c
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author John Levon <levon@movementarian.org>
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/oprofile.h>
- #include <linux/moduleparam.h>
- #include <linux/workqueue.h>
- #include <linux/time.h>
- #include <linux/mutex.h>
- #include "oprof.h"
- #include "event_buffer.h"
- #include "cpu_buffer.h"
- #include "buffer_sync.h"
- #include "oprofile_stats.h"
- struct oprofile_operations oprofile_ops;
- unsigned long oprofile_started;
- unsigned long oprofile_backtrace_depth;
- static unsigned long is_setup;
- static DEFINE_MUTEX(start_mutex);
- /* timer
- 0 - use performance monitoring hardware if available
- 1 - use the timer int mechanism regardless
- */
- static int timer = 0;
- int oprofile_setup(void)
- {
- int err;
- mutex_lock(&start_mutex);
- if ((err = alloc_cpu_buffers()))
- goto out;
- if ((err = alloc_event_buffer()))
- goto out1;
- if (oprofile_ops.setup && (err = oprofile_ops.setup()))
- goto out2;
- /* Note even though this starts part of the
- * profiling overhead, it's necessary to prevent
- * us missing task deaths and eventually oopsing
- * when trying to process the event buffer.
- */
- if (oprofile_ops.sync_start) {
- int sync_ret = oprofile_ops.sync_start();
- switch (sync_ret) {
- case 0:
- goto post_sync;
- case 1:
- goto do_generic;
- case -1:
- goto out3;
- default:
- goto out3;
- }
- }
- do_generic:
- if ((err = sync_start()))
- goto out3;
- post_sync:
- is_setup = 1;
- mutex_unlock(&start_mutex);
- return 0;
- out3:
- if (oprofile_ops.shutdown)
- oprofile_ops.shutdown();
- out2:
- free_event_buffer();
- out1:
- free_cpu_buffers();
- out:
- mutex_unlock(&start_mutex);
- return err;
- }
- #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
- static void switch_worker(struct work_struct *work);
- static DECLARE_DELAYED_WORK(switch_work, switch_worker);
- static void start_switch_worker(void)
- {
- if (oprofile_ops.switch_events)
- schedule_delayed_work(&switch_work, oprofile_time_slice);
- }
- static void stop_switch_worker(void)
- {
- cancel_delayed_work_sync(&switch_work);
- }
- static void switch_worker(struct work_struct *work)
- {
- if (oprofile_ops.switch_events())
- return;
- atomic_inc(&oprofile_stats.multiplex_counter);
- start_switch_worker();
- }
- /* User inputs in ms, converts to jiffies */
- int oprofile_set_timeout(unsigned long val_msec)
- {
- int err = 0;
- unsigned long time_slice;
- mutex_lock(&start_mutex);
- if (oprofile_started) {
- err = -EBUSY;
- goto out;
- }
- if (!oprofile_ops.switch_events) {
- err = -EINVAL;
- goto out;
- }
- time_slice = msecs_to_jiffies(val_msec);
- if (time_slice == MAX_JIFFY_OFFSET) {
- err = -EINVAL;
- goto out;
- }
- oprofile_time_slice = time_slice;
- out:
- mutex_unlock(&start_mutex);
- return err;
- }
- #else
- static inline void start_switch_worker(void) { }
- static inline void stop_switch_worker(void) { }
- #endif
- /* Actually start profiling (echo 1>/dev/oprofile/enable) */
- int oprofile_start(void)
- {
- int err = -EINVAL;
- mutex_lock(&start_mutex);
- if (!is_setup)
- goto out;
- err = 0;
- if (oprofile_started)
- goto out;
- oprofile_reset_stats();
- if ((err = oprofile_ops.start()))
- goto out;
- start_switch_worker();
- oprofile_started = 1;
- out:
- mutex_unlock(&start_mutex);
- return err;
- }
- /* echo 0>/dev/oprofile/enable */
- void oprofile_stop(void)
- {
- mutex_lock(&start_mutex);
- if (!oprofile_started)
- goto out;
- oprofile_ops.stop();
- oprofile_started = 0;
- stop_switch_worker();
- /* wake up the daemon to read what remains */
- wake_up_buffer_waiter();
- out:
- mutex_unlock(&start_mutex);
- }
- void oprofile_shutdown(void)
- {
- mutex_lock(&start_mutex);
- if (oprofile_ops.sync_stop) {
- int sync_ret = oprofile_ops.sync_stop();
- switch (sync_ret) {
- case 0:
- goto post_sync;
- case 1:
- goto do_generic;
- default:
- goto post_sync;
- }
- }
- do_generic:
- sync_stop();
- post_sync:
- if (oprofile_ops.shutdown)
- oprofile_ops.shutdown();
- is_setup = 0;
- free_event_buffer();
- free_cpu_buffers();
- mutex_unlock(&start_mutex);
- }
- int oprofile_set_ulong(unsigned long *addr, unsigned long val)
- {
- int err = -EBUSY;
- mutex_lock(&start_mutex);
- if (!oprofile_started) {
- *addr = val;
- err = 0;
- }
- mutex_unlock(&start_mutex);
- return err;
- }
- static int timer_mode;
- static int __init oprofile_init(void)
- {
- int err;
- /* always init architecture to setup backtrace support */
- err = oprofile_arch_init(&oprofile_ops);
- timer_mode = err || timer; /* fall back to timer mode on errors */
- if (timer_mode) {
- if (!err)
- oprofile_arch_exit();
- err = oprofile_timer_init(&oprofile_ops);
- if (err)
- return err;
- }
- err = oprofilefs_register();
- if (!err)
- return 0;
- /* failed */
- if (timer_mode)
- oprofile_timer_exit();
- else
- oprofile_arch_exit();
- return err;
- }
- static void __exit oprofile_exit(void)
- {
- oprofilefs_unregister();
- if (timer_mode)
- oprofile_timer_exit();
- else
- oprofile_arch_exit();
- }
- module_init(oprofile_init);
- module_exit(oprofile_exit);
- module_param_named(timer, timer, int, 0644);
- MODULE_PARM_DESC(timer, "force use of timer interrupt");
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("John Levon <levon@movementarian.org>");
- MODULE_DESCRIPTION("OProfile system profiler");
|