kernel: gtod: vsyscall

Introduce kernel user helper vsyscall to allow a higher performance
gettimeofday implementation in user-space.

Change-Id: I9b9971d217382129170ecd6a35461539dac9eebb
Signed-off-by: Brent DeGraaf <bdegraaf@codeaurora.org>
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 56691a5..07d599b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -272,6 +272,13 @@
 
 	 If unsure, say N
 
+config GENERIC_TIME_VSYSCALL
+	bool "Enable gettimeofday updates"
+	depends on CPU_V7
+	help
+	  Enables updating the kernel user helper area with the xtime struct
+	  data for gettimeofday via kernel user helpers.
+
 source "init/Kconfig"
 
 source "kernel/Kconfig.freezer"
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 22b0f1e..cd1e2a0 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -63,6 +63,7 @@
 obj-$(CONFIG_SWP_EMULATE)	+= swp_emulate.o
 CFLAGS_swp_emulate.o		:= -Wa,-march=armv7-a
 obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o
+obj-$(CONFIG_GENERIC_TIME_VSYSCALL)	+= update_vsyscall_arm.o
 
 obj-$(CONFIG_CPU_XSCALE)	+= xscale-cp0.o
 obj-$(CONFIG_CPU_XSC3)		+= xscale-cp0.o
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 7a8c2d6..ddd421c 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -764,6 +764,97 @@
 	.align	5
 	.globl	__kuser_helper_start
 __kuser_helper_start:
