# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1255949916 -3600
# Node ID ad2fd7b94bd32e4256640a908ca3c8b40f505021
# Parent 5f28661bb2bbfd903b79d52e2aeabc4e4cb1f4d8
Allow guests to register secondary vcpu_time_info
Allow a guest to register a second location for the VCPU time info
structure for each vcpu. This is intended to allow the guest kernel
to map this information into a usermode accessible page, so that
usermode can efficiently calculate system time from the TSC without
having to make a syscall.
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@xxxxxxxxxx>
Signed-off-by: Keir Fraser <keir.fraser@xxxxxxxxxx>
---
xen/arch/x86/domain.c | 19 +++++++
xen/arch/x86/time.c | 107 ++++++++++++++++++++++++++++---------------
xen/include/asm-x86/domain.h | 4 +
xen/include/asm-x86/time.h | 2
xen/include/public/vcpu.h | 28 +++++++++++
5 files changed, 125 insertions(+), 35 deletions(-)
diff -r 5f28661bb2bb -r ad2fd7b94bd3 xen/arch/x86/domain.c
--- a/xen/arch/x86/domain.c Mon Oct 19 10:57:58 2009 +0100
+++ b/xen/arch/x86/domain.c Mon Oct 19 11:58:36 2009 +0100
@@ -962,6 +962,25 @@ arch_do_vcpu_op(
break;
}
+ case VCPUOP_register_vcpu_time_memory_area:
+ {
+ struct vcpu_register_time_memory_area area;
+
+ rc = -EFAULT;
+ if ( copy_from_guest(&area, arg, 1) )
+ break;
+
+ if ( !guest_handle_okay(area.addr.h, 1) )
+ break;
+
+ rc = 0;
+ v->arch.time_info_guest = area.addr.h;
+
+ force_update_vcpu_system_time(v);
+
+ break;
+ }
+
case VCPUOP_get_physid:
{
struct vcpu_get_physid cpu_id;
diff -r 5f28661bb2bb -r ad2fd7b94bd3 xen/arch/x86/time.c
--- a/xen/arch/x86/time.c Mon Oct 19 10:57:58 2009 +0100
+++ b/xen/arch/x86/time.c Mon Oct 19 11:58:36 2009 +0100
@@ -22,6 +22,7 @@
#include <xen/irq.h>
#include <xen/softirq.h>
#include <xen/keyhandler.h>
+#include <xen/guest_access.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/mpspec.h>
@@ -807,23 +808,15 @@ s_time_t get_s_time(void)
return now;
}
-static inline void version_update_begin(u32 *version)
-{
- /* Explicitly OR with 1 just in case version number gets out of sync. */
- *version = (*version + 1) | 1;
- wmb();
-}
-
-static inline void version_update_end(u32 *version)
-{
- wmb();
- (*version)++;
-}
-
-void update_vcpu_system_time(struct vcpu *v)
+/* Explicitly OR with 1 just in case version number gets out of sync. */
+#define version_update_begin(v) (((v)+1)|1)
+#define version_update_end(v) ((v)+1)
+
+static void __update_vcpu_system_time(struct vcpu *v, int force)
{
struct cpu_time *t;
- struct vcpu_time_info *u;
+ struct vcpu_time_info *u, _u;
+ XEN_GUEST_HANDLE(vcpu_time_info_t) user_u;
if ( v->vcpu_info == NULL )
return;
@@ -831,35 +824,79 @@ void update_vcpu_system_time(struct vcpu
t = &this_cpu(cpu_time);
u = &vcpu_info(v, time);
+ /* Don't bother unless timestamps have changed or we are forced. */
+ if ( !force && (u->tsc_timestamp == (v->domain->arch.vtsc
+ ? t->stime_local_stamp
+ : t->local_tsc_stamp)) )
+ return;
+
+ memset(&_u, 0, sizeof(_u));
+
if ( v->domain->arch.vtsc )
{
- if ( u->tsc_timestamp == t->stime_local_stamp )
- return;
- version_update_begin(&u->version);
- u->tsc_timestamp = t->stime_local_stamp;
- u->system_time = t->stime_local_stamp;
- u->tsc_to_system_mul = 0x80000000u;
- u->tsc_shift = 1;
- version_update_end(&u->version);
- }
- else if ( u->tsc_timestamp != t->local_tsc_stamp )
- {
- version_update_begin(&u->version);
- u->tsc_timestamp = t->local_tsc_stamp;
- u->system_time = t->stime_local_stamp;
- u->tsc_to_system_mul = t->tsc_scale.mul_frac;
- u->tsc_shift = (s8)t->tsc_scale.shift;
- version_update_end(&u->version);
- }
+ _u.tsc_timestamp = t->stime_local_stamp;
+ _u.system_time = t->stime_local_stamp;
+ _u.tsc_to_system_mul = 0x80000000u;
+ _u.tsc_shift = 1;
+ }
+ else
+ {
+ _u.tsc_timestamp = t->local_tsc_stamp;
+ _u.system_time = t->stime_local_stamp;
+ _u.tsc_to_system_mul = t->tsc_scale.mul_frac;
+ _u.tsc_shift = (s8)t->tsc_scale.shift;
+ }
+
+ /* 1. Update guest kernel version. */
+ _u.version = u->version = version_update_begin(u->version);
+ wmb();
+ /* 2. Update all other guest kernel fields. */
+ *u = _u;
+ wmb();
+ /* 3. Update guest kernel version. */
+ u->version = version_update_end(u->version);
+
+ user_u = v->arch.time_info_guest;
+ if ( !guest_handle_is_null(user_u) )
+ {
+ /* 1. Update userspace version. */
+ __copy_field_to_guest(user_u, &_u, version);
+ wmb();
+ /* 2. Update all other userspavce fields. */
+ __copy_to_guest(user_u, &_u, 1);
+ wmb();
+ /* 3. Update userspace version. */
+ _u.version = version_update_end(_u.version);
+ __copy_field_to_guest(user_u, &_u, version);
+ }
+}
+
+void update_vcpu_system_time(struct vcpu *v)
+{
+ __update_vcpu_system_time(v, 0);
+}
+
+void force_update_vcpu_system_time(struct vcpu *v)
+{
+ __update_vcpu_system_time(v, 1);
}
void update_domain_wallclock_time(struct domain *d)
{
+ uint32_t *wc_version;
+
spin_lock(&wc_lock);
- version_update_begin(&shared_info(d, wc_version));
+
+ wc_version = &shared_info(d, wc_version);
+ *wc_version = version_update_begin(*wc_version);
+ wmb();
+
shared_info(d, wc_sec) = wc_sec + d->time_offset_seconds;
shared_info(d, wc_nsec) = wc_nsec;
- version_update_end(&shared_info(d, wc_version));
+
+ wmb();
+ *wc_version = version_update_end(*wc_version);
+
spin_unlock(&wc_lock);
}
diff -r 5f28661bb2bb -r ad2fd7b94bd3 xen/include/asm-x86/domain.h
--- a/xen/include/asm-x86/domain.h Mon Oct 19 10:57:58 2009 +0100
+++ b/xen/include/asm-x86/domain.h Mon Oct 19 11:58:36 2009 +0100
@@ -6,6 +6,7 @@
#include <asm/hvm/vcpu.h>
#include <asm/hvm/domain.h>
#include <asm/e820.h>
+#include <public/vcpu.h>
#define has_32bit_shinfo(d) ((d)->arch.has_32bit_shinfo)
#define is_pv_32bit_domain(d) ((d)->arch.is_32bit_pv)
@@ -418,6 +419,9 @@ struct arch_vcpu
uint32_t gdbsx_vcpu_event;
#endif
+ /* A secondary copy of the vcpu time info. */
+ XEN_GUEST_HANDLE(vcpu_time_info_t) time_info_guest;
+
} __cacheline_aligned;
/* Shorthands to improve code legibility. */
diff -r 5f28661bb2bb -r ad2fd7b94bd3 xen/include/asm-x86/time.h
--- a/xen/include/asm-x86/time.h Mon Oct 19 10:57:58 2009 +0100
+++ b/xen/include/asm-x86/time.h Mon Oct 19 11:58:36 2009 +0100
@@ -43,4 +43,6 @@ uint64_t ns_to_acpi_pm_tick(uint64_t ns)
void pv_soft_rdtsc(struct vcpu *v, struct cpu_user_regs *regs);
+void force_update_vcpu_system_time(struct vcpu *v);
+
#endif /* __X86_TIME_H__ */
diff -r 5f28661bb2bb -r ad2fd7b94bd3 xen/include/public/vcpu.h
--- a/xen/include/public/vcpu.h Mon Oct 19 10:57:58 2009 +0100
+++ b/xen/include/public/vcpu.h Mon Oct 19 11:58:36 2009 +0100
@@ -202,6 +202,34 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_get_physid_
#define xen_vcpu_physid_to_x86_acpiid(physid) \
((((uint32_t)((physid)>>32)) >= 0xff) ? 0xff : ((uint8_t)((physid)>>32)))
+/*
+ * Register a memory location to get a secondary copy of the vcpu time
+ * parameters. The master copy still exists as part of the vcpu shared
+ * memory area, and this secondary copy is updated whenever the master copy
+ * is updated (and using the same versioning scheme for synchronisation).
+ *
+ * The intent is that this copy may be mapped (RO) into userspace so
+ * that usermode can compute system time using the time info and the
+ * tsc. Usermode will see an array of vcpu_time_info structures, one
+ * for each vcpu, and choose the right one by an existing mechanism
+ * which allows it to get the current vcpu number (such as via a
+ * segment limit). It can then apply the normal algorithm to compute
+ * system time from the tsc.
+ *
+ * @extra_arg == pointer to vcpu_register_time_info_memory_area structure.
+ */
+#define VCPUOP_register_vcpu_time_memory_area 13
+DEFINE_XEN_GUEST_HANDLE(vcpu_time_info_t);
+struct vcpu_register_time_memory_area {
+ union {
+ XEN_GUEST_HANDLE(vcpu_time_info_t) h;
+ struct vcpu_time_info *v;
+ uint64_t p;
+ } addr;
+};
+typedef struct vcpu_register_time_memory_area vcpu_register_time_memory_area_t;
+DEFINE_XEN_GUEST_HANDLE(vcpu_register_time_memory_area_t);
+
#endif /* __XEN_PUBLIC_VCPU_H__ */
/*
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|