diff mbox

[Xen-devel,incomplete] xen: arm: wallclock support (incomplete, needs work/refactoring)

Message ID 1413900470.23337.47.camel@citrix.com
State New
Headers show

Commit Message

Ian Campbell Oct. 21, 2014, 2:07 p.m. UTC
On Tue, 2014-10-21 at 11:50 +0100, Ian Campbell wrote:
> > +/* Wall-clock time is not currently available on ARM, so this is always zero for now:
> > + * http://secure-web.cisco.com/1riZQA377dZLKCo5tuoXAK4khWPPIvJePpjzLaQORhQEDB_kQWnA4fzAgRlX09RiW8tAnBtxXwom3oU37PxptJvdEgqAEvbJAHz9mB8G4h6XwnaDr9bcSh1o32Mldi1w6uBD_3Bf3fOL4m9UHxTUsdzkhF1wAMQEQej0ALt0lZHQ/http%3A%2F%2Fwiki.xenproject.org%2Fwiki%2FXen_ARM_TODO#Expose_Wallclock_time_to_guests
> 
> I have some slightly hacky patches for this, I really should dust them
> off and submit them...

Since that doesn't seem likely to happen soon rather than let them rot
in my tree here are the two patches (one for Xen, one for Linux). IMO
they need a fair bit of refactoring (e.g. to make useful bits of the x86
support common/generic) and other code cleanup etc.

Maybe someone would like to pick them up, or else I'll get to it
eventually.

Ian.
diff mbox

Patch

From 1284ba2380b32bf62789b738dbac4d99da463ea1 Mon Sep 17 00:00:00 2001
From: Ian Campbell <ian.campbell@citrix.com>
Date: Tue, 5 Aug 2014 15:23:23 +0100
Subject: [PATCH] Wallclock time support. Needs much refactoring etc

---
 arch/arm/include/asm/xen/hypercall.h |    9 +++
 arch/arm/xen/enlighten.c             |  121 ++++++++++++++++++++++++++++++++++
 arch/arm/xen/hypercall.S             |    4 +-
 3 files changed, 133 insertions(+), 1 deletion(-)

diff --git a/arch/arm/include/asm/xen/hypercall.h b/arch/arm/include/asm/xen/hypercall.h
index 712b50e..055399a 100644
--- a/arch/arm/include/asm/xen/hypercall.h
+++ b/arch/arm/include/asm/xen/hypercall.h
@@ -35,6 +35,7 @@ 
 
 #include <xen/interface/xen.h>
 #include <xen/interface/sched.h>
+#include <xen/interface/platform.h>
 
 long privcmd_call(unsigned call, unsigned long a1,
 		unsigned long a2, unsigned long a3,
@@ -49,6 +50,14 @@  int HYPERVISOR_memory_op(unsigned int cmd, void *arg);
 int HYPERVISOR_physdev_op(int cmd, void *arg);
 int HYPERVISOR_vcpu_op(int cmd, int vcpuid, void *extra_args);
 int HYPERVISOR_tmem_op(void *arg);
+int HYPERVISOR_dom0_op_raw(struct xen_platform_op *platform_op);
+static inline int
+HYPERVISOR_dom0_op(struct xen_platform_op *platform_op)
+{
+        platform_op->interface_version = XENPF_INTERFACE_VERSION;
+        return HYPERVISOR_dom0_op_raw(platform_op);
+}
+
 int HYPERVISOR_multicall(struct multicall_entry *calls, uint32_t nr);
 
 static inline int
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index 1e63243..0364987 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -11,6 +11,7 @@ 
 #include <xen/xenbus.h>
 #include <xen/page.h>
 #include <xen/interface/sched.h>
+#include <xen/interface/platform.h>
 #include <xen/xen-ops.h>
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
@@ -24,6 +25,7 @@ 
 #include <linux/cpuidle.h>
 #include <linux/cpufreq.h>
 #include <linux/cpu.h>
+#include <linux/pvclock_gtod.h>
 
 #include <linux/mm.h>
 
@@ -219,6 +221,122 @@  static irqreturn_t xen_arm_callback(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
+unsigned long xen_clocksource_read(void)
+{
+	unsigned long val = 0;
+	/* XXX arch timer? Error handling etc etc */
+	if (read_current_timer(&val) < 0)
+		printk("read_current_timer failed?\n");
+	return val;
+}
+
+void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock,
+                            struct pvclock_vcpu_time_info *vcpu_time,
+                            struct timespec *ts)
+{
+        u32 version;
+        u64 delta;
+        struct timespec now;
+
+        /* get wallclock at system boot */
+        do {
+                version = wall_clock->version;
+                rmb();          /* fetch version before time */
+                now.tv_sec  = wall_clock->sec;
+                now.tv_nsec = wall_clock->nsec;
+                rmb();          /* fetch time before checking version */
+        } while ((wall_clock->version & 1) || (version != wall_clock->version));
+
+        //delta = pvclock_clocksource_read(vcpu_time);    /* time since system boot */
+	delta = xen_clocksource_read();
+        delta += now.tv_sec * (u64)NSEC_PER_SEC + now.tv_nsec;
+
+        now.tv_nsec = do_div(delta, NSEC_PER_SEC);
+        now.tv_sec = delta;
+
+        set_normalized_timespec(ts, now.tv_sec, now.tv_nsec);
+}
+
+static void xen_read_wallclock(struct timespec *ts)
+{
+	struct shared_info *s = HYPERVISOR_shared_info;
+	struct pvclock_wall_clock *wall_clock = &(s->wc);
+        struct pvclock_vcpu_time_info *vcpu_time;
+
+	vcpu_time = &get_cpu_var(xen_vcpu)->time;
+	pvclock_read_wallclock(wall_clock, vcpu_time, ts);
+	put_cpu_var(xen_vcpu);
+}
+
+/* Parts could be common with x86? */
+static int xen_pvclock_gtod_notify(struct notifier_block *nb,
+				   unsigned long was_set, void *priv)
+{
+	/* Protected by the calling core code serialization */
+	static struct timespec next_sync;
+
+	struct xen_platform_op op;
+	struct timespec now;
+
+	now = __current_kernel_time();
+
+	/*
+	 * We only take the expensive HV call when the clock was set
+	 * or when the 11 minutes RTC synchronization time elapsed.
+	 */
+	if (!was_set && timespec_compare(&now, &next_sync) < 0)
+		return NOTIFY_OK;
+
+	op.cmd = XENPF_settime;
+	op.u.settime.secs = now.tv_sec;
+	op.u.settime.nsecs = now.tv_nsec;
+	op.u.settime.system_time = xen_clocksource_read();
+	printk("GTOD: Setting to %ld.%ld at %lld\n",
+	       (long)op.u.settime.secs,
+	       (long)op.u.settime.nsecs,
+	       (long long)op.u.settime.system_time);
+	(void)HYPERVISOR_dom0_op(&op);
+
+	/*
+	 * Move the next drift compensation time 11 minutes
+	 * ahead. That's emulating the sync_cmos_clock() update for
+	 * the hardware RTC.
+	 */
+	next_sync = now;
+	next_sync.tv_sec += 11 * 60;
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block xen_pvclock_gtod_notifier = {
+	.notifier_call = xen_pvclock_gtod_notify,
+};
+
+static void __init xen_time_init(void)
+{
+	//int cpu = smp_processor_id();
+	struct timespec tp;
+
+	/* Set initial system time with full resolution */
+	xen_read_wallclock(&tp);
+	do_settimeofday(&tp);
+
+	printk("INITIAL TIME %ld.%ld\n",
+	       (long)tp.tv_sec,
+	       (long)tp.tv_nsec);
+	{
+		struct shared_info *s = HYPERVISOR_shared_info;
+		struct pvclock_wall_clock *wall_clock = &(s->wc);
+		struct pvclock_vcpu_time_info *vcpu_time;
+		printk("SHARED INFO TIME %ld.%ld\n",
+		       (long)wall_clock->sec,
+		       (long)wall_clock->nsec);
+	}
+
+	if (xen_initial_domain())
+		pvclock_gtod_register_notifier(&xen_pvclock_gtod_notifier);
+}
+
 /*
  * see Documentation/devicetree/bindings/arm/xen.txt for the
  * documentation of the Xen Device Tree format.
@@ -323,6 +441,8 @@  static int __init xen_guest_init(void)
 
 	register_cpu_notifier(&xen_cpu_notifier);
 
+	xen_time_init();
+
 	return 0;
 }
 early_initcall(xen_guest_init);
@@ -359,4 +479,5 @@  EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op);
 EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op);
 EXPORT_SYMBOL_GPL(HYPERVISOR_tmem_op);
 EXPORT_SYMBOL_GPL(HYPERVISOR_multicall);
+EXPORT_SYMBOL_GPL(HYPERVISOR_dom0_op_raw);
 EXPORT_SYMBOL_GPL(privcmd_call);
diff --git a/arch/arm/xen/hypercall.S b/arch/arm/xen/hypercall.S
index 44e3a5f..d3bf35e 100644
--- a/arch/arm/xen/hypercall.S
+++ b/arch/arm/xen/hypercall.S
@@ -78,7 +78,6 @@  ENTRY(HYPERVISOR_##hypercall)			\
 ENDPROC(HYPERVISOR_##hypercall)
 
                 .text
-
 HYPERCALL2(xen_version);
 HYPERCALL3(console_io);
 HYPERCALL3(grant_table_op);
@@ -91,6 +90,9 @@  HYPERCALL3(vcpu_op);
 HYPERCALL1(tmem_op);
 HYPERCALL2(multicall);
 
+#define __HYPERVISOR_dom0_op_raw __HYPERVISOR_dom0_op /* Hack to allow setting interface version from C wrapper */
+HYPERCALL1(dom0_op_raw);/*XXX legacy name for what is now platform_op, used by x86 too*/
+
 ENTRY(privcmd_call)
 	stmdb sp!, {r4}
 	mov r12, r0
-- 
1.7.10.4