]>
Commit | Line | Data |
---|---|---|
2aae950b AK |
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> | |
2aae950b AK |
22 | #include "vextern.h" |
23 | ||
24 | #define gtod vdso_vsyscall_gtod_data | |
25 | ||
26 | static long vdso_fallback_gettime(long clock, struct timespec *ts) | |
27 | { | |
28 | long ret; | |
29 | asm("syscall" : "=a" (ret) : | |
30 | "0" (__NR_clock_gettime),"D" (clock), "S" (ts) : "memory"); | |
31 | return ret; | |
32 | } | |
33 | ||
34 | static inline long vgetns(void) | |
35 | { | |
95b08679 | 36 | long v; |
2aae950b AK |
37 | cycles_t (*vread)(void); |
38 | vread = gtod->clock.vread; | |
95b08679 AK |
39 | v = (vread() - gtod->clock.cycle_last) & gtod->clock.mask; |
40 | return (v * gtod->clock.mult) >> gtod->clock.shift; | |
2aae950b AK |
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)) { | |
a1289643 AK |
109 | /* Avoid memcpy. Some old compilers fail to inline it */ |
110 | tz->tz_minuteswest = gtod->sys_tz.tz_minuteswest; | |
111 | tz->tz_dsttime = gtod->sys_tz.tz_dsttime; | |
2aae950b AK |
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"))); |