|
@@ -26,6 +26,7 @@
|
|
#include <linux/seqlock.h>
|
|
#include <linux/seqlock.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/sysctl.h>
|
|
#include <linux/sysctl.h>
|
|
|
|
+#include <linux/clocksource.h>
|
|
#include <linux/getcpu.h>
|
|
#include <linux/getcpu.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/smp.h>
|
|
@@ -34,6 +35,7 @@
|
|
#include <asm/vsyscall.h>
|
|
#include <asm/vsyscall.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/page.h>
|
|
#include <asm/page.h>
|
|
|
|
+#include <asm/unistd.h>
|
|
#include <asm/fixmap.h>
|
|
#include <asm/fixmap.h>
|
|
#include <asm/errno.h>
|
|
#include <asm/errno.h>
|
|
#include <asm/io.h>
|
|
#include <asm/io.h>
|
|
@@ -44,56 +46,41 @@
|
|
#define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
|
|
#define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
|
|
#define __syscall_clobber "r11","rcx","memory"
|
|
#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;
|
|
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)
|
|
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)
|
|
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;
|
|
int ret;
|
|
asm volatile("vsysc2: syscall"
|
|
asm volatile("vsysc2: syscall"
|
|
: "=a" (ret)
|
|
: "=a" (ret)
|
|
- : "0" (__NR_gettimeofday),"D" (tv),"S" (tz) : __syscall_clobber );
|
|
|
|
|
|
+ : "0" (__NR_gettimeofday),"D" (tv),"S" (tz)
|
|
|
|
+ : __syscall_clobber );
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -114,10 +102,44 @@ static __always_inline long time_syscall(long *t)
|
|
return secs;
|
|
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)
|
|
int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
|
|
{
|
|
{
|
|
- if (!__sysctl_vsyscall)
|
|
|
|
- return gettimeofday(tv,tz);
|
|
|
|
if (tv)
|
|
if (tv)
|
|
do_vgettimeofday(tv);
|
|
do_vgettimeofday(tv);
|
|
if (tz)
|
|
if (tz)
|
|
@@ -129,11 +151,11 @@ int __vsyscall(0) vgettimeofday(struct timeval * tv, struct timezone * tz)
|
|
* unlikely */
|
|
* unlikely */
|
|
time_t __vsyscall(1) vtime(time_t *t)
|
|
time_t __vsyscall(1) vtime(time_t *t)
|
|
{
|
|
{
|
|
- if (!__sysctl_vsyscall)
|
|
|
|
|
|
+ if (unlikely(!__vsyscall_gtod_data.sysctl_enabled))
|
|
return time_syscall(t);
|
|
return time_syscall(t);
|
|
else if (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.
|
|
/* 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;
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
- if (!sysctl_vsyscall) {
|
|
|
|
|
|
+ if (!vsyscall_gtod_data.sysctl_enabled) {
|
|
writew(SYSCALL, map1);
|
|
writew(SYSCALL, map1);
|
|
writew(SYSCALL, map2);
|
|
writew(SYSCALL, map2);
|
|
} else {
|
|
} else {
|
|
@@ -232,7 +254,8 @@ static int vsyscall_sysctl_nostrat(ctl_table *t, int __user *name, int nlen,
|
|
|
|
|
|
static ctl_table kernel_table2[] = {
|
|
static ctl_table kernel_table2[] = {
|
|
{ .ctl_name = 99, .procname = "vsyscall64",
|
|
{ .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,
|
|
.strategy = vsyscall_sysctl_nostrat,
|
|
.proc_handler = vsyscall_sysctl_change },
|
|
.proc_handler = vsyscall_sysctl_change },
|
|
{}
|
|
{}
|