x86: fix vsyscall wreckage
[linux-2.6] / arch / x86 / kernel / vsyscall_64.c
1 /*
2  *  Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE
3  *  Copyright 2003 Andi Kleen, SuSE Labs.
4  *
5  *  Thanks to hpa@transmeta.com for some useful hint.
6  *  Special thanks to Ingo Molnar for his early experience with
7  *  a different vsyscall implementation for Linux/IA32 and for the name.
8  *
9  *  vsyscall 1 is located at -10Mbyte, vsyscall 2 is located
10  *  at virtual address -10Mbyte+1024bytes etc... There are at max 4
11  *  vsyscalls. One vsyscall can reserve more than 1 slot to avoid
12  *  jumping out of line if necessary. We cannot add more with this
13  *  mechanism because older kernels won't return -ENOSYS.
14  *  If we want more than four we need a vDSO.
15  *
16  *  Note: the concept clashes with user mode linux. If you use UML and
17  *  want per guest time just set the kernel.vsyscall64 sysctl to 0.
18  */
19
20 #include <linux/time.h>
21 #include <linux/init.h>
22 #include <linux/kernel.h>
23 #include <linux/timer.h>
24 #include <linux/seqlock.h>
25 #include <linux/jiffies.h>
26 #include <linux/sysctl.h>
27 #include <linux/clocksource.h>
28 #include <linux/getcpu.h>
29 #include <linux/cpu.h>
30 #include <linux/smp.h>
31 #include <linux/notifier.h>
32
33 #include <asm/vsyscall.h>
34 #include <asm/pgtable.h>
35 #include <asm/page.h>
36 #include <asm/unistd.h>
37 #include <asm/fixmap.h>
38 #include <asm/errno.h>
39 #include <asm/io.h>
40 #include <asm/segment.h>
41 #include <asm/desc.h>
42 #include <asm/topology.h>
43 #include <asm/vgtod.h>
44
45 #define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
46 #define __syscall_clobber "r11","cx","memory"
47
48 /*
49  * vsyscall_gtod_data contains data that is :
50  * - readonly from vsyscalls
51  * - written by timer interrupt or systcl (/proc/sys/kernel/vsyscall64)
52  * Try to keep this structure as small as possible to avoid cache line ping pongs
53  */
54 int __vgetcpu_mode __section_vgetcpu_mode;
55
56 struct vsyscall_gtod_data __vsyscall_gtod_data __section_vsyscall_gtod_data =
57 {
58         .lock = SEQLOCK_UNLOCKED,
59         .sysctl_enabled = 1,
60 };
61
62 void update_vsyscall_tz(void)
63 {
64         unsigned long flags;
65
66         write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags);
67         /* sys_tz has changed */
68         vsyscall_gtod_data.sys_tz = sys_tz;
69         write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
70 }
71
72 void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
73 {
74         unsigned long flags;
75
76         write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags);
77         /* copy vsyscall data */
78         vsyscall_gtod_data.clock.vread = clock->vread;
79         vsyscall_gtod_data.clock.cycle_last = clock->cycle_last;
80         vsyscall_gtod_data.clock.mask = clock->mask;
81         vsyscall_gtod_data.clock.mult = clock->mult;
82         vsyscall_gtod_data.clock.shift = clock->shift;
83         vsyscall_gtod_data.wall_time_sec = wall_time->tv_sec;
84         vsyscall_gtod_data.wall_time_nsec = wall_time->tv_nsec;
85         vsyscall_gtod_data.wall_to_monotonic = wall_to_monotonic;
86         write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
87 }
88
89 /* RED-PEN may want to readd seq locking, but then the variable should be
90  * write-once.
91  */
92 static __always_inline void do_get_tz(struct timezone * tz)
93 {
94         *tz = __vsyscall_gtod_data.sys_tz;
95 }
96
97 static __always_inline int gettimeofday(struct timeval *tv, struct timezone *tz)
98 {
99         int ret;
100         asm volatile("syscall"
101                 : "=a" (ret)
102                 : "0" (__NR_gettimeofday),"D" (tv),"S" (tz)
103                 : __syscall_clobber );
104         return ret;
105 }
106
107 static __always_inline long time_syscall(long *t)
108 {
109         long secs;
110         asm volatile("syscall"
111                 : "=a" (secs)
112                 : "0" (__NR_time),"D" (t) : __syscall_clobber);
113         return secs;
114 }
115
116 static __always_inline void do_vgettimeofday(struct timeval * tv)
117 {
118         cycle_t now, base, mask, cycle_delta;
119         unsigned seq;
120         unsigned long mult, shift, nsec;
121         cycle_t (*vread)(void);
122         do {
123                 seq = read_seqbegin(&__vsyscall_gtod_data.lock);
124
125                 vread = __vsyscall_gtod_data.clock.vread;
126                 if (unlikely(!__vsyscall_gtod_data.sysctl_enabled || !vread)) {
127                         gettimeofday(tv,NULL);
128                         return;
129                 }
130                 now = vread();
131                 base = __vsyscall_gtod_data.clock.cycle_last;
132                 mask = __vsyscall_gtod_data.clock.mask;
133                 mult = __vsyscall_gtod_data.clock.mult;
134                 shift = __vsyscall_gtod_data.clock.shift;
135
136                 tv->tv_sec = __vsyscall_gtod_data.wall_time_sec;
137                 nsec = __vsyscall_gtod_data.wall_time_nsec;
138         } while (read_seqretry(&__vsyscall_gtod_data.lock, seq));
139
140         /* calculate interval: */
141         cycle_delta = (now - base) & mask;
142         /* convert to nsecs: */
143         nsec += (cycle_delta * mult) >> shift;
144
145         while (nsec >= NSEC_PER_SEC) {
146                 tv->tv_sec += 1;
147                 nsec -= NSEC_PER_SEC;
148         }
149         tv->tv_usec = nsec / NSEC_PER_USEC;
150 }
151
152 int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
153 {
154         if (tv)
155                 do_vgettimeofday(tv);
156         if (tz)
157                 do_get_tz(tz);
158         return 0;
159 }
160
161 /* This will break when the xtime seconds get inaccurate, but that is
162  * unlikely */
163 time_t __vsyscall(1) vtime(time_t *t)
164 {
165         struct timeval tv;
166         time_t result;
167         if (unlikely(!__vsyscall_gtod_data.sysctl_enabled))
168                 return time_syscall(t);
169
170         vgettimeofday(&tv, NULL);
171         result = tv.tv_sec;
172         if (t)
173                 *t = result;
174         return result;
175 }
176
177 /* Fast way to get current CPU and node.
178    This helps to do per node and per CPU caches in user space.
179    The result is not guaranteed without CPU affinity, but usually
180    works out because the scheduler tries to keep a thread on the same
181    CPU.
182
183    tcache must point to a two element sized long array.
184    All arguments can be NULL. */
185 long __vsyscall(2)
186 vgetcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
187 {
188         unsigned int p;
189         unsigned long j = 0;
190
191         /* Fast cache - only recompute value once per jiffies and avoid
192            relatively costly rdtscp/cpuid otherwise.
193            This works because the scheduler usually keeps the process
194            on the same CPU and this syscall doesn't guarantee its
195            results anyways.
196            We do this here because otherwise user space would do it on
197            its own in a likely inferior way (no access to jiffies).
198            If you don't like it pass NULL. */
199         if (tcache && tcache->blob[0] == (j = __jiffies)) {
200                 p = tcache->blob[1];
201         } else if (__vgetcpu_mode == VGETCPU_RDTSCP) {
202                 /* Load per CPU data from RDTSCP */
203                 native_read_tscp(&p);
204         } else {
205                 /* Load per CPU data from GDT */
206                 asm("lsl %1,%0" : "=r" (p) : "r" (__PER_CPU_SEG));
207         }
208         if (tcache) {
209                 tcache->blob[0] = j;
210                 tcache->blob[1] = p;
211         }
212         if (cpu)
213                 *cpu = p & 0xfff;
214         if (node)
215                 *node = p >> 12;
216         return 0;
217 }
218
219 long __vsyscall(3) venosys_1(void)
220 {
221         return -ENOSYS;
222 }
223
224 #ifdef CONFIG_SYSCTL
225 static ctl_table kernel_table2[] = {
226         { .procname = "vsyscall64",
227           .data = &vsyscall_gtod_data.sysctl_enabled, .maxlen = sizeof(int),
228           .mode = 0644 },
229         {}
230 };
231
232 static ctl_table kernel_root_table2[] = {
233         { .ctl_name = CTL_KERN, .procname = "kernel", .mode = 0555,
234           .child = kernel_table2 },
235         {}
236 };
237 #endif
238
239 /* Assume __initcall executes before all user space. Hopefully kmod
240    doesn't violate that. We'll find out if it does. */
241 static void __cpuinit vsyscall_set_cpu(int cpu)
242 {
243         unsigned long *d;
244         unsigned long node = 0;
245 #ifdef CONFIG_NUMA
246         node = cpu_to_node(cpu);
247 #endif
248         if (cpu_has(&cpu_data(cpu), X86_FEATURE_RDTSCP))
249                 write_rdtscp_aux((node << 12) | cpu);
250
251         /* Store cpu number in limit so that it can be loaded quickly
252            in user space in vgetcpu.
253            12 bits for the CPU and 8 bits for the node. */
254         d = (unsigned long *)(get_cpu_gdt_table(cpu) + GDT_ENTRY_PER_CPU);
255         *d = 0x0f40000000000ULL;
256         *d |= cpu;
257         *d |= (node & 0xf) << 12;
258         *d |= (node >> 4) << 48;
259 }
260
261 static void __cpuinit cpu_vsyscall_init(void *arg)
262 {
263         /* preemption should be already off */
264         vsyscall_set_cpu(raw_smp_processor_id());
265 }
266
267 static int __cpuinit
268 cpu_vsyscall_notifier(struct notifier_block *n, unsigned long action, void *arg)
269 {
270         long cpu = (long)arg;
271         if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN)
272                 smp_call_function_single(cpu, cpu_vsyscall_init, NULL, 0, 1);
273         return NOTIFY_DONE;
274 }
275
276 void __init map_vsyscall(void)
277 {
278         extern char __vsyscall_0;
279         unsigned long physaddr_page0 = __pa_symbol(&__vsyscall_0);
280
281         /* Note that VSYSCALL_MAPPED_PAGES must agree with the code below. */
282         __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL);
283 }
284
285 static int __init vsyscall_init(void)
286 {
287         BUG_ON(((unsigned long) &vgettimeofday !=
288                         VSYSCALL_ADDR(__NR_vgettimeofday)));
289         BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime));
290         BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)));
291         BUG_ON((unsigned long) &vgetcpu != VSYSCALL_ADDR(__NR_vgetcpu));
292 #ifdef CONFIG_SYSCTL
293         register_sysctl_table(kernel_root_table2);
294 #endif
295         on_each_cpu(cpu_vsyscall_init, NULL, 0, 1);
296         hotcpu_notifier(cpu_vsyscall_notifier, 0);
297         return 0;
298 }
299
300 __initcall(vsyscall_init);