vclock_gettime.c 8.2 KB
Newer Older
1 2 3 4
/*
 * Copyright 2006 Andi Kleen, SUSE Labs.
 * Subject to the GNU Public License, v.2
 *
A
Andy Lutomirski 已提交
5
 * Fast user context implementation of clock_gettime, gettimeofday, and time.
6
 *
7 8 9
 * 32 Bit compat layer by Stefani Seibold <stefani@seibold.net>
 *  sponsored by Rohde & Schwarz GmbH & Co. KG Munich/Germany
 *
10 11 12 13
 * The code should have no internal unresolved relocations.
 * Check with readelf after changing.
 */

14
#include <uapi/linux/time.h>
15 16
#include <asm/vgtod.h>
#include <asm/hpet.h>
17
#include <asm/vvar.h>
18
#include <asm/unistd.h>
19 20 21
#include <asm/msr.h>
#include <linux/math64.h>
#include <linux/time.h>
22

23
#define gtod (&VVAR(vsyscall_gtod_data))
24

25 26 27 28
extern int __vdso_clock_gettime(clockid_t clock, struct timespec *ts);
extern int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz);
extern time_t __vdso_time(time_t *t);

29
#ifdef CONFIG_HPET_TIMER
30 31 32 33
extern u8 hpet_page
	__attribute__((visibility("hidden")));

static notrace cycle_t vread_hpet(void)
34
{
35
	return *(const volatile u32 *)(&hpet_page + HPET_COUNTER);
36 37 38
}
#endif

39 40
#ifndef BUILD_VDSO32

41 42 43 44 45
#include <linux/kernel.h>
#include <asm/vsyscall.h>
#include <asm/fixmap.h>
#include <asm/pvclock.h>

46 47 48 49 50 51
notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
{
	long ret;
	asm("syscall" : "=a" (ret) :
	    "0" (__NR_clock_gettime), "D" (clock), "S" (ts) : "memory");
	return ret;
52 53
}

54
notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz)
55
{
56 57 58 59 60
	long ret;

	asm("syscall" : "=a" (ret) :
	    "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory");
	return ret;
61 62
}

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
#ifdef CONFIG_PARAVIRT_CLOCK

static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu)
{
	const struct pvclock_vsyscall_time_info *pvti_base;
	int idx = cpu / (PAGE_SIZE/PVTI_SIZE);
	int offset = cpu % (PAGE_SIZE/PVTI_SIZE);

	BUG_ON(PVCLOCK_FIXMAP_BEGIN + idx > PVCLOCK_FIXMAP_END);

	pvti_base = (struct pvclock_vsyscall_time_info *)
		    __fix_to_virt(PVCLOCK_FIXMAP_BEGIN+idx);

	return &pvti_base[offset];
}

static notrace cycle_t vread_pvclock(int *mode)
{
	const struct pvclock_vsyscall_time_info *pvti;
	cycle_t ret;
	u64 last;
	u32 version;
85
	u32 migrate_count;
86 87 88 89 90
	u8 flags;
	unsigned cpu, cpu1;


	/*
91 92 93
	 * When looping to get a consistent (time-info, tsc) pair, we
	 * also need to deal with the possibility we can switch vcpus,
	 * so make sure we always re-fetch time-info for the current vcpu.
94 95 96 97 98 99 100 101
	 */
	do {
		cpu = __getcpu() & VGETCPU_CPU_MASK;
		/* TODO: We can put vcpu id into higher bits of pvti.version.
		 * This will save a couple of cycles by getting rid of
		 * __getcpu() calls (Gleb).
		 */

102 103 104 105
		/* Make sure migrate_count will change if we leave the VCPU. */
		do {
			pvti = get_pvti(cpu);
			migrate_count = pvti->migrate_count;
106

107 108 109
			cpu1 = cpu;
			cpu = __getcpu() & VGETCPU_CPU_MASK;
		} while (unlikely(cpu != cpu1));
110

111 112 113 114
		version = __pvclock_read_cycles(&pvti->pvti, &ret, &flags);

		/*
		 * Test we're still on the cpu as well as the version.
115 116 117
		 * - We must read TSC of pvti's VCPU.
		 * - KVM doesn't follow the versioning protocol, so data could
		 *   change before version if we left the VCPU.
118
		 */
119 120
		smp_rmb();
	} while (unlikely((pvti->pvti.version & 1) ||
121 122
			  pvti->pvti.version != version ||
			  pvti->migrate_count != migrate_count));
123 124 125 126 127

	if (unlikely(!(flags & PVCLOCK_TSC_STABLE_BIT)))
		*mode = VCLOCK_NONE;

	/* refer to tsc.c read_tsc() comment for rationale */
128
	last = gtod->cycle_last;
129 130 131 132 133 134 135 136

	if (likely(ret >= last))
		return ret;

	return last;
}
#endif