+#ifdef GENERIC_TIME_VSYSCALL
+/*
+ * Reference declaration:
+ *
+ *      extern struct timezone __kernel_helper_gtod_timezone
+ *      extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ *  Definition and user space usage example:
+ *
+ *      #define __kernel_helper_gtod_timezone (*(unsigned int*)0xffff0f20)
+ *      #define __kernel_helper_gtod_seqnum   (*(unsigned int*)0xffff0f28)
+ *
+ *      unsigned int prelock, postlock ;
+ *      do {
+ *          prelock = __kernel_helper_gtod_seqnum;
+ *          memcpy(&tz, (void*)&(__kernel_helper_gtod_timezone),
+ *                     sizeof(struct timezone)) ;
+ *          postlock = __kernel_helper_gtod_seqnum;
+ *      } while (prelock != postlock);
+ *
+ * 0xffff0f20-3: tz_minuteswest
+ * 0xffff0f24-7: tz_dsttime
+ * 0xffff0f28-b: sequence #.
+ * 0xffff0f30-3: offset into CONFIG_USER_ACCESSIBLE_TIMER_BASE to get the timer.
+ * 0xffff0f34-7: Feature flag
+ * 0xffff0f38-b: wall-to-mononic: tv_sec
+ * 0xffff0f3c-f: wall-to-mononic: tv_nsec
+ */
+	.globl  __kuser_gtod_timezone
+__kuser_gtod_timezone: @0xffff0f20
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	/* This offset is where the flag to enable the
+	 * user accessible timers is located.
+	 */
+	.word	0
+	.word	0
+	.word	0
+	.align	5
+
+/*
+ * Reference declaration:
+ *
+ *      extern struct timeval __kernel_helper_gtod_timeval
+ *      extern unsigned int __kernel_helper_gtod_seqnum
+ *
+ *  Definition and user space usage example:
+ *
+ *      #define __kernel_helper_gtod_timeval (*(unsigned int*)0xffff0f40)
+ *      #define __kernel_helper_gtod_seqnum   (*(unsigned int*)0xffff0f48)
+ *
+ *      unsigned int prelock, postlock ;
+ *      struct gtod {
+ *          uint64_t  cycle_last;
+ *          uint64_t  mask;
+ *          uint32_t  mult;
+ *          uint32_t  shift;
+ *          uint32_t  tv_sec;
+ *          uint32_t  tv_nsec;
+ *      };
+ *      struct gtod gdtod;
+ *
+ *      do {
+ *          prelock = __kernel_helper_gtod_seqnum;
+ *          memcpy(&gdtod, (void*)&(__kernel_helper_gtod_timeval),
+ *                     sizeof(struct gtod)) ;
+ *          postlock = __kernel_helper_gtod_seqnum;
+ *      } while (prelock != postlock);
+ *
+ * 0xffff0f40-7: cycle_last
+ * 0xffff0f48-f: mask
+ * 0xffff0f50-3: mult
+ * 0xffff0f54-7: shift
+ * 0xffff0f58-b: tv_sec
+ * 0xffff0f5c-f: tv_nsec
+ */
+	.globl  __kuser_gtod_timeval
+__kuser_gtod_timeval:  @0xffff0f40
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.align	5
+#endif
 
 /*
  * Due to the length of some sequences, __kuser_cmpxchg64 spans 2 regular
diff --git a/arch/arm/kernel/update_vsyscall_arm.c b/arch/arm/kernel/update_vsyscall_arm.c
new file mode 100644
index 0000000..51f47ae
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.c
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include "update_vsyscall_arm.h"
+/*
+ * See entry-armv.S for the offsets into the kernel user helper for
+ * these fields.
+ */
+#define ARM_VSYSCALL_TIMER_TZ			0xf20
+#define ARM_VSYSCALL_TIMER_SEQ			0xf28
+#define ARM_VSYSCALL_TIMER_OFFSET		0xf30
+#define ARM_VSYSCALL_TIMER_WTM_TV_SEC		0xf38
+#define ARM_VSYSCALL_TIMER_WTM_TV_NSEC		0xf3c
+#define ARM_VSYSCALL_TIMER_CYCLE_LAST		0xf40
+#define ARM_VSYSCALL_TIMER_MASK			0xf48
+#define ARM_VSYSCALL_TIMER_MULT			0xf50
+#define ARM_VSYSCALL_TIMER_SHIFT		0xf54
+#define ARM_VSYSCALL_TIMER_TV_SEC		0xf58
+#define ARM_VSYSCALL_TIMER_TV_NSEC		0xf5c
+
+struct kernel_gtod_t {
+	u64  cycle_last;
+	u64  mask;
+	u32  mult;
+	u32  shift;
+	u32  tv_sec;
+	u32  tv_nsec;
+};
+
+struct kernel_tz_t {
+	u32  tz_minuteswest;
+	u32  tz_dsttime;
+};
+
+struct kernel_wtm_t {
+	u32  tv_sec;
+	u32  tv_nsec;
+};
+
+/*
+ * Updates the kernel user helper area with the current timespec
+ * data, as well as additional fields needed to calculate
+ * gettimeofday, clock_gettime, etc.
+ */
+void
+update_vsyscall(struct timespec *ts, struct timespec *wtm,
+	struct clocksource *c, u32 mult)
+{
+	unsigned long vectors = (unsigned long)vectors_page;
+	unsigned long flags;
+	unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+	struct kernel_gtod_t *dgtod = (struct kernel_gtod_t *)(vectors +
+		ARM_VSYSCALL_TIMER_CYCLE_LAST);
+	struct kernel_wtm_t *dgwtm = (struct kernel_wtm_t *)(vectors +
+		ARM_VSYSCALL_TIMER_WTM_TV_SEC);
+
+	write_seqlock_irqsave(&kuh_time_lock, flags);
+	*seqnum = kuh_time_lock.sequence;
+	dgtod->cycle_last = c->cycle_last;
+	dgtod->mask = c->mask;
+	dgtod->mult = c->mult;
+	dgtod->shift = c->shift;
+	dgtod->tv_sec = ts->tv_sec;
+	dgtod->tv_nsec = ts->tv_nsec;
+	dgwtm->tv_sec = wtm->tv_sec;
+	dgwtm->tv_nsec = wtm->tv_nsec;
+	*seqnum = kuh_time_lock.sequence + 1;
+	write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall);
+
+void
+update_vsyscall_tz(void)
+{
+	unsigned long vectors = (unsigned long)vectors_page;
+	unsigned long flags;
+	unsigned *seqnum = (unsigned *)(vectors + ARM_VSYSCALL_TIMER_SEQ);
+	struct kernel_tz_t *dgtod = (struct kernel_tz_t *)(vectors +
+		ARM_VSYSCALL_TIMER_TZ);
+
+	write_seqlock_irqsave(&kuh_time_lock, flags);
+	*seqnum = kuh_time_lock.sequence;
+	dgtod->tz_minuteswest = sys_tz.tz_minuteswest;
+	dgtod->tz_dsttime = sys_tz.tz_dsttime;
+	*seqnum = kuh_time_lock.sequence + 1;
+	write_sequnlock_irqrestore(&kuh_time_lock, flags);
+}
+EXPORT_SYMBOL(update_vsyscall_tz);
diff --git a/arch/arm/kernel/update_vsyscall_arm.h b/arch/arm/kernel/update_vsyscall_arm.h
new file mode 100644
index 0000000..d06ca56
--- /dev/null
+++ b/arch/arm/kernel/update_vsyscall_arm.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+
+extern void *vectors_page;
+extern struct timezone sys_tz;
+
+/*
+ * This read-write spinlock protects us from races in SMP while
+ * updating the kernel user helper-embedded time.
+ */
+__cacheline_aligned_in_smp DEFINE_SEQLOCK(kuh_time_lock);