- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / arch / x86 / vdso / vclock_gettime.c
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, gettimeofday, and time.
6  *
7  * The code should have no internal unresolved relocations.
8  * Check with readelf after changing.
9  */
10
11 /* Disable profiling for userspace code: */
12 #define DISABLE_BRANCH_PROFILING
13
14 #include <linux/kernel.h>
15 #include <linux/posix-timers.h>
16 #include <linux/time.h>
17 #include <linux/string.h>
18 #include <asm/vsyscall.h>
19 #include <asm/fixmap.h>
20 #include <asm/vgtod.h>
21 #include <asm/timex.h>
22 #include <asm/hpet.h>
23 #include <asm/unistd.h>
24 #include <asm/io.h>
25
26 #define gtod (&VVAR(vsyscall_gtod_data))
27
28 #ifndef CONFIG_XEN
29 #define using_vclock likely(gtod->clock.vclock_mode != VCLOCK_NONE)
30 #else
31 #define using_vclock 0
32 #define VCLOCK_TSC (-1)
33 #endif
34
35 notrace static cycle_t vread_tsc(void)
36 {
37         cycle_t ret;
38         u64 last;
39
40         /*
41          * Empirically, a fence (of type that depends on the CPU)
42          * before rdtsc is enough to ensure that rdtsc is ordered
43          * with respect to loads.  The various CPU manuals are unclear
44          * as to whether rdtsc can be reordered with later loads,
45          * but no one has ever seen it happen.
46          */
47         rdtsc_barrier();
48         ret = (cycle_t)vget_cycles();
49
50         last = VVAR(vsyscall_gtod_data).clock.cycle_last;
51
52         if (likely(ret >= last))
53                 return ret;
54
55         /*
56          * GCC likes to generate cmov here, but this branch is extremely
57          * predictable (it's just a funciton of time and the likely is
58          * very likely) and there's a data dependence, so force GCC
59          * to generate a branch instead.  I don't barrier() because
60          * we don't actually need a barrier, and if this function
61          * ever gets inlined it will generate worse code.
62          */
63         asm volatile ("");
64         return last;
65 }
66
67 static notrace cycle_t vread_hpet(void)
68 {
69         return readl((const void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0);
70 }
71
72 notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
73 {
74         long ret;
75         asm("syscall" : "=a" (ret) :
76             "0" (__NR_clock_gettime),"D" (clock), "S" (ts) : "memory");
77         return ret;
78 }
79
80 notrace static inline long vgetns(void)
81 {
82         long v;
83         cycles_t cycles;
84         if (gtod->clock.vclock_mode == VCLOCK_TSC)
85                 cycles = vread_tsc();
86         else
87                 cycles = vread_hpet();
88         v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask;
89         return (v * gtod->clock.mult) >> gtod->clock.shift;
90 }
91
92 notrace static noinline int do_realtime(struct timespec *ts)
93 {
94         unsigned long seq, ns;
95         do {
96                 seq = read_seqbegin(&gtod->lock);
97                 ts->tv_sec = gtod->wall_time_sec;
98                 ts->tv_nsec = gtod->wall_time_nsec;
99                 ns = vgetns();
100         } while (unlikely(read_seqretry(&gtod->lock, seq)));
101         timespec_add_ns(ts, ns);
102         return 0;
103 }
104
105 notrace static noinline int do_monotonic(struct timespec *ts)
106 {
107         unsigned long seq, ns, secs;
108         do {
109                 seq = read_seqbegin(&gtod->lock);
110                 secs = gtod->wall_time_sec;
111                 ns = gtod->wall_time_nsec + vgetns();
112                 secs += gtod->wall_to_monotonic.tv_sec;
113                 ns += gtod->wall_to_monotonic.tv_nsec;
114         } while (unlikely(read_seqretry(&gtod->lock, seq)));
115
116         /* wall_time_nsec, vgetns(), and wall_to_monotonic.tv_nsec
117          * are all guaranteed to be nonnegative.
118          */
119         while (ns >= NSEC_PER_SEC) {
120                 ns -= NSEC_PER_SEC;
121                 ++secs;
122         }
123         ts->tv_sec = secs;
124         ts->tv_nsec = ns;
125
126         return 0;
127 }
128
129 notrace static noinline int do_realtime_coarse(struct timespec *ts)
130 {
131         unsigned long seq;
132         do {
133                 seq = read_seqbegin(&gtod->lock);
134                 ts->tv_sec = gtod->wall_time_coarse.tv_sec;
135                 ts->tv_nsec = gtod->wall_time_coarse.tv_nsec;
136         } while (unlikely(read_seqretry(&gtod->lock, seq)));
137         return 0;
138 }
139
140 notrace static noinline int do_monotonic_coarse(struct timespec *ts)
141 {
142         unsigned long seq, ns, secs;
143         do {
144                 seq = read_seqbegin(&gtod->lock);
145                 secs = gtod->wall_time_coarse.tv_sec;
146                 ns = gtod->wall_time_coarse.tv_nsec;
147                 secs += gtod->wall_to_monotonic.tv_sec;
148                 ns += gtod->wall_to_monotonic.tv_nsec;
149         } while (unlikely(read_seqretry(&gtod->lock, seq)));
150
151         /* wall_time_nsec and wall_to_monotonic.tv_nsec are
152          * guaranteed to be between 0 and NSEC_PER_SEC.
153          */
154         if (ns >= NSEC_PER_SEC) {
155                 ns -= NSEC_PER_SEC;
156                 ++secs;
157         }
158         ts->tv_sec = secs;
159         ts->tv_nsec = ns;
160
161         return 0;
162 }
163
164 notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
165 {
166         switch (clock) {
167         case CLOCK_REALTIME:
168                 if (using_vclock)
169                         return do_realtime(ts);
170                 break;
171         case CLOCK_MONOTONIC:
172                 if (using_vclock)
173                         return do_monotonic(ts);
174                 break;
175         case CLOCK_REALTIME_COARSE:
176                 return do_realtime_coarse(ts);
177         case CLOCK_MONOTONIC_COARSE:
178                 return do_monotonic_coarse(ts);
179         }
180
181         return vdso_fallback_gettime(clock, ts);
182 }
183 int clock_gettime(clockid_t, struct timespec *)
184         __attribute__((weak, alias("__vdso_clock_gettime")));
185
186 notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
187 {
188         long ret;
189         if (using_vclock) {
190                 if (likely(tv != NULL)) {
191                         BUILD_BUG_ON(offsetof(struct timeval, tv_usec) !=
192                                      offsetof(struct timespec, tv_nsec) ||
193                                      sizeof(*tv) != sizeof(struct timespec));
194                         do_realtime((struct timespec *)tv);
195                         tv->tv_usec /= 1000;
196                 }
197                 if (unlikely(tz != NULL)) {
198                         /* Avoid memcpy. Some old compilers fail to inline it */
199                         tz->tz_minuteswest = gtod->sys_tz.tz_minuteswest;
200                         tz->tz_dsttime = gtod->sys_tz.tz_dsttime;
201                 }
202                 return 0;
203         }
204         asm("syscall" : "=a" (ret) :
205             "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory");
206         return ret;
207 }
208 int gettimeofday(struct timeval *, struct timezone *)
209         __attribute__((weak, alias("__vdso_gettimeofday")));
210
211 /*
212  * This will break when the xtime seconds get inaccurate, but that is
213  * unlikely
214  */
215 notrace time_t __vdso_time(time_t *t)
216 {
217         /* This is atomic on x86_64 so we don't need any locks. */
218         time_t result = ACCESS_ONCE(VVAR(vsyscall_gtod_data).wall_time_sec);
219
220         if (t)
221                 *t = result;
222         return result;
223 }
224 int time(time_t *t)
225         __attribute__((weak, alias("__vdso_time")));