diff options
Diffstat (limited to 'arch/x86/vdso/vclock_gettime.c')
-rw-r--r-- | arch/x86/vdso/vclock_gettime.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/arch/x86/vdso/vclock_gettime.c b/arch/x86/vdso/vclock_gettime.c new file mode 100644 index 000000000000..5b54cdfb2b07 --- /dev/null +++ b/arch/x86/vdso/vclock_gettime.c | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * Copyright 2006 Andi Kleen, SUSE Labs. | ||
3 | * Subject to the GNU Public License, v.2 | ||
4 | * | ||
5 | * Fast user context implementation of clock_gettime and gettimeofday. | ||
6 | * | ||
7 | * The code should have no internal unresolved relocations. | ||
8 | * Check with readelf after changing. | ||
9 | * Also alternative() doesn't work. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/posix-timers.h> | ||
14 | #include <linux/time.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <asm/vsyscall.h> | ||
17 | #include <asm/vgtod.h> | ||
18 | #include <asm/timex.h> | ||
19 | #include <asm/hpet.h> | ||
20 | #include <asm/unistd.h> | ||
21 | #include <asm/io.h> | ||
22 | #include <asm/vgtod.h> | ||
23 | #include "vextern.h" | ||
24 | |||
25 | #define gtod vdso_vsyscall_gtod_data | ||
26 | |||
27 | static long vdso_fallback_gettime(long clock, struct timespec *ts) | ||
28 | { | ||
29 | long ret; | ||
30 | asm("syscall" : "=a" (ret) : | ||
31 | "0" (__NR_clock_gettime),"D" (clock), "S" (ts) : "memory"); | ||
32 | return ret; | ||
33 | } | ||
34 | |||
35 | static inline long vgetns(void) | ||
36 | { | ||
37 | long v; | ||
38 | cycles_t (*vread)(void); | ||
39 | vread = gtod->clock.vread; | ||
40 | v = (vread() - gtod->clock.cycle_last) & gtod->clock.mask; | ||
41 | return (v * gtod->clock.mult) >> gtod->clock.shift; | ||
42 | } | ||
43 | |||
44 | static noinline int do_realtime(struct timespec *ts) | ||
45 | { | ||
46 | unsigned long seq, ns; | ||
47 | do { | ||
48 | seq = read_seqbegin(>od->lock); | ||
49 | ts->tv_sec = gtod->wall_time_sec; | ||
50 | ts->tv_nsec = gtod->wall_time_nsec; | ||
51 | ns = vgetns(); | ||
52 | } while (unlikely(read_seqretry(>od->lock, seq))); | ||
53 | timespec_add_ns(ts, ns); | ||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | /* Copy of the version in kernel/time.c which we cannot directly access */ | ||
58 | static void vset_normalized_timespec(struct timespec *ts, long sec, long nsec) | ||
59 | { | ||
60 | while (nsec >= NSEC_PER_SEC) { | ||
61 | nsec -= NSEC_PER_SEC; | ||
62 | ++sec; | ||
63 | } | ||
64 | while (nsec < 0) { | ||
65 | nsec += NSEC_PER_SEC; | ||
66 | --sec; | ||
67 | } | ||
68 | ts->tv_sec = sec; | ||
69 | ts->tv_nsec = nsec; | ||
70 | } | ||
71 | |||
72 | static noinline int do_monotonic(struct timespec *ts) | ||
73 | { | ||
74 | unsigned long seq, ns, secs; | ||
75 | do { | ||
76 | seq = read_seqbegin(>od->lock); | ||
77 | secs = gtod->wall_time_sec; | ||
78 | ns = gtod->wall_time_nsec + vgetns(); | ||
79 | secs += gtod->wall_to_monotonic.tv_sec; | ||
80 | ns += gtod->wall_to_monotonic.tv_nsec; | ||
81 | } while (unlikely(read_seqretry(>od->lock, seq))); | ||
82 | vset_normalized_timespec(ts, secs, ns); | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) | ||
87 | { | ||
88 | if (likely(gtod->sysctl_enabled && gtod->clock.vread)) | ||
89 | switch (clock) { | ||
90 | case CLOCK_REALTIME: | ||
91 | return do_realtime(ts); | ||
92 | case CLOCK_MONOTONIC: | ||
93 | return do_monotonic(ts); | ||
94 | } | ||
95 | return vdso_fallback_gettime(clock, ts); | ||
96 | } | ||
97 | int clock_gettime(clockid_t, struct timespec *) | ||
98 | __attribute__((weak, alias("__vdso_clock_gettime"))); | ||
99 | |||
100 | int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) | ||
101 | { | ||
102 | long ret; | ||
103 | if (likely(gtod->sysctl_enabled && gtod->clock.vread)) { | ||
104 | BUILD_BUG_ON(offsetof(struct timeval, tv_usec) != | ||
105 | offsetof(struct timespec, tv_nsec) || | ||
106 | sizeof(*tv) != sizeof(struct timespec)); | ||
107 | do_realtime((struct timespec *)tv); | ||
108 | tv->tv_usec /= 1000; | ||
109 | if (unlikely(tz != NULL)) { | ||
110 | /* This relies on gcc inlining the memcpy. We'll notice | ||
111 | if it ever fails to do so. */ | ||
112 | memcpy(tz, >od->sys_tz, sizeof(struct timezone)); | ||
113 | } | ||
114 | return 0; | ||
115 | } | ||
116 | asm("syscall" : "=a" (ret) : | ||
117 | "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory"); | ||
118 | return ret; | ||
119 | } | ||
120 | int gettimeofday(struct timeval *, struct timezone *) | ||
121 | __attribute__((weak, alias("__vdso_gettimeofday"))); | ||