aboutsummaryrefslogtreecommitdiff
path: root/kernel/trace/trace-clock.c
blob: 3ed1667aacbab04f8dbd01d75394df3542b28797 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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);