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@citrix.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
This commit is contained in:
Keir Fraser committed 2009-10-19 11:58:36 +01:00
1 parent 980aa0a42f
commit a5d39947cb
5 files changed
+121 -31

No files matched your search

+19
View File
@@ -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;
+68 -31
View File
@@ -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();
}
/* 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 inline void version_update_end(u32 *version)
{
wmb();
(*version)++;
}
void update_vcpu_system_time(struct vcpu *v)
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 *v)
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);
_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 if ( u->tsc_timestamp != t->local_tsc_stamp )
else
{
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->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);
}
+4
View File
@@ -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. */
+2
View File
@@ -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__ */
+28
View File
@@ -202,6 +202,34 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_get_physid_t);
#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__ */
/*
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.