aboutsummaryrefslogtreecommitdiff
path: root/kernel/trace/trace-clock-32-to-64.c
blob: d80255eb288d4e16a46732a9fbc9cfd83cb3055b (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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
 * kernel/trace/trace-clock-32-to-64.c
 *
 * (C) Copyright	2006,2007,2008 -
 * 		Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca)
 *
 * Extends a 32 bits clock source to a full 64 bits count, readable atomically
 * from any execution context.
 *
 * notes :
 * - trace clock 32->64 bits extended timer-based clock cannot be used for early
 *   tracing in the boot process, as it depends on timer interrupts.
 * - The timer is only on one CPU to support hotplug.
 * - We have the choice between schedule_delayed_work_on and an IPI to get each
 *   CPU to write the heartbeat. IPI has been chosen because it is considered
 *   faster than passing through the timer to get the work scheduled on all the
 *   CPUs.
 */

#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/smp.h>
#include <linux/sched.h> /* needed due to include order problem on m68k */
#include <linux/math64.h>

#define HW_BITMASK			((1ULL << TC_HW_BITS) - 1)
#define HW_LS32(hw)			((hw) & HW_BITMASK)
#define SW_MS32(sw)			((sw) & ~HW_BITMASK)

static DEFINE_SPINLOCK(synthetic_tsc_lock);
static int synthetic_tsc_refcount;  /* Number of readers */
static int synthetic_tsc_enabled;   /* synth. TSC enabled on all online CPUs */

static DEFINE_PER_CPU(struct timer_list, tsc_timer);
static unsigned int precalc_expire;

struct synthetic_tsc_struct {
	union {
		u64 val;
		struct {
#ifdef __BIG_ENDIAN
			u32 ms32;
			u32 ls32;
#else
			u32 ls32;
			u32 ms32;
#endif
		} sel;
	} tsc[2];
	unsigned int index;	/* Index of the current synth. tsc. */
};

static DEFINE_PER_CPU(struct synthetic_tsc_struct, synthetic_tsc);

/* Called from IPI or timer interrupt */
static void update_synthetic_tsc(void)
{
	struct synthetic_tsc_struct *cpu_synth;
	u32 tsc;

	cpu_synth = &per_cpu(synthetic_tsc, smp_processor_id());
	tsc = trace_clock_read32();		/* Hardware clocksource read */

	if (tsc < HW_LS32(cpu_synth->tsc[cpu_synth->index].sel.ls32)) {
		unsigned int new_index = 1 - cpu_synth->index; /* 0 <-> 1 */
		/*
		 * Overflow
		 * Non atomic update of the non current synthetic TSC, followed
		 * by an atomic index change. There is no write concurrency,
		 * so the index read/write does not need to be atomic.
		 */
		cpu_synth->tsc[new_index].val =
			(SW_MS32(cpu_synth->tsc[cpu_synth->index].val)
				| (u64)tsc) + (1ULL << TC_HW_BITS);
		/*
		 * Ensure the compiler does not reorder index write. It makes
		 * sure all nested interrupts will see the new value before the
		 * new index is written.
		 */
		barrier();
		cpu_synth->index = new_index;	/* atomic change of index */
	} else {
		/*
		 * No overflow : We know that the only bits changed are
		 * contained in the 32 LS32s, which can be written to atomically.
		 */
		cpu_synth->tsc[cpu_synth->index].sel.ls32 =
			SW_MS32(cpu_synth->tsc[cpu_synth->index].sel.ls32) | tsc;
	}
}

/*
 * Should only be called when interrupts are off. Affects only current CPU.
 */
void _trace_clock_write_synthetic_tsc(u64 value)
{
	struct synthetic_tsc_struct *cpu_synth;
	unsigned int new_index;

	cpu_synth = &per_cpu(synthetic_tsc, smp_processor_id());
	new_index = 1 - cpu_synth->index; /* 0 <-> 1 */
	cpu_synth->tsc[new_index].val = value;
	barrier();
	cpu_synth->index = new_index;	/* atomic change of index */
}

/* Called from buffer switch : in _any_ context (even NMI) */
u64 notrace trace_clock_read_synthetic_tsc(void)
{
	struct synthetic_tsc_struct *cpu_synth;
	u64 ret;
	unsigned int index;
	u32 tsc;

	preempt_disable_notrace();
	cpu_synth = &per_cpu(synthetic_tsc, smp_processor_id());
	index = ACCESS_ONCE(cpu_synth->index);	/* atomic read */
	tsc = trace_clock_read32();		/* Hardware clocksource read */

	/* Overflow detection */
	if (unlikely(tsc < HW_LS32(cpu_synth->tsc[index].sel.ls32)))
		ret = (SW_MS32(cpu_synth->tsc[index].val) | (u64)tsc)
			+ (1ULL << TC_HW_BITS);
	else
		ret = SW_MS32(cpu_synth->tsc[index].val) | (u64)tsc;
	preempt_enable_notrace();
	return ret;
}
EXPORT_SYMBOL_GPL(trace_clock_read_synthetic_tsc);

static void synthetic_tsc_ipi(void *info)
{
	update_synthetic_tsc();
}

/*
 * tsc_timer_fct : - Timer function synchronizing synthetic TSC.
 * @data: unused
 *
 * Guarantees at least 1 execution before low word of TSC wraps.
 */