137 138 139 140 141 142 143 144 145
#else

notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
{
	long ret;

	asm(
		"mov %%ebx, %%edx \n"
		"mov %2, %%ebx \n"
146
		"call __kernel_vsyscall \n"
147 148 149 150 151 152 153 154 155 156 157 158 159 160
		"mov %%edx, %%ebx \n"
		: "=a" (ret)
		: "0" (__NR_clock_gettime), "g" (clock), "c" (ts)
		: "memory", "edx");
	return ret;
}

notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz)
{
	long ret;

	asm(
		"mov %%ebx, %%edx \n"
		"mov %2, %%ebx \n"
161
		"call __kernel_vsyscall \n"
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
		"mov %%edx, %%ebx \n"
		: "=a" (ret)
		: "0" (__NR_gettimeofday), "g" (tv), "c" (tz)
		: "memory", "edx");
	return ret;
}

#ifdef CONFIG_PARAVIRT_CLOCK

static notrace cycle_t vread_pvclock(int *mode)
{
	*mode = VCLOCK_NONE;
	return 0;
}
#endif

#endif

180
notrace static cycle_t vread_tsc(void)
181
{
182 183
	cycle_t ret;
	u64 last;
184

185 186 187 188 189 190 191 192
	/*
	 * Empirically, a fence (of type that depends on the CPU)
	 * before rdtsc is enough to ensure that rdtsc is ordered
	 * with respect to loads.  The various CPU manuals are unclear
	 * as to whether rdtsc can be reordered with later loads,
	 * but no one has ever seen it happen.
	 */
	rdtsc_barrier();
193
	ret = (cycle_t)__native_read_tsc();
194

195
	last = gtod->cycle_last;
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210
	if (likely(ret >= last))
		return ret;

	/*
	 * GCC likes to generate cmov here, but this branch is extremely
	 * predictable (it's just a funciton of time and the likely is
	 * very likely) and there's a data dependence, so force GCC
	 * to generate a branch instead.  I don't barrier() because
	 * we don't actually need a barrier, and if this function
	 * ever gets inlined it will generate worse code.
	 */
	asm volatile ("");
	return last;
}
211

212
notrace static inline u64 vgetsns(int *mode)
213
{
214
	u64 v;
215
	cycles_t cycles;
216 217

	if (gtod->vclock_mode == VCLOCK_TSC)
218
		cycles = vread_tsc();
219
#ifdef CONFIG_HPET_TIMER
220
	else if (gtod->vclock_mode == VCLOCK_HPET)
221
		cycles = vread_hpet();
222
#endif
223
#ifdef CONFIG_PARAVIRT_CLOCK
224
	else if (gtod->vclock_mode == VCLOCK_PVCLOCK)
225 226
		cycles = vread_pvclock(mode);
#endif
227 228
	else
		return 0;
229 230
	v = (cycles - gtod->cycle_last) & gtod->mask;
	return v * gtod->mult;
231 232
}

