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);
|