diff options
author | john stultz <johnstul@us.ibm.com> | 2007-02-16 04:28:21 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:14:00 -0500 |
commit | 7460ed2844ffad7141e30271c0c3da8336e66014 (patch) | |
tree | 4ae7f2ebf09b59f214d243d0f886fb2c4e7915e8 /arch/x86_64/kernel/vsyscall.c | |
parent | 1489939f0ab64b96998e04068c516c39afe29654 (diff) |
[PATCH] time: x86_64: re-enable vsyscall support for x86_64
Cleanup and re-enable vsyscall gettimeofday using the generic clocksource
infrastructure.
[akpm@osdl.org: cleanup]
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andi Kleen <ak@muc.de>
Cc: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/x86_64/kernel/vsyscall.c')
-rw-r--r-- | arch/x86_64/kernel/vsyscall.c | 121 |
1 files changed, 72 insertions, 49 deletions
diff --git a/arch/x86_64/kernel/vsyscall.c b/arch/x86_64/kernel/vsyscall.c index 313dc6ad780b..180ff919eaf9 100644 --- a/arch/x86_64/kernel/vsyscall.c +++ b/arch/x86_64/kernel/vsyscall.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/seqlock.h> | 26 | #include <linux/seqlock.h> |
27 | #include <linux/jiffies.h> | 27 | #include <linux/jiffies.h> |
28 | #include <linux/sysctl.h> | 28 | #include <linux/sysctl.h> |
29 | #include <linux/clocksource.h> | ||
29 | #include <linux/getcpu.h> | 30 | #include <linux/getcpu.h> |
30 | #include <linux/cpu.h> | 31 | #include <linux/cpu.h> |
31 | #include <linux/smp.h> | 32 | #include <linux/smp.h> |
@@ -34,6 +35,7 @@ | |||
34 | #include <asm/vsyscall.h> | 35 | #include <asm/vsyscall.h> |
35 | #include <asm/pgtable.h> | 36 | #include <asm/pgtable.h> |
36 | #include <asm/page.h> | 37 | #include <asm/page.h> |
38 | #include <asm/unistd.h> | ||
37 | #include <asm/fixmap.h> | 39 | #include <asm/fixmap.h> |
38 | #include <asm/errno.h> | 40 | #include <asm/errno.h> |
39 | #include <asm/io.h> | 41 | #include <asm/io.h> |
@@ -44,56 +46,41 @@ | |||
44 | #define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr))) | 46 | #define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr))) |
45 | #define __syscall_clobber "r11","rcx","memory" | 47 | #define __syscall_clobber "r11","rcx","memory" |
46 | 48 | ||
47 | int __sysctl_vsyscall __section_sysctl_vsyscall = 1; | 49 | struct vsyscall_gtod_data_t { |
48 | seqlock_t __xtime_lock __section_xtime_lock = SEQLOCK_UNLOCKED; | 50 | seqlock_t lock; |
51 | int sysctl_enabled; | ||
52 | struct timeval wall_time_tv; | ||
53 | struct timezone sys_tz; | ||
54 | cycle_t offset_base; | ||
55 | struct clocksource clock; | ||
56 | }; | ||
49 | int __vgetcpu_mode __section_vgetcpu_mode; | 57 | int __vgetcpu_mode __section_vgetcpu_mode; |
50 | 58 | ||
51 | #include <asm/unistd.h> | 59 | struct vsyscall_gtod_data_t __vsyscall_gtod_data __section_vsyscall_gtod_data = |
52 | |||
53 | static __always_inline void timeval_normalize(struct timeval * tv) | ||
54 | { | 60 | { |
55 | time_t __sec; | 61 | .lock = SEQLOCK_UNLOCKED, |
56 | 62 | .sysctl_enabled = 1, | |
57 | __sec = tv->tv_usec / 1000000; | 63 | }; |
58 | if (__sec) { | ||
59 | tv->tv_usec %= 1000000; | ||
60 | tv->tv_sec += __sec; | ||
61 | } | ||
62 | } | ||
63 | 64 | ||
64 | static __always_inline void do_vgettimeofday(struct timeval * tv) | 65 | void update_vsyscall(struct timespec *wall_time, struct clocksource *clock) |
65 | { | 66 | { |
66 | long sequence, t; | 67 | unsigned long flags; |
67 | unsigned long sec, usec; | 68 | |
68 | 69 | write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags); | |
69 | do { | 70 | /* copy vsyscall data */ |
70 | sequence = read_seqbegin(&__xtime_lock); | 71 | vsyscall_gtod_data.clock = *clock; |
71 | 72 | vsyscall_gtod_data.wall_time_tv.tv_sec = wall_time->tv_sec; | |
72 | sec = __xtime.tv_sec; | 73 | vsyscall_gtod_data.wall_time_tv.tv_usec = wall_time->tv_nsec/1000; |
73 | usec = __xtime.tv_nsec / 1000; | 74 | vsyscall_gtod_data.sys_tz = sys_tz; |
74 | 75 | write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags); | |
75 | if (__vxtime.mode != VXTIME_HPET) { | ||
76 | t = get_cycles_sync(); | ||
77 | if (t < __vxtime.last_tsc) | ||
78 | t = __vxtime.last_tsc; | ||
79 | usec += ((t - __vxtime.last_tsc) * | ||
80 | __vxtime.tsc_quot) >> 32; | ||
81 | /* See comment in x86_64 do_gettimeofday. */ | ||
82 | } else { | ||
83 | usec += ((readl((void __iomem *) | ||
84 | fix_to_virt(VSYSCALL_HPET) + 0xf0) - | ||
85 | __vxtime.last) * __vxtime.quot) >> 32; | ||
86 | } | ||
87 | } while (read_seqretry(&__xtime_lock, sequence)); | ||
88 | |||
89 | tv->tv_sec = sec + usec / 1000000; | ||
90 | tv->tv_usec = usec % 1000000; | ||
91 | } | 76 | } |
92 | 77 | ||
93 | /* RED-PEN may want to readd seq locking, but then the variable should be write-once. */ | 78 | /* RED-PEN may want to readd seq locking, but then the variable should be |
79 | * write-once. | ||
80 | */ | ||
94 | static __always_inline void do_get_tz(struct timezone * tz) | 81 | static __always_inline void do_get_tz(struct timezone * tz) |
95 | { | 82 | { |
96 | *tz = __sys_tz; | 83 | *tz = __vsyscall_gtod_data.sys_tz; |
97 | } | 84 | } |
98 | 85 | ||
99 | static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz) | 86 | static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz) |
@@ -101,7 +88,8 @@ static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz) | |||
101 | int ret; | 88 | int ret; |
102 | asm volatile("vsysc2: syscall" | 89 | asm volatile("vsysc2: syscall" |
103 | : "=a" (ret) | 90 | : "=a" (ret) |
104 | : "0" (__NR_gettimeofday),"D" (tv),"S" (tz) : __syscall_clobber ); | 91 | : "0" (__NR_gettimeofday),"D" (tv),"S" (tz) |
92 | : __syscall_clobber ); | ||
105 | return ret; | 93 | return ret; |
106 | } | 94 | } |
107 | 95 | ||
@@ -114,10 +102,44 @@ static __always_inline long time_syscall(long *t) | |||
114 | return secs; | 102 | return secs; |
115 | } | 103 | } |
116 | 104 | ||
105 | static __always_inline void do_vgettimeofday(struct timeval * tv) | ||
106 | { | ||
107 | cycle_t now, base, mask, cycle_delta; | ||
108 | unsigned long seq, mult, shift, nsec_delta; | ||
109 | cycle_t (*vread)(void); | ||
110 | do { | ||
111 | seq = read_seqbegin(&__vsyscall_gtod_data.lock); | ||
112 | |||
113 | vread = __vsyscall_gtod_data.clock.vread; | ||
114 | if (unlikely(!__vsyscall_gtod_data.sysctl_enabled || !vread)) { | ||
115 | gettimeofday(tv,0); | ||
116 | return; | ||
117 | } | ||
118 | now = vread(); | ||
119 | base = __vsyscall_gtod_data.clock.cycle_last; | ||
120 | mask = __vsyscall_gtod_data.clock.mask; | ||
121 | mult = __vsyscall_gtod_data.clock.mult; | ||
122 | shift = __vsyscall_gtod_data.clock.shift; | ||
123 | |||
124 | *tv = __vsyscall_gtod_data.wall_time_tv; | ||
125 | |||
126 | } while (read_seqretry(&__vsyscall_gtod_data.lock, seq)); | ||
127 | |||
128 | /* calculate interval: */ | ||
129 | cycle_delta = (now - base) & mask; | ||
130 | /* convert to nsecs: */ | ||
131 | nsec_delta = (cycle_delta * mult) >> shift; | ||
132 | |||
133 | /* convert to usecs and add to timespec: */ | ||
134 | tv->tv_usec += nsec_delta / NSEC_PER_USEC; | ||
135 | while (tv->tv_usec > USEC_PER_SEC) { | ||
136 | tv->tv_sec += 1; | ||
137 | tv->tv_usec -= USEC_PER_SEC; | ||
138 | } | ||
139 | } | ||
140 | |||
117 | int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz) | 141 | int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz) |
118 | { | 142 | { |
119 | if (!__sysctl_vsyscall) | ||
120 | return gettimeofday(tv,tz); | ||
121 | if (tv) | 143 | if (tv) |
122 | do_vgettimeofday(tv); | 144 | do_vgettimeofday(tv); |
123 | if (tz) | 145 | if (tz) |
@@ -129,11 +151,11 @@ int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz) | |||
129 | * unlikely */ | 151 | * unlikely */ |
130 | time_t __vsyscall(1) vtime(time_t *t) | 152 | time_t __vsyscall(1) vtime(time_t *t) |
131 | { | 153 | { |
132 | if (!__sysctl_vsyscall) | 154 | if (unlikely(!__vsyscall_gtod_data.sysctl_enabled)) |
133 | return time_syscall(t); | 155 | return time_syscall(t); |
134 | else if (t) | 156 | else if (t) |
135 | *t = __xtime.tv_sec; | 157 | *t = __vsyscall_gtod_data.wall_time_tv.tv_sec; |
136 | return __xtime.tv_sec; | 158 | return __vsyscall_gtod_data.wall_time_tv.tv_sec; |
137 | } | 159 | } |
138 | 160 | ||
139 | /* Fast way to get current CPU and node. | 161 | /* Fast way to get current CPU and node. |
@@ -210,7 +232,7 @@ static int vsyscall_sysctl_change(ctl_table *ctl, int write, struct file * filp, | |||
210 | ret = -ENOMEM; | 232 | ret = -ENOMEM; |
211 | goto out; | 233 | goto out; |
212 | } | 234 | } |
213 | if (!sysctl_vsyscall) { | 235 | if (!vsyscall_gtod_data.sysctl_enabled) { |
214 | writew(SYSCALL, map1); | 236 | writew(SYSCALL, map1); |
215 | writew(SYSCALL, map2); | 237 | writew(SYSCALL, map2); |
216 | } else { | 238 | } else { |
@@ -232,7 +254,8 @@ static int vsyscall_sysctl_nostrat(ctl_table *t, int __user *name, int nlen, | |||
232 | 254 | ||
233 | static ctl_table kernel_table2[] = { | 255 | static ctl_table kernel_table2[] = { |
234 | { .ctl_name = 99, .procname = "vsyscall64", | 256 | { .ctl_name = 99, .procname = "vsyscall64", |
235 | .data = &sysctl_vsyscall, .maxlen = sizeof(int), .mode = 0644, | 257 | .data = &vsyscall_gtod_data.sysctl_enabled, .maxlen = sizeof(int), |
258 | .mode = 0644, | ||
236 | .strategy = vsyscall_sysctl_nostrat, | 259 | .strategy = vsyscall_sysctl_nostrat, |
237 | .proc_handler = vsyscall_sysctl_change }, | 260 | .proc_handler = vsyscall_sysctl_change }, |
238 | {} | 261 | {} |