233 234
/* Code size doesn't matter (vdso is 4k anyway) and this is faster. */
notrace static int __always_inline do_realtime(struct timespec *ts)
235
{
236 237
	unsigned long seq;
	u64 ns;
238 239
	int mode;

240
	do {
241 242
		seq = gtod_read_begin(gtod);
		mode = gtod->vclock_mode;
243
		ts->tv_sec = gtod->wall_time_sec;
244
		ns = gtod->wall_time_snsec;
245
		ns += vgetsns(&mode);
246 247 248 249 250
		ns >>= gtod->shift;
	} while (unlikely(gtod_read_retry(gtod, seq)));

	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
	ts->tv_nsec = ns;
251 252

	return mode;
253 254
}

255
notrace static int __always_inline do_monotonic(struct timespec *ts)
256
{
257 258
	unsigned long seq;
	u64 ns;
259 260
	int mode;

261
	do {
262 263
		seq = gtod_read_begin(gtod);
		mode = gtod->vclock_mode;
264
		ts->tv_sec = gtod->monotonic_time_sec;
265
		ns = gtod->monotonic_time_snsec;
266
		ns += vgetsns(&mode);
267 268 269 270 271
		ns >>= gtod->shift;
	} while (unlikely(gtod_read_retry(gtod, seq)));

	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
	ts->tv_nsec = ns;
272

273
	return mode;
274 275
}

276
notrace static void do_realtime_coarse(struct timespec *ts)
277 278 279
{
	unsigned long seq;
	do {
280 281 282 283
		seq = gtod_read_begin(gtod);
		ts->tv_sec = gtod->wall_time_coarse_sec;
		ts->tv_nsec = gtod->wall_time_coarse_nsec;
	} while (unlikely(gtod_read_retry(gtod, seq)));
284 285
}

286
notrace static void do_monotonic_coarse(struct timespec *ts)
287
{
288
	unsigned long seq;
289
	do {
290 291 292 293
		seq = gtod_read_begin(gtod);
		ts->tv_sec = gtod->monotonic_time_coarse_sec;
		ts->tv_nsec = gtod->monotonic_time_coarse_nsec;
	} while (unlikely(gtod_read_retry(gtod, seq)));
294 295
}

296
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
297
{
298 299
	switch (clock) {
	case CLOCK_REALTIME:
300 301
		if (do_realtime(ts) == VCLOCK_NONE)
			goto fallback;
302 303
		break;
	case CLOCK_MONOTONIC:
304 305
		if (do_monotonic(ts) == VCLOCK_NONE)
			goto fallback;
306 307
		break;
	case CLOCK_REALTIME_COARSE:
308 309
		do_realtime_coarse(ts);
		break;
310
	case CLOCK_MONOTONIC_COARSE:
311 312 313 314
		do_monotonic_coarse(ts);
		break;
	default:
		goto fallback;
315 316
	}

317
	return 0;
318 319
fallback:
	return vdso_fallback_gettime(clock, ts);
320 321 322 323
}
int clock_gettime(clockid_t, struct timespec *)
	__attribute__((weak, alias("__vdso_clock_gettime")));

324
notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
325
{
326
	if (likely(tv != NULL)) {
327 328
		if (unlikely(do_realtime((struct timespec *)tv) == VCLOCK_NONE))
			return vdso_fallback_gtod(tv, tz);
329
		tv->tv_usec /= 1000;
330
	}
331
	if (unlikely(tz != NULL)) {
332 333
		tz->tz_minuteswest = gtod->tz_minuteswest;
		tz->tz_dsttime = gtod->tz_dsttime;
334 335 336
	}

	return 0;
337 338 339
}
int gettimeofday(struct timeval *, struct timezone *)
	__attribute__((weak, alias("__vdso_gettimeofday")));
A
Andy Lutomirski 已提交
340

341 342 343 344
/*
 * This will break when the xtime seconds get inaccurate, but that is
 * unlikely
 */
A
Andy Lutomirski 已提交
345 346
notrace time_t __vdso_time(time_t *t)
{
347
	/* This is atomic on x86 so we don't need any locks. */
348
	time_t result = ACCESS_ONCE(gtod->wall_time_sec);
A
Andy Lutomirski 已提交
349 350 351 352 353 354 355

	if (t)
		*t = result;
	return result;
}
int time(time_t *t)
	__attribute__((weak, alias("__vdso_time")));