diff options
Diffstat (limited to 'arch/x86_64/vdso/vclock_gettime.c')
-rw-r--r-- | arch/x86_64/vdso/vclock_gettime.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/arch/x86_64/vdso/vclock_gettime.c b/arch/x86_64/vdso/vclock_gettime.c new file mode 100644 index 000000000000..17f6a00de712 --- /dev/null +++ b/arch/x86_64/vdso/vclock_gettime.c | |||
@@ -0,0 +1,120 @@ | |||
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 | cycles_t (*vread)(void); | ||
38 | vread = gtod->clock.vread; | ||
39 | return ((vread() - gtod->clock.cycle_last) * gtod->clock.mult) >> | ||
40 | gtod->clock.shift; | ||
41 | } | ||
42 | |||
43 | static noinline int do_realtime(struct timespec *ts) | ||
44 | { | ||
45 | unsigned long seq, ns; | ||
46 | do { | ||
47 | seq = read_seqbegin(>od->lock); | ||
48 | ts->tv_sec = gtod->wall_time_sec; | ||
49 | ts->tv_nsec = gtod->wall_time_nsec; | ||
50 | ns = vgetns(); | ||
51 | } while (unlikely(read_seqretry(>od->lock, seq))); | ||
52 | timespec_add_ns(ts, ns); | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | /* Copy of the version in kernel/time.c which we cannot directly access */ | ||
57 | static void vset_normalized_timespec(struct timespec *ts, long sec, long nsec) | ||
58 | { | ||
59 | while (nsec >= NSEC_PER_SEC) { | ||
60 | nsec -= NSEC_PER_SEC; | ||
61 | ++sec; | ||
62 | } | ||
63 | while (nsec < 0) { | ||
64 | nsec += NSEC_PER_SEC; | ||
65 | --sec; | ||
66 | } | ||
67 | ts->tv_sec = sec; | ||
68 | ts->tv_nsec = nsec; | ||
69 | } | ||
70 | |||
71 | static noinline int do_monotonic(struct timespec *ts) | ||
72 | { | ||
73 | unsigned long seq, ns, secs; | ||
74 | do { | ||
75 | seq = read_seqbegin(>od->lock); | ||
76 | secs = gtod->wall_time_sec; | ||
77 | ns = gtod->wall_time_nsec + vgetns(); | ||
78 | secs += gtod->wall_to_monotonic.tv_sec; | ||
79 | ns += gtod->wall_to_monotonic.tv_nsec; | ||
80 | } while (unlikely(read_seqretry(>od->lock, seq))); | ||
81 | vset_normalized_timespec(ts, secs, ns); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) | ||
86 | { | ||
87 | if (likely(gtod->sysctl_enabled && gtod->clock.vread)) | ||
88 | switch (clock) { | ||
89 | case CLOCK_REALTIME: | ||
90 | return do_realtime(ts); | ||
91 | case CLOCK_MONOTONIC: | ||
92 | return do_monotonic(ts); | ||
93 | } | ||
94 | return vdso_fallback_gettime(clock, ts); | ||
95 | } | ||
96 | int clock_gettime(clockid_t, struct timespec *) | ||
97 | __attribute__((weak, alias("__vdso_clock_gettime"))); | ||
98 | |||
99 | int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) | ||
100 | { | ||
101 | long ret; | ||
102 | if (likely(gtod->sysctl_enabled && gtod->clock.vread)) { | ||
103 | BUILD_BUG_ON(offsetof(struct timeval, tv_usec) != | ||
104 | offsetof(struct timespec, tv_nsec) || | ||
105 | sizeof(*tv) != sizeof(struct timespec)); | ||
106 | do_realtime((struct timespec *)tv); | ||
107 | tv->tv_usec /= 1000; | ||
108 | if (unlikely(tz != NULL)) { | ||
109 | /* This relies on gcc inlining the memcpy. We'll notice | ||
110 | if it ever fails to do so. */ | ||
111 | memcpy(tz, >od->sys_tz, sizeof(struct timezone)); | ||
112 | } | ||
113 | return 0; | ||
114 | } | ||
115 | asm("syscall" : "=a" (ret) : | ||
116 | "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory"); | ||
117 | return ret; | ||
118 | } | ||
119 | int gettimeofday(struct timeval *, struct timezone *) | ||
120 | __attribute__((weak, alias("__vdso_gettimeofday"))); | ||