static void tsc_timer_fct(unsigned long data)
{
	update_synthetic_tsc();

	per_cpu(tsc_timer, smp_processor_id()).expires =
		jiffies + precalc_expire;
	add_timer_on(&per_cpu(tsc_timer, smp_processor_id()),
		     smp_processor_id());
}

/*
 * precalc_stsc_interval: - Precalculates the interval between the clock
 * wraparounds.
 */
static int __init precalc_stsc_interval(void)
{
	u64 rem_freq, rem_interval;

	precalc_expire =
		__iter_div_u64_rem(HW_BITMASK, (
		  __iter_div_u64_rem(trace_clock_frequency(),
		  HZ * trace_clock_freq_scale(), &rem_freq) << 1
		 )
		 - 1
		 - (TC_EXPECTED_INTERRUPT_LATENCY * HZ / 1000), &rem_interval)
		>> 1;
	WARN_ON(precalc_expire == 0);
	printk(KERN_DEBUG "Synthetic TSC timer will fire each %u jiffies.\n",
		precalc_expire);
	return 0;
}

static void prepare_synthetic_tsc(int cpu)
{
	struct synthetic_tsc_struct *cpu_synth;
	u64 local_count;

	cpu_synth = &per_cpu(synthetic_tsc, cpu);
	local_count = trace_clock_read_synthetic_tsc();
	cpu_synth->tsc[0].val = local_count;
	cpu_synth->index = 0;
	smp_wmb();	/* Writing in data of CPU about to come up */
	init_timer_deferrable(&per_cpu(tsc_timer, cpu));
	per_cpu(tsc_timer, cpu).function = tsc_timer_fct;
	per_cpu(tsc_timer, cpu).expires = jiffies + precalc_expire;
}

static void enable_synthetic_tsc(int cpu)
{
	smp_call_function_single(cpu, synthetic_tsc_ipi, NULL, 1);
	add_timer_on(&per_cpu(tsc_timer, cpu), cpu);
}

/*
 * Cannot use del_timer_sync with add_timer_on, so use an IPI to locally
 * delete the timer.
 */
static void disable_synthetic_tsc_ipi(void *info)
{
	del_timer(&per_cpu(tsc_timer, smp_processor_id()));
}

static void disable_synthetic_tsc(int cpu)
{
	smp_call_function_single(cpu, disable_synthetic_tsc_ipi, NULL, 1);
}

/*
 * 	hotcpu_callback - CPU hotplug callback
 * 	@nb: notifier block
 * 	@action: hotplug action to take
 * 	@hcpu: CPU number
 *
 *	Sets the new CPU's current synthetic TSC to the same value as the
 *	currently running CPU.
 *
 * 	Returns the success/failure of the operation. (NOTIFY_OK, NOTIFY_BAD)
 */
static int __cpuinit hotcpu_callback(struct notifier_block *nb,
				unsigned long action,
				void *hcpu)
{
	unsigned int hotcpu = (unsigned long)hcpu;

	switch (action) {
	case CPU_UP_PREPARE:
	case CPU_UP_PREPARE_FROZEN:
		spin_lock(&synthetic_tsc_lock);
		if (synthetic_tsc_refcount)
			prepare_synthetic_tsc(hotcpu);
		spin_unlock(&synthetic_tsc_lock);
		break;
	case CPU_ONLINE:
	case CPU_ONLINE_FROZEN:
		spin_lock(&synthetic_tsc_lock);
		if (synthetic_tsc_refcount)
			enable_synthetic_tsc(hotcpu);
		spin_unlock(&synthetic_tsc_lock);
		break;
#ifdef CONFIG_HOTPLUG_CPU
	case CPU_DOWN_PREPARE:
	case CPU_DOWN_PREPARE_FROZEN:
		spin_lock(&synthetic_tsc_lock);
		if (synthetic_tsc_refcount)
			disable_synthetic_tsc(hotcpu);
		spin_unlock(&synthetic_tsc_lock);
		break;
#endif /* CONFIG_HOTPLUG_CPU */
	}
	return NOTIFY_OK;
}

void get_synthetic_tsc(void)
{
	int cpu;

	spin_lock(&synthetic_tsc_lock);
	if (synthetic_tsc_refcount++)
		goto end;

	synthetic_tsc_enabled = 1;
	for_each_online_cpu(cpu) {
		prepare_synthetic_tsc(cpu);
		enable_synthetic_tsc(cpu);
	}
end:
	spin_unlock(&synthetic_tsc_lock);
}
EXPORT_SYMBOL_GPL(get_synthetic_tsc);

void put_synthetic_tsc(void)
{
	int cpu;

	spin_lock(&synthetic_tsc_lock);
	WARN_ON(synthetic_tsc_refcount <= 0);
	if (synthetic_tsc_refcount != 1 || !synthetic_tsc_enabled)
		goto end;

	for_each_online_cpu(cpu)
		disable_synthetic_tsc(cpu);
	synthetic_tsc_enabled = 0;
end:
	synthetic_tsc_refcount--;
	spin_unlock(&synthetic_tsc_lock);
}
EXPORT_SYMBOL_GPL(put_synthetic_tsc);

/* Called from CPU 0, before any tracing starts, to init each structure */
static int __init init_synthetic_tsc(void)
{
	precalc_stsc_interval();
	hotcpu_notifier(hotcpu_callback, 3);
	return 0;
}

/* Before SMP is up */
early_initcall(init_synthetic_tsc);