aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/vdso/vclock_gettime.c
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2007-07-21 11:10:01 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-21 21:37:08 -0400
commit2aae950b21e4bc789d1fc6668faf67e8748300b7 (patch)
tree5777768cc2493695ec9f4000c14f3584b3db28fd /arch/x86_64/vdso/vclock_gettime.c
parenta586df067afe0580bb02b7a6312ca2afe49bba03 (diff)
x86_64: Add vDSO for x86-64 with gettimeofday/clock_gettime/getcpu
This implements new vDSO for x86-64. The concept is similar to the existing vDSOs on i386 and PPC. x86-64 has had static vsyscalls before, but these are not flexible enough anymore. A vDSO is a ELF shared library supplied by the kernel that is mapped into user address space. The vDSO mapping is randomized for each process for security reasons. Doing this was needed for clock_gettime, because clock_gettime always needs a syscall fallback and having one at a fixed address would have made buffer overflow exploits too easy to write. The vdso can be disabled with vdso=0 It currently includes a new gettimeofday implemention and optimized clock_gettime(). The gettimeofday implementation is slightly faster than the one in the old vsyscall. clock_gettime is significantly faster than the syscall for CLOCK_MONOTONIC and CLOCK_REALTIME. The new calls are generally faster than the old vsyscall. Advantages over the old x86-64 vsyscalls: - Extensible - Randomized - Cleaner - Easier to virtualize (the old static address range previously causes overhead e.g. for Xen because it has to create special page tables for it) Weak points: - glibc support still to be written The VM interface is partly based on Ingo Molnar's i386 version. Includes compile fix from Joachim Deguara Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/x86_64/vdso/vclock_gettime.c')
-rw-r--r--arch/x86_64/vdso/vclock_gettime.c120
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
27static 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
35static 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
43static noinline int do_realtime(struct timespec *ts)
44{
45 unsigned long seq, ns;
46 do {
47 seq = read_seqbegin(&gtod->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(&gtod->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 */
57static 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
71static noinline int do_monotonic(struct timespec *ts)
72{
73 unsigned long seq, ns, secs;
74 do {
75 seq = read_seqbegin(&gtod->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(&gtod->lock, seq)));
81 vset_normalized_timespec(ts, secs, ns);
82 return 0;
83}
84
85int __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}
96int clock_gettime(clockid_t, struct timespec *)
97 __attribute__((weak, alias("__vdso_clock_gettime")));
98
99int __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, &gtod->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}
119int gettimeofday(struct timeval *, struct timezone *)
120 __attribute__((weak, alias("__vdso_gettimeofday")));