diff options
Diffstat (limited to 'kernel/trace/trace-clock.c')
-rw-r--r-- | kernel/trace/trace-clock.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/kernel/trace/trace-clock.c b/kernel/trace/trace-clock.c new file mode 100644 index 00000000000..3ed1667aacb --- /dev/null +++ b/kernel/trace/trace-clock.c @@ -0,0 +1,97 @@ +/* + * kernel/trace/trace-clock.c + * + * (C) Copyright 2008 - + * Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca) + * + * Generic kernel tracing clock for architectures without TSC. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/workqueue.h> +#include <linux/cpu.h> +#include <linux/timex.h> +#include <linux/bitops.h> +#include <linux/trace-clock.h> +#include <linux/jiffies.h> + +static int trace_clock_refcount; +static DEFINE_MUTEX(trace_clock_mutex); +static struct timer_list trace_clock_timer; +/* + * bits 0..12 : counter, atomically incremented + * bits 13..{32,64} : time counter, incremented each jiffy. + */ +atomic_long_t trace_clock_var; +EXPORT_SYMBOL(trace_clock_var); + +static void trace_clock_update(void) +{ + long old_clock, new_clock; + unsigned long ticks; + + /* + * Make sure we keep track of delayed timer. + */ + ticks = jiffies - trace_clock_timer.expires + 1; + /* Don't update if ticks is zero, time would go backward. */ + if (unlikely(!ticks)) + return; + do { + old_clock = atomic_long_read(&trace_clock_var); + new_clock = (old_clock + (ticks << TRACE_CLOCK_SHIFT)) + & (~((1 << TRACE_CLOCK_SHIFT) - 1)); + } while (atomic_long_cmpxchg(&trace_clock_var, old_clock, new_clock) + != old_clock); +} + +static void trace_clock_timer_fct(unsigned long data) +{ + trace_clock_update(); + trace_clock_timer.expires = jiffies + 1; + add_timer(&trace_clock_timer); +} + +static void enable_trace_clock(void) +{ + init_timer(&trace_clock_timer); + /* trace_clock_update() reads expires */ + trace_clock_timer.function = trace_clock_timer_fct; + trace_clock_timer.expires = jiffies + 1; + trace_clock_update(); + add_timer(&trace_clock_timer); +} + +static void disable_trace_clock(void) +{ + del_timer_sync(&trace_clock_timer); +} + +void get_trace_clock(void) +{ + get_synthetic_tsc(); + mutex_lock(&trace_clock_mutex); + if (trace_clock_refcount++) + goto end; + enable_trace_clock(); +end: + mutex_unlock(&trace_clock_mutex); +} +EXPORT_SYMBOL_GPL(get_trace_clock); + +void put_trace_clock(void) +{ + mutex_lock(&trace_clock_mutex); + WARN_ON(trace_clock_refcount <= 0); + if (trace_clock_refcount != 1) + goto end; + disable_trace_clock(); +end: + trace_clock_refcount--; + mutex_unlock(&trace_clock_mutex); + put_synthetic_tsc(); +} +EXPORT_SYMBOL_GPL(put_trace_clock); |