mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
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:
1 parent
980aa0a42f
commit
a5d39947cb
5 files changed
+121
-31
No files matched your search
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
|
||||
/*
|
||||
|
||||