MIPS: VDSO: Add implementations of gettimeofday() and clock_gettime()

Add user-mode implementations of gettimeofday() and clock_gettime() to
the VDSO. This is currently usable with 2 clocksources: the CP0 count
register, which is accessible to user-mode via RDHWR on R2 and later
cores, or the MIPS Global Interrupt Controller (GIC) timer, which
provides a "user-mode visible" section containing a mirror of its
counter registers. This section must be mapped into user memory, which
is done below the VDSO data page.

When a supported clocksource is not in use, the VDSO functions will
return -ENOSYS, which causes libc to fall back on the standard syscall
path.

When support for neither of these clocksources is compiled into the
kernel at all, the VDSO still provides clock_gettime(), as the coarse
realtime/monotonic clocks can still be implemented. However,
gettimeofday() is not provided in this case as nothing can be done
without a suitable clocksource. This causes the symbol lookup to fail
in libc and it will then always use the standard syscall path.

This patch includes a workaround for a bug in QEMU which results in
RDHWR on the CP0 count register always returning a constant (incorrect)
value. A fix for this has been submitted, and the workaround can be
removed after the fix has been in stable releases for a reasonable
amount of time.

A simple performance test which calls gettimeofday() 1000 times in a
loop and calculates the average execution time gives the following
results on a Malta + I6400 (running at 20MHz):

 - Syscall:    ~31000 ns
 - VDSO (GIC): ~15000 ns
 - VDSO (CP0): ~9500 ns

[markos.chandras@imgtec.com:
- Minor code re-arrangements in order for mappings to be made
in the order they appear to the process' address space.
- Move do_{monotonic, realtime} outside of the MIPS_CLOCK_VSYSCALL ifdef
- Use gic_get_usm_range so we can do the GIC mapping in the
arch/mips/kernel/vdso instead of the GIC irqchip driver]

Signed-off-by: Alex Smith <alex.smith@imgtec.com>
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/11338/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/include/asm/vdso.h b/arch/mips/include/asm/vdso.h
index db2d45b..8f4ca5d 100644
--- a/arch/mips/include/asm/vdso.h
+++ b/arch/mips/include/asm/vdso.h
@@ -13,6 +13,8 @@
 
 #include <linux/mm_types.h>
 
+#include <asm/barrier.h>
+
 /**
  * struct mips_vdso_image - Details of a VDSO image.
  * @data: Pointer to VDSO image data (page-aligned).
@@ -53,18 +55,82 @@
 
 /**
  * union mips_vdso_data - Data provided by the kernel for the VDSO.
+ * @xtime_sec:		Current real time (seconds part).
+ * @xtime_nsec:		Current real time (nanoseconds part, shifted).
+ * @wall_to_mono_sec:	Wall-to-monotonic offset (seconds part).
+ * @wall_to_mono_nsec:	Wall-to-monotonic offset (nanoseconds part).
+ * @seq_count:		Counter to synchronise updates (odd = updating).
+ * @cs_shift:		Clocksource shift value.
+ * @clock_mode:		Clocksource to use for time functions.
+ * @cs_mult:		Clocksource multiplier value.
+ * @cs_cycle_last:	Clock cycle value at last update.
+ * @cs_mask:		Clocksource mask value.
+ * @tz_minuteswest:	Minutes west of Greenwich (from timezone).
+ * @tz_dsttime:		Type of DST correction (from timezone).
  *
  * This structure contains data needed by functions within the VDSO. It is
- * populated by the kernel and mapped read-only into user memory.
+ * populated by the kernel and mapped read-only into user memory. The time
+ * fields are mirrors of internal data from the timekeeping infrastructure.
  *
  * Note: Care should be taken when modifying as the layout must remain the same
  * for both 64- and 32-bit (for 32-bit userland on 64-bit kernel).
  */
 union mips_vdso_data {
 	struct {
+		u64 xtime_sec;
+		u64 xtime_nsec;
+		u32 wall_to_mono_sec;
+		u32 wall_to_mono_nsec;
+		u32 seq_count;
+		u32 cs_shift;
+		u8 clock_mode;
+		u32 cs_mult;
+		u64 cs_cycle_last;
+		u64 cs_mask;
+		s32 tz_minuteswest;
+		s32 tz_dsttime;
 	};
 
 	u8 page[PAGE_SIZE];
 };
 
+static inline u32 vdso_data_read_begin(const union mips_vdso_data *data)
+{
+	u32 seq;
+
+	while (true) {
+		seq = ACCESS_ONCE(data->seq_count);
+		if (likely(!(seq & 1))) {
+			/* Paired with smp_wmb() in vdso_data_write_*(). */
+			smp_rmb();
+			return seq;
+		}
+
+		cpu_relax();
+	}
+}
+
+static inline bool vdso_data_read_retry(const union mips_vdso_data *data,
+					u32 start_seq)
+{
+	/* Paired with smp_wmb() in vdso_data_write_*(). */
+	smp_rmb();
+	return unlikely(data->seq_count != start_seq);
+}
+
+static inline void vdso_data_write_begin(union mips_vdso_data *data)
+{
+	++data->seq_count;
+
+	/* Ensure sequence update is written before other data page values. */
+	smp_wmb();
+}
+
+static inline void vdso_data_write_end(union mips_vdso_data *data)
+{
+	/* Ensure data values are written before updating sequence again. */
+	smp_wmb();
+	++data->seq_count;
+}
+
 #endif /* __ASM_VDSO_H */