[PATCH] time: x86_64: re-enable vsyscall support for x86_64
[linux-2.6.git] / arch / x86_64 / kernel / vsyscall.c
index 313dc6a..180ff91 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/seqlock.h>
 #include <linux/jiffies.h>
 #include <linux/sysctl.h>
+#include <linux/clocksource.h>
 #include <linux/getcpu.h>
 #include <linux/cpu.h>
 #include <linux/smp.h>
@@ -34,6 +35,7 @@
 #include <asm/vsyscall.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
+#include <asm/unistd.h>
 #include <asm/fixmap.h>
 #include <asm/errno.h>
 #include <asm/io.h>
 #define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
 #define __syscall_clobber "r11","rcx","memory"
 
-int __sysctl_vsyscall __section_sysctl_vsyscall = 1;
-seqlock_t __xtime_lock __section_xtime_lock = SEQLOCK_UNLOCKED;
+struct vsyscall_gtod_data_t {
+       seqlock_t lock;
+       int sysctl_enabled;
+       struct timeval wall_time_tv;
+       struct timezone sys_tz;
+       cycle_t offset_base;
+       struct clocksource clock;
+};
 int __vgetcpu_mode __section_vgetcpu_mode;
 
-#include <asm/unistd.h>
-
-static __always_inline void timeval_normalize(struct timeval * tv)
+struct vsyscall_gtod_data_t __vsyscall_gtod_data __section_vsyscall_gtod_data =
 {
-       time_t __sec;
-
-       __sec = tv->tv_usec / 1000000;
-       if (__sec) {
-               tv->tv_usec %= 1000000;
-               tv->tv_sec += __sec;
-       }
-}
+       .lock = SEQLOCK_UNLOCKED,
+       .sysctl_enabled = 1,
+};
 
-static __always_inline void do_vgettimeofday(struct timeval * tv)
+void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
 {
-       long sequence, t;
-       unsigned long sec, usec;
-
-       do {
-               sequence = read_seqbegin(&__xtime_lock);
-               
-               sec = __xtime.tv_sec;
-               usec = __xtime.tv_nsec / 1000;
-
-               if (__vxtime.mode != VXTIME_HPET) {
-                       t = get_cycles_sync();
-                       if (t < __vxtime.last_tsc)
-                               t = __vxtime.last_tsc;
-                       usec += ((t - __vxtime.last_tsc) *
-                                __vxtime.tsc_quot) >> 32;
-                       /* See comment in x86_64 do_gettimeofday. */
-               } else {
-                       usec += ((readl((void __iomem *)
-                                  fix_to_virt(VSYSCALL_HPET) + 0xf0) -
-                                 __vxtime.last) * __vxtime.quot) >> 32;
-               }
-       } while (read_seqretry(&__xtime_lock, sequence));
-
-       tv->tv_sec = sec + usec / 1000000;
-       tv->tv_usec = usec % 1000000;
+       unsigned long flags;
+
+       write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags);
+       /* copy vsyscall data */
+       vsyscall_gtod_data.clock = *clock;
+       vsyscall_gtod_data.wall_time_tv.tv_sec = wall_time->tv_sec;
+       vsyscall_gtod_data.wall_time_tv.tv_usec = wall_time->tv_nsec/1000;
+       vsyscall_gtod_data.sys_tz = sys_tz;
+       write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
 }
 
-/* RED-PEN may want to readd seq locking, but then the variable should be write-once. */
+/* RED-PEN may want to readd seq locking, but then the variable should be
+ * write-once.
+ */
 static __always_inline void do_get_tz(struct timezone * tz)
 {
-       *tz = __sys_tz;
+       *tz = __vsyscall_gtod_data.sys_tz;
 }
 
 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)
        int ret;
        asm volatile("vsysc2: syscall"
                : "=a" (ret)
-               : "0" (__NR_gettimeofday),"D" (tv),"S" (tz) : __syscall_clobber );
+               : "0" (__NR_gettimeofday),"D" (tv),"S" (tz)
+               : __syscall_clobber );
        return ret;
 }
 
@@ -114,10 +102,44 @@ static __always_inline long time_syscall(long *t)
        return secs;
 }
 
+static __always_inline void do_vgettimeofday(struct timeval * tv)
+{
+       cycle_t now, base, mask, cycle_delta;
+       unsigned long seq, mult, shift, nsec_delta;
+       cycle_t (*vread)(void);
+       do {
+               seq = read_seqbegin(&__vsyscall_gtod_data.lock);
+
+               vread = __vsyscall_gtod_data.clock.vread;
+               if (unlikely(!__vsyscall_gtod_data.sysctl_enabled || !vread)) {
+                       gettimeofday(tv,0);
+                       return;
+               }
+               now = vread();
+               base = __vsyscall_gtod_data.clock.cycle_last;
+               mask = __vsyscall_gtod_data.clock.mask;
+               mult = __vsyscall_gtod_data.clock.mult;
+               shift = __vsyscall_gtod_data.clock.shift;
+
+               *tv = __vsyscall_gtod_data.wall_time_tv;
+
+       } while (read_seqretry(&__vsyscall_gtod_data.lock, seq));
+
+       /* calculate interval: */
+       cycle_delta = (now - base) & mask;
+       /* convert to nsecs: */
+       nsec_delta = (cycle_delta * mult) >> shift;
+
+       /* convert to usecs and add to timespec: */
+       tv->tv_usec += nsec_delta / NSEC_PER_USEC;
+       while (tv->tv_usec > USEC_PER_SEC) {
+               tv->tv_sec += 1;
+               tv->tv_usec -= USEC_PER_SEC;
+       }
+}
+
 int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
 {
-       if (!__sysctl_vsyscall)
-               return gettimeofday(tv,tz);
        if (tv)
                do_vgettimeofday(tv);
        if (tz)
@@ -129,11 +151,11 @@ int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
  * unlikely */
 time_t __vsyscall(1) vtime(time_t *t)
 {
-       if (!__sysctl_vsyscall)
+       if (unlikely(!__vsyscall_gtod_data.sysctl_enabled))
                return time_syscall(t);
        else if (t)
-               *t = __xtime.tv_sec;            
-       return __xtime.tv_sec;
+               *t = __vsyscall_gtod_data.wall_time_tv.tv_sec;
+       return __vsyscall_gtod_data.wall_time_tv.tv_sec;
 }
 
 /* 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,
                ret = -ENOMEM;
                goto out;
        }
-       if (!sysctl_vsyscall) {
+       if (!vsyscall_gtod_data.sysctl_enabled) {
                writew(SYSCALL, map1);
                writew(SYSCALL, map2);
        } else {
@@ -232,7 +254,8 @@ static int vsyscall_sysctl_nostrat(ctl_table *t, int __user *name, int nlen,
 
 static ctl_table kernel_table2[] = {
        { .ctl_name = 99, .procname = "vsyscall64",
-         .data = &sysctl_vsyscall, .maxlen = sizeof(int), .mode = 0644,
+         .data = &vsyscall_gtod_data.sysctl_enabled, .maxlen = sizeof(int),
+         .mode = 0644,
          .strategy = vsyscall_sysctl_nostrat,
          .proc_handler = vsyscall_sysctl_change },
        {}