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 | |
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')
-rw-r--r-- | arch/x86_64/Kconfig | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/hpet.c | 6 | ||||
-rw-r--r-- | arch/x86_64/kernel/time.c | 6 | ||||
-rw-r--r-- | arch/x86_64/kernel/tsc.c | 7 | ||||
-rw-r--r-- | arch/x86_64/kernel/vmlinux.lds.S | 28 | ||||
-rw-r--r-- | arch/x86_64/kernel/vsyscall.c | 121 |
6 files changed, 100 insertions, 72 deletions
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 286b4606fb1e..56eb14c98475 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig | |||
@@ -28,6 +28,10 @@ config GENERIC_TIME | |||
28 | bool | 28 | bool |
29 | default y | 29 | default y |
30 | 30 | ||
31 | config GENERIC_TIME_VSYSCALL | ||
32 | bool | ||
33 | default y | ||
34 | |||
31 | config ZONE_DMA32 | 35 | config ZONE_DMA32 |
32 | bool | 36 | bool |
33 | default y | 37 | default y |
diff --git a/arch/x86_64/kernel/hpet.c b/arch/x86_64/kernel/hpet.c index c23738899ae9..65a0edd71a17 100644 --- a/arch/x86_64/kernel/hpet.c +++ b/arch/x86_64/kernel/hpet.c | |||
@@ -458,6 +458,11 @@ static cycle_t read_hpet(void) | |||
458 | return (cycle_t)readl(hpet_ptr); | 458 | return (cycle_t)readl(hpet_ptr); |
459 | } | 459 | } |
460 | 460 | ||
461 | static cycle_t __vsyscall_fn vread_hpet(void) | ||
462 | { | ||
463 | return readl((void __iomem *)fix_to_virt(VSYSCALL_HPET) + 0xf0); | ||
464 | } | ||
465 | |||
461 | struct clocksource clocksource_hpet = { | 466 | struct clocksource clocksource_hpet = { |
462 | .name = "hpet", | 467 | .name = "hpet", |
463 | .rating = 250, | 468 | .rating = 250, |
@@ -466,6 +471,7 @@ struct clocksource clocksource_hpet = { | |||
466 | .mult = 0, /* set below */ | 471 | .mult = 0, /* set below */ |
467 | .shift = HPET_SHIFT, | 472 | .shift = HPET_SHIFT, |
468 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 473 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
474 | .vread = vread_hpet, | ||
469 | }; | 475 | }; |
470 | 476 | ||
471 | static int __init init_hpet_clocksource(void) | 477 | static int __init init_hpet_clocksource(void) |
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index d84208e3c618..a87c51705620 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c | |||
@@ -53,13 +53,7 @@ DEFINE_SPINLOCK(rtc_lock); | |||
53 | EXPORT_SYMBOL(rtc_lock); | 53 | EXPORT_SYMBOL(rtc_lock); |
54 | DEFINE_SPINLOCK(i8253_lock); | 54 | DEFINE_SPINLOCK(i8253_lock); |
55 | 55 | ||
56 | unsigned long vxtime_hz = PIT_TICK_RATE; | ||
57 | |||
58 | struct vxtime_data __vxtime __section_vxtime; /* for vsyscalls */ | ||
59 | |||
60 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; | 56 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; |
61 | struct timespec __xtime __section_xtime; | ||
62 | struct timezone __sys_tz __section_sys_tz; | ||
63 | 57 | ||
64 | unsigned long profile_pc(struct pt_regs *regs) | 58 | unsigned long profile_pc(struct pt_regs *regs) |
65 | { | 59 | { |
diff --git a/arch/x86_64/kernel/tsc.c b/arch/x86_64/kernel/tsc.c index 8c92f2fe7e2e..895831865019 100644 --- a/arch/x86_64/kernel/tsc.c +++ b/arch/x86_64/kernel/tsc.c | |||
@@ -180,6 +180,12 @@ static cycle_t read_tsc(void) | |||
180 | return ret; | 180 | return ret; |
181 | } | 181 | } |
182 | 182 | ||
183 | static cycle_t __vsyscall_fn vread_tsc(void) | ||
184 | { | ||
185 | cycle_t ret = (cycle_t)get_cycles_sync(); | ||
186 | return ret; | ||
187 | } | ||
188 | |||
183 | static struct clocksource clocksource_tsc = { | 189 | static struct clocksource clocksource_tsc = { |
184 | .name = "tsc", | 190 | .name = "tsc", |
185 | .rating = 300, | 191 | .rating = 300, |
@@ -188,6 +194,7 @@ static struct clocksource clocksource_tsc = { | |||
188 | .shift = 22, | 194 | .shift = 22, |
189 | .flags = CLOCK_SOURCE_IS_CONTINUOUS | | 195 | .flags = CLOCK_SOURCE_IS_CONTINUOUS | |
190 | CLOCK_SOURCE_MUST_VERIFY, | 196 | CLOCK_SOURCE_MUST_VERIFY, |
197 | .vread = vread_tsc, | ||
191 | }; | 198 | }; |
192 | 199 | ||
193 | void mark_tsc_unstable(void) | 200 | void mark_tsc_unstable(void) |
diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S index c360c4225244..b73212c0a550 100644 --- a/arch/x86_64/kernel/vmlinux.lds.S +++ b/arch/x86_64/kernel/vmlinux.lds.S | |||
@@ -88,31 +88,25 @@ SECTIONS | |||
88 | __vsyscall_0 = VSYSCALL_VIRT_ADDR; | 88 | __vsyscall_0 = VSYSCALL_VIRT_ADDR; |
89 | 89 | ||
90 | . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); | 90 | . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); |
91 | .xtime_lock : AT(VLOAD(.xtime_lock)) { *(.xtime_lock) } | 91 | .vsyscall_fn : AT(VLOAD(.vsyscall_fn)) { *(.vsyscall_fn) } |
92 | xtime_lock = VVIRT(.xtime_lock); | 92 | . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); |
93 | 93 | .vsyscall_gtod_data : AT(VLOAD(.vsyscall_gtod_data)) | |
94 | .vxtime : AT(VLOAD(.vxtime)) { *(.vxtime) } | 94 | { *(.vsyscall_gtod_data) } |
95 | vxtime = VVIRT(.vxtime); | 95 | vsyscall_gtod_data = VVIRT(.vsyscall_gtod_data); |
96 | 96 | ||
97 | .vgetcpu_mode : AT(VLOAD(.vgetcpu_mode)) { *(.vgetcpu_mode) } | 97 | .vgetcpu_mode : AT(VLOAD(.vgetcpu_mode)) { *(.vgetcpu_mode) } |
98 | vgetcpu_mode = VVIRT(.vgetcpu_mode); | 98 | vgetcpu_mode = VVIRT(.vgetcpu_mode); |
99 | 99 | ||
100 | .sys_tz : AT(VLOAD(.sys_tz)) { *(.sys_tz) } | ||
101 | sys_tz = VVIRT(.sys_tz); | ||
102 | |||
103 | .sysctl_vsyscall : AT(VLOAD(.sysctl_vsyscall)) { *(.sysctl_vsyscall) } | ||
104 | sysctl_vsyscall = VVIRT(.sysctl_vsyscall); | ||
105 | |||
106 | .xtime : AT(VLOAD(.xtime)) { *(.xtime) } | ||
107 | xtime = VVIRT(.xtime); | ||
108 | |||
109 | . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); | 100 | . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); |
110 | .jiffies : AT(VLOAD(.jiffies)) { *(.jiffies) } | 101 | .jiffies : AT(VLOAD(.jiffies)) { *(.jiffies) } |
111 | jiffies = VVIRT(.jiffies); | 102 | jiffies = VVIRT(.jiffies); |
112 | 103 | ||
113 | .vsyscall_1 ADDR(.vsyscall_0) + 1024: AT(VLOAD(.vsyscall_1)) { *(.vsyscall_1) } | 104 | .vsyscall_1 ADDR(.vsyscall_0) + 1024: AT(VLOAD(.vsyscall_1)) |
114 | .vsyscall_2 ADDR(.vsyscall_0) + 2048: AT(VLOAD(.vsyscall_2)) { *(.vsyscall_2) } | 105 | { *(.vsyscall_1) } |
115 | .vsyscall_3 ADDR(.vsyscall_0) + 3072: AT(VLOAD(.vsyscall_3)) { *(.vsyscall_3) } | 106 | .vsyscall_2 ADDR(.vsyscall_0) + 2048: AT(VLOAD(.vsyscall_2)) |
107 | { *(.vsyscall_2) } | ||
108 | .vsyscall_3 ADDR(.vsyscall_0) + 3072: AT(VLOAD(.vsyscall_3)) | ||
109 | { *(.vsyscall_3) } | ||
116 | 110 | ||
117 | . = VSYSCALL_VIRT_ADDR + 4096; | 111 | . = VSYSCALL_VIRT_ADDR + 4096; |
118 | 112 | ||
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 | {} |