aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefani Seibold <stefani@seibold.net>2014-03-17 18:22:10 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2014-03-18 15:52:41 -0400
commit7c03156f34d113f885f045d8fb8cc3efd9e64751 (patch)
tree4d63bb06f639e77cbb09c2d74f295148946ce3f0
parent7a59ed415f5b57469e22e41fc4188d5399e0b194 (diff)
x86, vdso: Add 32 bit VDSO time support for 64 bit kernel
This patch add the VDSO time support for the IA32 Emulation Layer. Due the nature of the kernel headers and the LP64 compiler where the size of a long and a pointer differs against a 32 bit compiler, there is some type hacking necessary for optimal performance. The vsyscall_gtod_data struture must be a rearranged to serve 32- and 64-bit code access at the same time: - The seqcount_t was replaced by an unsigned, this makes the vsyscall_gtod_data intedepend of kernel configuration and internal functions. - All kernel internal structures are replaced by fix size elements which works for 32- and 64-bit access - The inner struct clock was removed to pack the whole struct. The "unsigned seq" would be handled by functions derivated from seqcount_t. Signed-off-by: Stefani Seibold <stefani@seibold.net> Link: http://lkml.kernel.org/r/1395094933-14252-11-git-send-email-stefani@seibold.net Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--arch/x86/include/asm/vgtod.h71
-rw-r--r--arch/x86/include/asm/vvar.h5
-rw-r--r--arch/x86/kernel/vsyscall_gtod.c34
-rw-r--r--arch/x86/vdso/vclock_gettime.c91
-rw-r--r--arch/x86/vdso/vdso32/vclock_gettime.c21
5 files changed, 155 insertions, 67 deletions
diff --git a/arch/x86/include/asm/vgtod.h b/arch/x86/include/asm/vgtod.h
index 46e24d36b7da..3c3366c2e37f 100644
--- a/arch/x86/include/asm/vgtod.h
+++ b/arch/x86/include/asm/vgtod.h
@@ -1,30 +1,73 @@
1#ifndef _ASM_X86_VGTOD_H 1#ifndef _ASM_X86_VGTOD_H
2#define _ASM_X86_VGTOD_H 2#define _ASM_X86_VGTOD_H
3 3
4#include <asm/vsyscall.h> 4#include <linux/compiler.h>
5#include <linux/clocksource.h> 5#include <linux/clocksource.h>
6 6
7#ifdef BUILD_VDSO32_64
8typedef u64 gtod_long_t;
9#else
10typedef unsigned long gtod_long_t;
11#endif
12/*
13 * vsyscall_gtod_data will be accessed by 32 and 64 bit code at the same time
14 * so be carefull by modifying this structure.
15 */
7struct vsyscall_gtod_data { 16struct vsyscall_gtod_data {
8 seqcount_t seq; 17 unsigned seq;
9 18
10 struct { /* extract of a clocksource struct */ 19 int vclock_mode;
11 int vclock_mode; 20 cycle_t cycle_last;
12 cycle_t cycle_last; 21 cycle_t mask;
13 cycle_t mask; 22 u32 mult;
14 u32 mult; 23 u32 shift;
15 u32 shift;
16 } clock;
17 24
18 /* open coded 'struct timespec' */ 25 /* open coded 'struct timespec' */
19 time_t wall_time_sec;
20 u64 wall_time_snsec; 26 u64 wall_time_snsec;
27 gtod_long_t wall_time_sec;
28 gtod_long_t monotonic_time_sec;
21 u64 monotonic_time_snsec; 29 u64 monotonic_time_snsec;
22 time_t monotonic_time_sec; 30 gtod_long_t wall_time_coarse_sec;
31 gtod_long_t wall_time_coarse_nsec;
32 gtod_long_t monotonic_time_coarse_sec;
33 gtod_long_t monotonic_time_coarse_nsec;
23 34
24 struct timezone sys_tz; 35 int tz_minuteswest;
25 struct timespec wall_time_coarse; 36 int tz_dsttime;
26 struct timespec monotonic_time_coarse;
27}; 37};
28extern struct vsyscall_gtod_data vsyscall_gtod_data; 38extern struct vsyscall_gtod_data vsyscall_gtod_data;
29 39
40static inline unsigned gtod_read_begin(const struct vsyscall_gtod_data *s)
41{
42 unsigned ret;
43
44repeat:
45 ret = ACCESS_ONCE(s->seq);
46 if (unlikely(ret & 1)) {
47 cpu_relax();
48 goto repeat;
49 }
50 smp_rmb();
51 return ret;
52}
53
54static inline int gtod_read_retry(const struct vsyscall_gtod_data *s,
55 unsigned start)
56{
57 smp_rmb();
58 return unlikely(s->seq != start);
59}
60
61static inline void gtod_write_begin(struct vsyscall_gtod_data *s)
62{
63 ++s->seq;
64 smp_wmb();
65}
66
67static inline void gtod_write_end(struct vsyscall_gtod_data *s)
68{
69 smp_wmb();
70 ++s->seq;
71}
72
30#endif /* _ASM_X86_VGTOD_H */ 73#endif /* _ASM_X86_VGTOD_H */
diff --git a/arch/x86/include/asm/vvar.h b/arch/x86/include/asm/vvar.h
index 52c79ffc3161..081d909bc495 100644
--- a/arch/x86/include/asm/vvar.h
+++ b/arch/x86/include/asm/vvar.h
@@ -16,6 +16,9 @@
16 * you mess up, the linker will catch it.) 16 * you mess up, the linker will catch it.)
17 */ 17 */
18 18
19#ifndef _ASM_X86_VVAR_H
20#define _ASM_X86_VVAR_H
21
19#if defined(__VVAR_KERNEL_LDS) 22#if defined(__VVAR_KERNEL_LDS)
20 23
21/* The kernel linker script defines its own magic to put vvars in the 24/* The kernel linker script defines its own magic to put vvars in the
@@ -64,3 +67,5 @@ DECLARE_VVAR(16, int, vgetcpu_mode)
64DECLARE_VVAR(128, struct vsyscall_gtod_data, vsyscall_gtod_data) 67DECLARE_VVAR(128, struct vsyscall_gtod_data, vsyscall_gtod_data)
65 68
66#undef DECLARE_VVAR 69#undef DECLARE_VVAR
70
71#endif
diff --git a/arch/x86/kernel/vsyscall_gtod.c b/arch/x86/kernel/vsyscall_gtod.c
index b5a943dba9f3..f9c6e56e14b5 100644
--- a/arch/x86/kernel/vsyscall_gtod.c
+++ b/arch/x86/kernel/vsyscall_gtod.c
@@ -4,6 +4,7 @@
4 * 4 *
5 * Modified for x86 32 bit architecture by 5 * Modified for x86 32 bit architecture by
6 * Stefani Seibold <stefani@seibold.net> 6 * Stefani Seibold <stefani@seibold.net>
7 * sponsored by Rohde & Schwarz GmbH & Co. KG Munich/Germany
7 * 8 *
8 * Thanks to hpa@transmeta.com for some useful hint. 9 * Thanks to hpa@transmeta.com for some useful hint.
9 * Special thanks to Ingo Molnar for his early experience with 10 * Special thanks to Ingo Molnar for his early experience with
@@ -13,26 +14,28 @@
13 14
14#include <linux/timekeeper_internal.h> 15#include <linux/timekeeper_internal.h>
15#include <asm/vgtod.h> 16#include <asm/vgtod.h>
17#include <asm/vvar.h>
16 18
17DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data); 19DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data);
18 20
19void update_vsyscall_tz(void) 21void update_vsyscall_tz(void)
20{ 22{
21 vsyscall_gtod_data.sys_tz = sys_tz; 23 vsyscall_gtod_data.tz_minuteswest = sys_tz.tz_minuteswest;
24 vsyscall_gtod_data.tz_dsttime = sys_tz.tz_dsttime;
22} 25}
23 26
24void update_vsyscall(struct timekeeper *tk) 27void update_vsyscall(struct timekeeper *tk)
25{ 28{
26 struct vsyscall_gtod_data *vdata = &vsyscall_gtod_data; 29 struct vsyscall_gtod_data *vdata = &vsyscall_gtod_data;
27 30
28 write_seqcount_begin(&vdata->seq); 31 gtod_write_begin(vdata);
29 32
30 /* copy vsyscall data */ 33 /* copy vsyscall data */
31 vdata->clock.vclock_mode = tk->clock->archdata.vclock_mode; 34 vdata->vclock_mode = tk->clock->archdata.vclock_mode;
32 vdata->clock.cycle_last = tk->clock->cycle_last; 35 vdata->cycle_last = tk->clock->cycle_last;
33 vdata->clock.mask = tk->clock->mask; 36 vdata->mask = tk->clock->mask;
34 vdata->clock.mult = tk->mult; 37 vdata->mult = tk->mult;
35 vdata->clock.shift = tk->shift; 38 vdata->shift = tk->shift;
36 39
37 vdata->wall_time_sec = tk->xtime_sec; 40 vdata->wall_time_sec = tk->xtime_sec;
38 vdata->wall_time_snsec = tk->xtime_nsec; 41 vdata->wall_time_snsec = tk->xtime_nsec;
@@ -49,11 +52,18 @@ void update_vsyscall(struct timekeeper *tk)
49 vdata->monotonic_time_sec++; 52 vdata->monotonic_time_sec++;
50 } 53 }
51 54
52 vdata->wall_time_coarse.tv_sec = tk->xtime_sec; 55 vdata->wall_time_coarse_sec = tk->xtime_sec;
53 vdata->wall_time_coarse.tv_nsec = (long)(tk->xtime_nsec >> tk->shift); 56 vdata->wall_time_coarse_nsec = (long)(tk->xtime_nsec >> tk->shift);
54 57
55 vdata->monotonic_time_coarse = timespec_add(vdata->wall_time_coarse, 58 vdata->monotonic_time_coarse_sec =
56 tk->wall_to_monotonic); 59 vdata->wall_time_coarse_sec + tk->wall_to_monotonic.tv_sec;
60 vdata->monotonic_time_coarse_nsec =
61 vdata->wall_time_coarse_nsec + tk->wall_to_monotonic.tv_nsec;
57 62
58 write_seqcount_end(&vdata->seq); 63 while (vdata->monotonic_time_coarse_nsec >= NSEC_PER_SEC) {
64 vdata->monotonic_time_coarse_nsec -= NSEC_PER_SEC;
65 vdata->monotonic_time_coarse_sec++;
66 }
67
68 gtod_write_end(vdata);
59} 69}
diff --git a/arch/x86/vdso/vclock_gettime.c b/arch/x86/vdso/vclock_gettime.c
index 90bb5e8027ac..16d686171e9a 100644
--- a/arch/x86/vdso/vclock_gettime.c
+++ b/arch/x86/vdso/vclock_gettime.c
@@ -14,16 +14,14 @@
14/* Disable profiling for userspace code: */ 14/* Disable profiling for userspace code: */
15#define DISABLE_BRANCH_PROFILING 15#define DISABLE_BRANCH_PROFILING
16 16
17#include <linux/kernel.h>
18#include <uapi/linux/time.h> 17#include <uapi/linux/time.h>
19#include <linux/string.h>
20#include <asm/vsyscall.h>
21#include <asm/fixmap.h>
22#include <asm/vgtod.h> 18#include <asm/vgtod.h>
23#include <asm/hpet.h> 19#include <asm/hpet.h>
20#include <asm/vvar.h>
24#include <asm/unistd.h> 21#include <asm/unistd.h>
25#include <asm/io.h> 22#include <asm/msr.h>
26#include <asm/pvclock.h> 23#include <linux/math64.h>
24#include <linux/time.h>
27 25
28#define gtod (&VVAR(vsyscall_gtod_data)) 26#define gtod (&VVAR(vsyscall_gtod_data))
29 27
@@ -31,11 +29,23 @@ extern int __vdso_clock_gettime(clockid_t clock, struct timespec *ts);
31extern int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz); 29extern int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz);
32extern time_t __vdso_time(time_t *t); 30extern time_t __vdso_time(time_t *t);
33 31
32#ifdef CONFIG_HPET_TIMER
33static inline u32 read_hpet_counter(const volatile void *addr)
34{
35 return *(const volatile u32 *) (addr + HPET_COUNTER);
36}
37#endif
38
34#ifndef BUILD_VDSO32 39#ifndef BUILD_VDSO32
35 40
41#include <linux/kernel.h>
42#include <asm/vsyscall.h>
43#include <asm/fixmap.h>
44#include <asm/pvclock.h>
45
36static notrace cycle_t vread_hpet(void) 46static notrace cycle_t vread_hpet(void)
37{ 47{
38 return readl((const void __iomem *)fix_to_virt(VSYSCALL_HPET) + HPET_COUNTER); 48 return read_hpet_counter((const void *)fix_to_virt(VSYSCALL_HPET));
39} 49}
40 50
41notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) 51notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
@@ -116,7 +126,7 @@ static notrace cycle_t vread_pvclock(int *mode)
116 *mode = VCLOCK_NONE; 126 *mode = VCLOCK_NONE;
117 127
118 /* refer to tsc.c read_tsc() comment for rationale */ 128 /* refer to tsc.c read_tsc() comment for rationale */
119 last = gtod->clock.cycle_last; 129 last = gtod->cycle_last;
120 130
121 if (likely(ret >= last)) 131 if (likely(ret >= last))
122 return ret; 132 return ret;
@@ -133,7 +143,7 @@ extern u8 hpet_page
133#ifdef CONFIG_HPET_TIMER 143#ifdef CONFIG_HPET_TIMER
134static notrace cycle_t vread_hpet(void) 144static notrace cycle_t vread_hpet(void)
135{ 145{
136 return readl((const void __iomem *)(&hpet_page + HPET_COUNTER)); 146 return read_hpet_counter((const void *)(&hpet_page));
137} 147}
138#endif 148#endif
139 149
@@ -193,7 +203,7 @@ notrace static cycle_t vread_tsc(void)
193 rdtsc_barrier(); 203 rdtsc_barrier();
194 ret = (cycle_t)__native_read_tsc(); 204 ret = (cycle_t)__native_read_tsc();
195 205
196 last = gtod->clock.cycle_last; 206 last = gtod->cycle_last;
197 207
198 if (likely(ret >= last)) 208 if (likely(ret >= last))
199 return ret; 209 return ret;
@@ -214,20 +224,21 @@ notrace static inline u64 vgetsns(int *mode)
214{ 224{
215 u64 v; 225 u64 v;
216 cycles_t cycles; 226 cycles_t cycles;
217 if (gtod->clock.vclock_mode == VCLOCK_TSC) 227
228 if (gtod->vclock_mode == VCLOCK_TSC)
218 cycles = vread_tsc(); 229 cycles = vread_tsc();
219#ifdef CONFIG_HPET_TIMER 230#ifdef CONFIG_HPET_TIMER
220 else if (gtod->clock.vclock_mode == VCLOCK_HPET) 231 else if (gtod->vclock_mode == VCLOCK_HPET)
221 cycles = vread_hpet(); 232 cycles = vread_hpet();
222#endif 233#endif
223#ifdef CONFIG_PARAVIRT_CLOCK 234#ifdef CONFIG_PARAVIRT_CLOCK
224 else if (gtod->clock.vclock_mode == VCLOCK_PVCLOCK) 235 else if (gtod->vclock_mode == VCLOCK_PVCLOCK)
225 cycles = vread_pvclock(mode); 236 cycles = vread_pvclock(mode);
226#endif 237#endif
227 else 238 else
228 return 0; 239 return 0;
229 v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask; 240 v = (cycles - gtod->cycle_last) & gtod->mask;
230 return v * gtod->clock.mult; 241 return v * gtod->mult;
231} 242}
232 243
233/* Code size doesn't matter (vdso is 4k anyway) and this is faster. */ 244/* Code size doesn't matter (vdso is 4k anyway) and this is faster. */
@@ -237,17 +248,18 @@ notrace static int __always_inline do_realtime(struct timespec *ts)
237 u64 ns; 248 u64 ns;
238 int mode; 249 int mode;
239 250
240 ts->tv_nsec = 0;
241 do { 251 do {
242 seq = raw_read_seqcount_begin(&gtod->seq); 252 seq = gtod_read_begin(gtod);
243 mode = gtod->clock.vclock_mode; 253 mode = gtod->vclock_mode;
244 ts->tv_sec = gtod->wall_time_sec; 254 ts->tv_sec = gtod->wall_time_sec;
245 ns = gtod->wall_time_snsec; 255 ns = gtod->wall_time_snsec;
246 ns += vgetsns(&mode); 256 ns += vgetsns(&mode);
247 ns >>= gtod->clock.shift; 257 ns >>= gtod->shift;
248 } while (unlikely(read_seqcount_retry(&gtod->seq, seq))); 258 } while (unlikely(gtod_read_retry(gtod, seq)));
259
260 ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
261 ts->tv_nsec = ns;
249 262
250 timespec_add_ns(ts, ns);
251 return mode; 263 return mode;
252} 264}
253 265
@@ -257,16 +269,17 @@ notrace static int __always_inline do_monotonic(struct timespec *ts)
257 u64 ns; 269 u64 ns;
258 int mode; 270 int mode;
259 271
260 ts->tv_nsec = 0;
261 do { 272 do {
262 seq = raw_read_seqcount_begin(&gtod->seq); 273 seq = gtod_read_begin(gtod);
263 mode = gtod->clock.vclock_mode; 274 mode = gtod->vclock_mode;
264 ts->tv_sec = gtod->monotonic_time_sec; 275 ts->tv_sec = gtod->monotonic_time_sec;
265 ns = gtod->monotonic_time_snsec; 276 ns = gtod->monotonic_time_snsec;
266 ns += vgetsns(&mode); 277 ns += vgetsns(&mode);
267 ns >>= gtod->clock.shift; 278 ns >>= gtod->shift;
268 } while (unlikely(read_seqcount_retry(&gtod->seq, seq))); 279 } while (unlikely(gtod_read_retry(gtod, seq)));
269 timespec_add_ns(ts, ns); 280
281 ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
282 ts->tv_nsec = ns;
270 283
271 return mode; 284 return mode;
272} 285}
@@ -275,20 +288,20 @@ notrace static void do_realtime_coarse(struct timespec *ts)
275{ 288{
276 unsigned long seq; 289 unsigned long seq;
277 do { 290 do {
278 seq = raw_read_seqcount_begin(&gtod->seq); 291 seq = gtod_read_begin(gtod);
279 ts->tv_sec = gtod->wall_time_coarse.tv_sec; 292 ts->tv_sec = gtod->wall_time_coarse_sec;
280 ts->tv_nsec = gtod->wall_time_coarse.tv_nsec; 293 ts->tv_nsec = gtod->wall_time_coarse_nsec;
281 } while (unlikely(read_seqcount_retry(&gtod->seq, seq))); 294 } while (unlikely(gtod_read_retry(gtod, seq)));
282} 295}
283 296
284notrace static void do_monotonic_coarse(struct timespec *ts) 297notrace static void do_monotonic_coarse(struct timespec *ts)
285{ 298{
286 unsigned long seq; 299 unsigned long seq;
287 do { 300 do {
288 seq = raw_read_seqcount_begin(&gtod->seq); 301 seq = gtod_read_begin(gtod);
289 ts->tv_sec = gtod->monotonic_time_coarse.tv_sec; 302 ts->tv_sec = gtod->monotonic_time_coarse_sec;
290 ts->tv_nsec = gtod->monotonic_time_coarse.tv_nsec; 303 ts->tv_nsec = gtod->monotonic_time_coarse_nsec;
291 } while (unlikely(read_seqcount_retry(&gtod->seq, seq))); 304 } while (unlikely(gtod_read_retry(gtod, seq)));
292} 305}
293 306
294notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts) 307notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
@@ -322,17 +335,13 @@ int clock_gettime(clockid_t, struct timespec *)
322notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) 335notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
323{ 336{
324 if (likely(tv != NULL)) { 337 if (likely(tv != NULL)) {
325 BUILD_BUG_ON(offsetof(struct timeval, tv_usec) !=
326 offsetof(struct timespec, tv_nsec) ||
327 sizeof(*tv) != sizeof(struct timespec));
328 if (unlikely(do_realtime((struct timespec *)tv) == VCLOCK_NONE)) 338 if (unlikely(do_realtime((struct timespec *)tv) == VCLOCK_NONE))
329 return vdso_fallback_gtod(tv, tz); 339 return vdso_fallback_gtod(tv, tz);
330 tv->tv_usec /= 1000; 340 tv->tv_usec /= 1000;
331 } 341 }
332 if (unlikely(tz != NULL)) { 342 if (unlikely(tz != NULL)) {
333 /* Avoid memcpy. Some old compilers fail to inline it */ 343 tz->tz_minuteswest = gtod->tz_minuteswest;
334 tz->tz_minuteswest = gtod->sys_tz.tz_minuteswest; 344 tz->tz_dsttime = gtod->tz_dsttime;
335 tz->tz_dsttime = gtod->sys_tz.tz_dsttime;
336 } 345 }
337 346
338 return 0; 347 return 0;
diff --git a/arch/x86/vdso/vdso32/vclock_gettime.c b/arch/x86/vdso/vdso32/vclock_gettime.c
index ca65e4213d63..175cc72c0f68 100644
--- a/arch/x86/vdso/vdso32/vclock_gettime.c
+++ b/arch/x86/vdso/vdso32/vclock_gettime.c
@@ -6,4 +6,25 @@
6 6
7#undef CONFIG_X86_PPRO_FENCE 7#undef CONFIG_X86_PPRO_FENCE
8 8
9#ifdef CONFIG_X86_64
10
11/*
12 * in case of a 32 bit VDSO for a 64 bit kernel fake a 32 bit kernel
13 * configuration
14 */
15#undef CONFIG_64BIT
16#undef CONFIG_X86_64
17#undef CONFIG_ILLEGAL_POINTER_VALUE
18#undef CONFIG_SPARSEMEM_VMEMMAP
19#undef CONFIG_NR_CPUS
20
21#define CONFIG_X86_32 1
22#define CONFIG_PAGE_OFFSET 0
23#define CONFIG_ILLEGAL_POINTER_VALUE 0
24#define CONFIG_NR_CPUS 1
25
26#define BUILD_VDSO32_64
27
28#endif
29
9#include "../vclock_gettime.c" 30#include "../vclock_gettime.c"