pvclock vsyscall support + KVM hypervisor support (v2)
Subsystems (11)
cover pvclock vsyscall support + KVM hypervisor support (v2)
This patchset, based on earlier work by Jeremy Fitzhardinge, implements paravirtual clock vsyscall support.
It should be possible to implement Xen support relatively easily.
It reduces clock_gettime from 500 cycles to 200 cycles on my testbox.
Please review.
Quoted 1 line
From my POV, this is ready to merge.
v2:
- Do not allow visibility of different <system_timestamp, tsc_timestamp> tuples.
- Add option to disable vsyscall.
PATCH 1/18 KVM: x86: retain pvclock guest stopped bit in guest memory
Otherwise its possible for an unrelated KVM_REQ_UPDATE_CLOCK (such as due to CPU migration) to clear the bit.
Noticed by Paolo Bonzini.
arch/x86/kvm/x86.c +13 -7
@@ -1143,6 +1143,7 @@ static int kvm_guest_time_update(structunsigned long this_tsc_khz;s64 kernel_ns, max_kernel_ns;u64 tsc_timestamp; +struct pvclock_vcpu_time_info *guest_hv_clock;u8 pvclock_flags;/* Keep irq disabled to prevent changes to the clock */
@@ -1226,13 +1227,6 @@ static int kvm_guest_time_update(struct vcpu->last_kernel_ns = kernel_ns; vcpu->last_guest_tsc = tsc_timestamp; - pvclock_flags =0 ; -if (vcpu->pvclock_set_guest_stopped_request) { - pvclock_flags |=PVCLOCK_GUEST_STOPPED ; - vcpu->pvclock_set_guest_stopped_request = false; - } - - vcpu->hv_clock.flags = pvclock_flags;/* * The interface expects us to write an even number signaling that the
@@ -1243,6 +1237,18 @@ static int kvm_guest_time_update(struct shared_kaddr =kmap_atomic (vcpu->time_page); + guest_hv_clock = shared_kaddr + vcpu->time_offset; + +/* retain PVCLOCK_GUEST_STOPPED if set in guest copy */ + pvclock_flags = (guest_hv_clock->flags &PVCLOCK_GUEST_STOPPED ); + +if (vcpu->pvclock_set_guest_stopped_request) { + pvclock_flags |=PVCLOCK_GUEST_STOPPED ; + vcpu->pvclock_set_guest_stopped_request = false; + } + + vcpu->hv_clock.flags = pvclock_flags; +memcpy (shared_kaddr + vcpu->time_offset, &vcpu->hv_clock,sizeof (vcpu->hv_clock));
PATCH 2/18 x86: pvclock: make sure rdtsc doesnt speculate out of region
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.
arch/x86/kernel/pvclock.c +2 -2
@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(structdo { version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
PATCH 3/18 x86: pvclock: remove pvclock_shadow_time
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
We can copy the information directly from "struct pvclock_vcpu_time_info", remove pvclock_shadow_time.
arch/x86/kernel/pvclock.c +9 -46
@@ -19,21 +19,6 @@#include <linux/percpu.h> #include <asm/pvclock.h> -/* -* These are perodically updated -* xen: magic shared_info page -* kvm: gpa registered via msr -* and then copied here. -*/ -struct pvclock_shadow_time { -u64 tsc_timestamp;/* TSC at last update of time vals. */ -u64 system_timestamp;/* Time, in nanosecs, since boot. */ -u32 tsc_to_nsec_mul; -int tsc_shift; -u32 version; -u8 flags; -}; -static u8 valid_flags__read_mostly =0 ;void pvclock_set_flags (u8 flags)
@@ -41,32 +26,11 @@ void pvclock_set_flags(u8 flags) valid_flags = flags; } -static u64 pvclock_get_nsec_offset (struct pvclock_shadow_time *shadow) +static u64 pvclock_get_nsec_offset (const struct pvclock_vcpu_time_info *src) { -u64 delta =native_read_tsc () - shadow->tsc_timestamp; -return pvclock_scale_delta (delta, shadow->tsc_to_nsec_mul, - shadow->tsc_shift); -} - -/* -* Reads a consistent set of time-base values from hypervisor, -* into a shadow data area. -*/ -static unsigned pvclock_get_time_values (struct pvclock_shadow_time *dst, -struct pvclock_vcpu_time_info *src) -{ -do { - dst->version = src->version; -rmb ();/* fetch version before data */ - dst->tsc_timestamp = src->tsc_timestamp; - dst->system_timestamp = src->system_time; - dst->tsc_to_nsec_mul = src->tsc_to_system_mul; - dst->tsc_shift = src->tsc_shift; - dst->flags = src->flags; -rmb ();/* test version after fetching data */ - }while ((src->version &1 ) || (dst->version != src->version)); - -return dst->version; +u64 delta =native_read_tsc () - src->tsc_timestamp; +return pvclock_scale_delta (delta, src->tsc_to_system_mul, + src->tsc_shift); }unsigned long pvclock_tsc_khz (struct pvclock_vcpu_time_info *src)
@@ -90,21 +54,20 @@ void pvclock_resume(void)cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) { -struct pvclock_shadow_time shadow;unsigned version;cycle_t ret, offset;u64 last;do { - version =pvclock_get_time_values (&shadow, src); + version = src->version;rdtsc_barrier (); - offset =pvclock_get_nsec_offset (&shadow); - ret = shadow.system_timestamp + offset; + offset =pvclock_get_nsec_offset (src); + ret = src->system_time + offset;rdtsc_barrier (); - }while (version != src->version); + }while ((src->version &1 ) || version != src->version);if ((valid_flags &PVCLOCK_TSC_STABLE_BIT ) && - (shadow.flags &PVCLOCK_TSC_STABLE_BIT )) + (src->flags &PVCLOCK_TSC_STABLE_BIT ))return ret;/*
Re: [patch 03/18] x86: pvclock: remove pvclock_shadow_time
Quoted 25 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
We can copy the information directly from "struct
pvclock_vcpu_time_info", removepvclock_shadow_time.Signed-off-by Marcelo Tosatti <[email protected]>unsigned long
pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)arch/x86/kernel/pvclock.c +2
-4@@ -90,21 +54,20 @@ void pvclock_resume(void)cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) { -struct pvclock_shadow_time shadow;unsigned version;cycle_t ret, offset;u64 last; do { - version = pvclock_get_time_values(&shadow, src); + version = src->version; rdtsc_barrier(); - offset = pvclock_get_nsec_offset(&shadow); - ret = shadow.system_timestamp + offset; + offset = pvclock_get_nsec_offset(src);
arch/x86/kernel/pvclock.c void pvclock_resume(void) · line 72
Quoted 10 lines
arch/x86/kernel/pvclock.c +3
-2+ ret = src->system_time + offset;rdtsc_barrier (); - }while (version != src->version); + }while ((src->version &1 ) || version != src->version);if ((valid_flags &PVCLOCK_TSC_STABLE_BIT ) && - (shadow.flags &PVCLOCK_TSC_STABLE_BIT )) + (src->flags &PVCLOCK_TSC_STABLE_BIT ))return ret;
You're now reading PVCLOCK_TSC_STABLE outside the critical section. We could have live migrated to a tsc-unstable host and had this flag cleared.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 03/18] x86: pvclock: remove pvclock_shadow_time
Quoted 31 lines · Avi Kivity
Quoted 30 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
We can copy the information directly from "struct
pvclock_vcpu_time_info", removepvclock_shadow_time.Signed-off-by Marcelo Tosatti <[email protected]>unsigned long
pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)arch/x86/kernel/pvclock.c +4
-5@@ -90,21 +54,20 @@ void pvclock_resume(void)cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) { -struct pvclock_shadow_time shadow;unsigned version;cycle_t ret, offset;u64 last;do { - version =pvclock_get_time_values (&shadow, src); + version = src->version;rdtsc_barrier (); - offset =pvclock_get_nsec_offset (&shadow); - ret = shadow.system_timestamp + offset; + offset =pvclock_get_nsec_offset (src); + ret = src->system_time + offset;rdtsc_barrier (); - }while (version != src->version); + }while ((src->version &1 ) || version != src->version);
Quoted 9 lines
arch/x86/kernel/pvclock.c void pvclock_resume(void) · line 72
Quoted 5 lines
arch/x86/kernel/pvclock.c +1
-1if ((valid_flags &PVCLOCK_TSC_STABLE_BIT ) && - (shadow.flags &PVCLOCK_TSC_STABLE_BIT )) + (src->flags &PVCLOCK_TSC_STABLE_BIT ))return ret;You're now reading
PVCLOCK_TSC_STABLEoutside the critical section. We could have live migrated to a tsc-unstable host and had this flag cleared.
I see it's fixed in patch 5. So there's a window of two patches where this is broken.
Signature · 1 line
error compiling committee.c: too many arguments to function
PATCH 4/18 x86: pvclock: create helper for pvclock data retrieval
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
So code can be reused.
arch/x86/kernel/pvclock.c +2 -13
@@ -26,13 +26,6 @@ void pvclock_set_flags(u8 flags) valid_flags = flags; } -static u64 pvclock_get_nsec_offset (const struct pvclock_vcpu_time_info *src) -{ -u64 delta =native_read_tsc () - src->tsc_timestamp; -return pvclock_scale_delta (delta, src->tsc_to_system_mul, - src->tsc_shift); -} -unsigned long pvclock_tsc_khz (struct pvclock_vcpu_time_info *src) {u64 pv_tsc_khz =1000000ULL <<32 ;
@@ -55,15 +48,11 @@ void pvclock_resume(void)cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) {unsigned version; -cycle_t ret, offset; +cycle_t ret;u64 last;do { - version = src->version; -rdtsc_barrier (); - offset =pvclock_get_nsec_offset (src); - ret = src->system_time + offset; -rdtsc_barrier (); + version =__pvclock_read_cycles (src, &ret); }while ((src->version &1 ) || version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
arch/x86/include/asm/pvclock.h +25 -0
@@ -56,4 +56,29 @@ static inline u64 pvclock_scale_delta(u6return product; } +static __always_inline +u64 pvclock_get_nsec_offset(const struct pvclock_vcpu_time_info *src) +{ +u64 delta =__native_read_tsc () - src->tsc_timestamp; +return pvclock_scale_delta (delta, src->tsc_to_system_mul, + src->tsc_shift); +} + +static __always_inline +unsigned __pvclock_read_cycles (const struct pvclock_vcpu_time_info *src, +cycle_t *cycles) +{ +unsigned version; +cycle_t ret, offset; + + version = src->version; +rdtsc_barrier (); + offset =pvclock_get_nsec_offset (src); + ret = src->system_time + offset; +rdtsc_barrier (); + + *cycles = ret; +return version; +} +#endif /* _ASM_X86_PVCLOCK_H */
PATCH 5/18 x86: pvclock: fix flags usage race
In reply to Marcelo TosattiValidity of values returned by pvclock (including flags) is guaranteed by version checks.
That is, read of src->flags outside version check protection can refer to a different paravirt clock update by the hypervisor.
arch/x86/include/asm/pvclock.h +4 -1
@@ -66,18 +66,21 @@ u64 pvclock_get_nsec_offset(const structstatic __always_inline unsigned __pvclock_read_cycles (const struct pvclock_vcpu_time_info *src, -cycle_t *cycles) +cycle_t *cycles,u8 *flags) {unsigned version;cycle_t ret, offset; +u8 ret_flags; version = src->version;rdtsc_barrier (); offset =pvclock_get_nsec_offset (src); ret = src->system_time + offset; + ret_flags = src->flags;rdtsc_barrier (); *cycles = ret; + *flags = ret_flags;return version; }
arch/x86/kernel/pvclock.c +3 -2
@@ -50,13 +50,14 @@ cycle_t pvclock_clocksource_read(structunsigned version;cycle_t ret;u64 last; +u8 flags;do { - version =__pvclock_read_cycles (src, &ret); + version =__pvclock_read_cycles (src, &ret, &flags); }while ((src->version &1 ) || version != src->version);if ((valid_flags &PVCLOCK_TSC_STABLE_BIT ) && - (src->flags &PVCLOCK_TSC_STABLE_BIT )) + (flags &PVCLOCK_TSC_STABLE_BIT ))return ret;/*
PATCH 6/18 x86: pvclock: introduce helper to read flags
In reply to Marcelo Tosattiarch/x86/kernel/pvclock.c +13 -0
@@ -45,6 +45,19 @@ void pvclock_resume(void)atomic64_set (&last_value,0 ); } +u8 pvclock_read_flags (struct pvclock_vcpu_time_info *src) +{ +unsigned version; +cycle_t ret; +u8 flags; + +do { + version =__pvclock_read_cycles (src, &ret, &flags); + }while ((src->version &1 ) || version != src->version); + +return flags & valid_flags; +} +cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) {unsigned version;
arch/x86/include/asm/pvclock.h +1 -0
@@ -6,6 +6,7 @@/* some helper functions for xen and kvm pv clock sources */ cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src); +u8 pvclock_read_flags (struct pvclock_vcpu_time_info *src);void pvclock_set_flags (u8 flags);unsigned long pvclock_tsc_khz (struct pvclock_vcpu_time_info *src); void pvclock_read_wallclock(struct pvclock_wall_clock *wall,
PATCH 7/18 sched: add notifier for cross-cpu migrations
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
include/linux/sched.h +8 -0
@@ -107,6 +107,14 @@ extern unsigned long this_cpu_load(void)extern void calc_global_load (unsigned long ticks);extern void update_cpu_load_nohz (void ); +/* Notifier for when a task gets migrated to a new CPU */ +struct task_migration_notifier { +struct task_struct *task; +int from_cpu; +int to_cpu; +}; +extern void register_task_migration_notifier (struct notifier_block *n); +extern unsigned long get_parent_ip (unsigned long addr);struct seq_file ;
kernel/sched/core.c +15 -0
@@ -922,6 +922,13 @@ void check_preempt_curr(struct rq *rq, s rq->skip_clock_update =1 ; } +static ATOMIC_NOTIFIER_HEAD (task_migration_notifier); + +void register_task_migration_notifier (struct notifier_block *n) +{ +atomic_notifier_chain_register (&task_migration_notifier, n); +} +#ifdef CONFIG_SMP void set_task_cpu (struct task_struct *p,unsigned int new_cpu) {
@@ -952,8 +959,16 @@ void set_task_cpu(struct task_struct *p,trace_sched_migrate_task (p, new_cpu);if (task_cpu (p) != new_cpu) { +struct task_migration_notifier tmn; + p->se.nr_migrations++;perf_sw_event (PERF_COUNT_SW_CPU_MIGRATIONS ,1 ,NULL ,0 ); + + tmn.task = p; + tmn.from_cpu =task_cpu (p); + tmn.to_cpu = new_cpu; + +atomic_notifier_call_chain (&task_migration_notifier,0 , &tmn); }__set_task_cpu (p, new_cpu);
PATCH 8/18 x86: pvclock: generic pvclock vsyscall initialization
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
Introduce generic, non hypervisor specific, pvclock initialization routines.
arch/x86/kernel/pvclock.c +71 -0
@@ -17,6 +17,10 @@#include <linux/kernel.h> #include <linux/percpu.h> +#include <linux/notifier.h> +#include <linux/sched.h> +#include <linux/gfp.h> +#include <linux/bootmem.h> #include <asm/pvclock.h> static u8 valid_flags__read_mostly =0 ;
@@ -122,3 +126,70 @@ void pvclock_read_wallclock(struct pvcloset_normalized_timespec (ts, now.tv_sec, now.tv_nsec); } + +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL + +static aligned_pvti_t *pvclock_vdso_info; + +static struct pvclock_vsyscall_time_info *pvclock_get_vsyscall_user_time_info (int cpu) +{ +if (pvclock_vdso_info ==NULL ) { +BUG (); +return NULL ; + } + +return &pvclock_vdso_info[cpu].info; +} + +struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info (int cpu) +{ +return &pvclock_get_vsyscall_user_time_info (cpu)->pvti; +} + +int pvclock_task_migrate (struct notifier_block *nb,unsigned long l,void *v) +{ +struct task_migration_notifier *mn = v; +struct pvclock_vsyscall_time_info *pvti; + + pvti =pvclock_get_vsyscall_user_time_info (mn->from_cpu); + +if (pvti ==NULL ) +return NOTIFY_DONE ; + + pvti->migrate_count++; + +return NOTIFY_DONE ; +} + +static struct notifier_block pvclock_migrate = { + .notifier_call = pvclock_task_migrate, +}; + +/* +* Initialize the generic pvclock vsyscall state. This will allocate +* a/some page(s) for the per-vcpu pvclock information, set up a +* fixmap mapping for the page(s) +*/ +int __init pvclock_init_vsyscall (void ) +{ +int idx; +unsigned int size =PVCLOCK_VSYSCALL_NR_PAGES *PAGE_SIZE ; + + pvclock_vdso_info =__alloc_bootmem (size,PAGE_SIZE ,0 ); +if (!pvclock_vdso_info) +return -ENOMEM ; + +memset (pvclock_vdso_info,0 , size); + +for (idx =0 ; idx <= (PVCLOCK_FIXMAP_END -PVCLOCK_FIXMAP_BEGIN ); idx++) { +__set_fixmap (PVCLOCK_FIXMAP_BEGIN + idx, +__pa_symbol (pvclock_vdso_info) + (idx*PAGE_SIZE ), +PAGE_KERNEL_VVAR ); + } + +register_task_migration_notifier (&pvclock_migrate); + +return 0 ; +} + +#endif /* CONFIG_PARAVIRT_CLOCK_VSYSCALL */
arch/x86/include/asm/fixmap.h +5 -0
@@ -19,6 +19,7 @@#include <asm/acpi.h> #include <asm/apicdef.h> #include <asm/page.h> +#include <asm/pvclock.h> #ifdef CONFIG_X86_32 #include <linux/threads.h> #include <asm/kmap_types.h>
@@ -81,6 +82,10 @@ enum fixed_addresses { VVAR_PAGE, VSYSCALL_HPET,#endif +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL +PVCLOCK_FIXMAP_BEGIN , +PVCLOCK_FIXMAP_END =PVCLOCK_FIXMAP_BEGIN +PVCLOCK_VSYSCALL_NR_PAGES -1 , +#endifFIX_DBGP_BASE ,FIX_EARLYCON_MEM_BASE , #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
arch/x86/include/asm/pvclock.h +22 -0
@@ -13,6 +13,8 @@ void pvclock_read_wallclock(struct pvclostruct pvclock_vcpu_time_info *vcpu, struct timespec *ts);void pvclock_resume (void ); +int __init pvclock_init_vsyscall (void ); +struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info (int cpu);/* * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
@@ -85,4 +87,24 @@ unsigned __pvclock_read_cycles(const str return version; } +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL + +struct pvclock_vsyscall_time_info { +struct pvclock_vcpu_time_info pvti; +u32 migrate_count; +}; + +typedef union { +struct pvclock_vsyscall_time_info info; +char pad[SMP_CACHE_BYTES ]; +}aligned_pvti_t ____cacheline_aligned ; + +#define PVTI_SIZE sizeof(aligned_pvti_t) +#if NR_CPUS ==1 +#define PVCLOCK_VSYSCALL_NR_PAGES 1 +#else +#define PVCLOCK_VSYSCALL_NR_PAGES ((NR_CPUS-1)/(PAGE_SIZE/PVTI_SIZE))+1 +#endif +#endif /* CONFIG_PARAVIRT_CLOCK_VSYSCALL */ +#endif /* _ASM_X86_PVCLOCK_H */
arch/x86/include/asm/clocksource.h +1 -0
@@ -8,6 +8,7 @@#define VCLOCK_NONE 0/* No vDSO clock available. */ #define VCLOCK_TSC 1/* vDSO should use vread_tsc. */ #define VCLOCK_HPET 2/* vDSO should use vread_hpet. */ +#define VCLOCK_PVCLOCK 3/* vDSO should use vread_pvclock. */ struct arch_clocksource_data {int vclock_mode;
arch/x86/Kconfig +7 -0
@@ -632,6 +632,13 @@ config PARAVIRT_SPINLOCKS config PARAVIRT_CLOCK bool +config PARAVIRT_CLOCK_VSYSCALL + bool "Paravirt clock vsyscall support" + depends on PARAVIRT_CLOCK && GENERIC_TIME_VSYSCALL + ---help--- + Enable performance critical clock related system calls to + be executed in userspace, provided that the hypervisor + supports it. endif
Re: [patch 08/18] x86: pvclock: generic pvclock vsyscall initialization
Quoted 7 lines · Marcelo Tosatti
Index: vsyscall/arch/x86/Kconfig
===================================================================arch/x86/Kconfig +0
-0@@ -632,6 +632,13 @@ config PARAVIRT_SPINLOCKS config PARAVIRT_CLOCK
arch/x86/Kconfig config PARAVIRT_SPINLOCKS · line 643
Quoted 10 lines
arch/x86/Kconfig +7
-0bool +config PARAVIRT_CLOCK_VSYSCALL + bool "Paravirt clock vsyscall support" + depends on PARAVIRT_CLOCK && GENERIC_TIME_VSYSCALL + ---help--- + Enable performance critical clock related system calls to + be executed in userspace, provided that the hypervisor + supports it. endif
Besides debugging, what is the point in having this as an extra-selectable? Is there any case in which a virtual machine has code for this, but may decide to run without it ?
I believe all this code in vsyscall should be wrapped in PARAVIRT_CLOCK only.
Re: [patch 08/18] x86: pvclock: generic pvclock vsyscall initialization
Quoted 17 lines · Glauber Costa
Quoted 16 lines · Marcelo Tosatti
Index: vsyscall/arch/x86/Kconfig
===================================================================arch/x86/Kconfig +7
-0@@ -632,6 +632,13 @@ config PARAVIRT_SPINLOCKS config PARAVIRT_CLOCK bool +config PARAVIRT_CLOCK_VSYSCALL + bool "Paravirt clock vsyscall support" + depends on PARAVIRT_CLOCK && GENERIC_TIME_VSYSCALL + ---help--- + Enable performance critical clock related system calls to + be executed in userspace, provided that the hypervisor + supports it.
Quoted 5 lines
arch/x86/Kconfig config PARAVIRT_SPINLOCKS · line 643
Quoted 1 line
arch/x86/Kconfig +0
-0endifBesides debugging, what is the point in having this as an extra-selectable? Is there any case in which a virtual machine has code for this, but may decide to run without it ?
Don't think so (its pretty small anyway, the code).
Quoted 2 lines
I believe all this code in vsyscall should be wrapped in
PARAVIRT_CLOCKonly.
Unless Jeremy has a reason, i'm fine with that.
Re: [patch 08/18] x86: pvclock: generic pvclock vsyscall initialization
Quoted 22 lines · Marcelo Tosatti
Quoted 17 lines · Glauber Costa
Quoted 16 lines · Marcelo Tosatti
Index: vsyscall/arch/x86/Kconfig
===================================================================arch/x86/Kconfig +7
-0@@ -632,6 +632,13 @@ config PARAVIRT_SPINLOCKS config PARAVIRT_CLOCK bool +config PARAVIRT_CLOCK_VSYSCALL + bool "Paravirt clock vsyscall support" + depends on PARAVIRT_CLOCK && GENERIC_TIME_VSYSCALL + ---help--- + Enable performance critical clock related system calls to + be executed in userspace, provided that the hypervisor + supports it.Quoted 4 lines
arch/x86/Kconfig config PARAVIRT_SPINLOCKS · line 643
Quoted 1 line
arch/x86/Kconfig +0
-0endifBesides debugging, what is the point in having this as an extra-selectable? Is there any case in which a virtual machine has code for this, but may decide to run without it ?
Quoted 5 lines
Don't think so (its pretty small anyway, the code).
Quoted 2 lines
I believe all this code in vsyscall should be wrapped in
PARAVIRT_CLOCKonly.Unless Jeremy has a reason, i'm fine with that.
I often set up blind config variables for dependency management; I'm guessing the "GENERIC_TIME_VSYSCALL" dependency is important. I think the problem is that this exists, but that it's a user-selectable option. Removing the prompt should fix that.
J
Re: [patch 08/18] x86: pvclock: generic pvclock vsyscall initialization
In reply to Marcelo TosattiQuoted 6 lines · Marcelo Tosatti
+ */
arch/x86/kernel/pvclock.c +5
-0+int __init pvclock_init_vsyscall (void ) +{ +int idx; +unsigned int size =PVCLOCK_VSYSCALL_NR_PAGES *PAGE_SIZE ; +
arch/x86/kernel/pvclock.c void pvclock_read_wallclock(struct pvclo · line 187
Quoted 10 lines
arch/x86/kernel/pvclock.c +10
-0+ pvclock_vdso_info =__alloc_bootmem (size,PAGE_SIZE ,0 ); +if (!pvclock_vdso_info) +return -ENOMEM ; + +memset (pvclock_vdso_info,0 , size); + +for (idx =0 ; idx <= (PVCLOCK_FIXMAP_END -PVCLOCK_FIXMAP_BEGIN ); idx++) { +__set_fixmap (PVCLOCK_FIXMAP_BEGIN + idx, +__pa_symbol (pvclock_vdso_info) + (idx*PAGE_SIZE ), +PAGE_KERNEL_VVAR );
BTW, Previous line is whitespace damaged.
PATCH 9/18 KVM: x86: introduce facility to support vsyscall pvclock, via MSR
In reply to Marcelo TosattiAllow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by MSR_KVM_SYSTEM_TIME_NEW). 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.
arch/x86/include/asm/kvm_para.h +2 -0
@@ -23,6 +23,7 @@#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_USERSPACE_CLOCKSOURCE 7/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored.
@@ -39,6 +40,7 @@#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03#define MSR_KVM_PV_EOI_EN 0x4b564d04 +#define MSR_KVM_USERSPACE_TIME 0x4b564d05struct kvm_steal_time {__u64 steal;
Documentation/virtual/kvm/msr.txt +16 -0
@@ -125,6 +125,22 @@ MSR_KVM_SYSTEM_TIME_NEW: 0x4b564d01 Availability of this MSR must be checked via bit 3 in 0x4000001 cpuid leaf prior to usage. +MSR_KVM_USERSPACE_TIME: 0x4b564d05 + +Allow a guest to register a second location for the VCPU time info +structure for each vcpu (as described by MSR_KVM_SYSTEM_TIME_NEW). +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. + +Relationship with master copy (MSR_KVM_SYSTEM_TIME_NEW): + +- This MSR must be enabled only when the master is enabled. +- Disabling updates to the master automatically disables +updates for this copy. + +Availability of this MSR must be checked via bit 7 in 0x4000001 cpuid +leaf prior to usage. MSR_KVM_WALL_CLOCK: 0x11
arch/x86/include/asm/kvm_host.h +3 -0
@@ -415,10 +415,13 @@ struct kvm_vcpu_arch {int (*complete_userspace_io)(struct kvm_vcpu *vcpu);gpa_t time; +gpa_t uspace_time;struct pvclock_vcpu_time_info hv_clock;unsigned int hw_tsc_khz;unsigned int time_offset; +unsigned int uspace_time_offset;struct page *time_page; +struct page *uspace_time_page;/* set guest stopped flag in pvclock flags field */ bool pvclock_set_guest_stopped_request;
arch/x86/kvm/x86.c +81 -26
182 lines not on this page · raw message
arch/x86/kvm/cpuid.c +3 -1
@@ -411,7 +411,9 @@ static int do_cpuid_ent(struct kvm_cpuid (1 <<KVM_FEATURE_CLOCKSOURCE2 ) | (1 <<KVM_FEATURE_ASYNC_PF ) | (1 <<KVM_FEATURE_PV_EOI ) | - (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ); + (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ) | + (1 <<KVM_FEATURE_USERSPACE_CLOCKSOURCE ); +if (sched_info_on ()) entry->eax |= (1 <<KVM_FEATURE_STEAL_TIME );
include/uapi/linux/kvm.h +1 -0
@@ -626,6 +626,7 @@ struct kvm_ppc_smmu_info {#define KVM_CAP_READONLY_MEM 81#endif #define KVM_CAP_IRQFD_RESAMPLE 82 +#define KVM_CAP_USERSPACE_CLOCKSOURCE 83#ifdef KVM_CAP_IRQ_ROUTING
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 8 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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 Marcelo Tosatti <[email protected]>
Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.
Quoted 4 lines
Quoted 1 line
Signed-off-by Marcelo Tosatti <[email protected]>Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
In Xen the pvclock structure is embedded within a pile of other stuff that shouldn't be mapped into guest memory, so providing for a second location allows it to be placed whereever is convenient for the guest. That's a restriction of the Xen ABI, but I don't know if it affects KVM.
J
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 14 lines · Jeremy Fitzhardinge
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 4 lines
Quoted 1 line
Signed-off-by Marcelo Tosatti <[email protected]>Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
Quoted 6 lines
In Xen the pvclock structure is embedded within a pile of other stuff that shouldn't be mapped into guest memory, so providing for a second location allows it to be placed whereever is convenient for the guest. That's a restriction of the Xen ABI, but I don't know if it affects KVM.
J
It is possible to share the data for KVM in theory, but:
- It is a small amount of memory.
- It requires aligning to page size (the in-kernel percpu array is currently cacheline aligned).
- It is possible to modify flags separately for userspace/kernelspace, if desired.
This justifies the duplication IMO (code is simple and clean).
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 24 lines · Marcelo Tosatti
Quoted 14 lines · Jeremy Fitzhardinge
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 4 lines
Quoted 1 line
Signed-off-by Marcelo Tosatti <[email protected]>Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
Quoted 6 lines
In Xen the pvclock structure is embedded within a pile of other stuff that shouldn't be mapped into guest memory, so providing for a second location allows it to be placed whereever is convenient for the guest. That's a restriction of the Xen ABI, but I don't know if it affects KVM.
J
It is possible to share the data for KVM in theory, but:
Quoted 8 lines
- It is a small amount of memory.
- It requires aligning to page size (the in-kernel percpu array is currently cacheline aligned).
- It is possible to modify flags separately for userspace/kernelspace, if desired.This justifies the duplication IMO (code is simple and clean).
Duplicating is indeed no the end of the world. But one note:
* If it is page-size aligned, it is automatically cacheline aligned. Since we have to export the user page *anyway*, this is a non-issue.
That said, duplicating instead of integrating, which is technically possible, is a design decision, and it needs to be documented somewhere aside from this mail thread.
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
In reply to Marcelo TosattiQuoted 24 lines · Marcelo Tosatti
Quoted 14 lines · Jeremy Fitzhardinge
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 4 lines
Quoted 1 line
Signed-off-by Marcelo Tosatti <[email protected]>Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
Quoted 6 lines
In Xen the pvclock structure is embedded within a pile of other stuff that shouldn't be mapped into guest memory, so providing for a second location allows it to be placed whereever is convenient for the guest. That's a restriction of the Xen ABI, but I don't know if it affects KVM.
J
It is possible to share the data for KVM in theory, but:
Quoted 8 lines
- It is a small amount of memory.
- It requires aligning to page size (the in-kernel percpu array is currently cacheline aligned).
- It is possible to modify flags separately for userspace/kernelspace, if desired.This justifies the duplication IMO (code is simple and clean).
What would be the changes required to remove the duplication? If it's just page alignment, then is seems even smaller. In addition we avoid expanding the ABI again.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 31 lines · Avi Kivity
Quoted 30 lines · Marcelo Tosatti
Quoted 14 lines · Jeremy Fitzhardinge
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 4 lines
Quoted 1 line
Signed-off-by Marcelo Tosatti <[email protected]>Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
Quoted 6 lines
In Xen the pvclock structure is embedded within a pile of other stuff that shouldn't be mapped into guest memory, so providing for a second location allows it to be placed whereever is convenient for the guest. That's a restriction of the Xen ABI, but I don't know if it affects KVM.
J
It is possible to share the data for KVM in theory, but:
- It is a small amount of memory.
- It requires aligning to page size (the in-kernel percpu array is currently cacheline aligned).
- It is possible to modify flags separately for userspace/kernelspace, if desired.
Quoted 6 lines
Quoted 2 lines
This justifies the duplication IMO (code is simple and clean).
What would be the changes required to remove the duplication? If it's just page alignment, then is seems even smaller. In addition we avoid expanding the ABI again.
This would require changing the kernel copy from percpu data, which there is no guarantee is linear (necessary for fixmap mapping), to dynamically allocated (which in turn can be tricky due to early boot clock requirement).
Hum, no thanks.
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 39 lines · Marcelo Tosatti
Quoted 31 lines · Avi Kivity
Quoted 30 lines · Marcelo Tosatti
Quoted 14 lines · Jeremy Fitzhardinge
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 4 lines
Quoted 1 line
Signed-off-by Marcelo Tosatti <[email protected]>Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
Quoted 6 lines
In Xen the pvclock structure is embedded within a pile of other stuff that shouldn't be mapped into guest memory, so providing for a second location allows it to be placed whereever is convenient for the guest. That's a restriction of the Xen ABI, but I don't know if it affects KVM.
J
It is possible to share the data for KVM in theory, but:
- It is a small amount of memory.
- It requires aligning to page size (the in-kernel percpu array is currently cacheline aligned).
- It is possible to modify flags separately for userspace/kernelspace, if desired.Quoted 6 lines
Quoted 2 lines
This justifies the duplication IMO (code is simple and clean).
What would be the changes required to remove the duplication? If it's just page alignment, then is seems even smaller. In addition we avoid expanding the ABI again.
Quoted 7 lines
This would require changing the kernel copy from percpu data, which there is no guarantee is linear (necessary for fixmap mapping), to dynamically allocated (which in turn can be tricky due to early boot clock requirement).
Hum, no thanks.
You allocate it using bootmemory for vsyscall anyway. If they are strictly in the same physical location, you are not allocating anything extra.
Re: [patch 09/18] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
In reply to Jeremy FitzhardingeQuoted 14 lines · Jeremy Fitzhardinge
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 4 lines
Quoted 1 line
Signed-off-by Marcelo Tosatti <[email protected]>Can you please be a bit more specific about why we need this? Why does the host need to provide us with two pages with the exact same data? Why can't just do it with mapping tricks in the guest?
Quoted 7 lines
In Xen the pvclock structure is embedded within a pile of other stuff that shouldn't be mapped into guest memory, so providing for a second location allows it to be placed whereever is convenient for the guest. That's a restriction of the Xen ABI, but I don't know if it affects KVM.
J
In kvm the exported data seems to be exactly the same. So it makes sense to have a single page exported to the guest. The guest may have a facility that maps the vsyscall clock and the normal clock to a specific location given a page - that can be either a different page or the same page.
Any reason why it wouldn't work?
PATCH 10/18 x86: kvm guest: pvclock vsyscall support
In reply to Marcelo TosattiAllow hypervisor to update userspace visible copy of pvclock data.
arch/x86/kernel/kvmclock.c +57 -0
@@ -31,6 +31,9 @@ static int kvmclock = 1;static int msr_kvm_system_time =MSR_KVM_SYSTEM_TIME ;static int msr_kvm_wall_clock =MSR_KVM_WALL_CLOCK ; +/* set when the generic vsyscall pvclock elements are setup */ +bool vsyscall_clock_initializable = false; +static int parse_no_kvmclock (char *arg) { kvmclock =0 ;
@@ -151,6 +154,28 @@ int kvm_register_clock(char *txt)return ret; } +static int kvm_register_vsyscall_clock (char *txt) +{ +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL +int cpu =smp_processor_id (); +int low, high, ret; +struct pvclock_vcpu_time_info *info; + + info =pvclock_get_vsyscall_time_info (cpu); + + low = (int )__pa (info) |1 ; + high = ((u64 )__pa (&per_cpu (hv_clock, cpu)) >>32 ); + ret =native_write_msr_safe (MSR_KVM_USERSPACE_TIME , low, high); +printk (KERN_INFO "kvm-clock: cpu %d, msr %x:%x, %s\n" , + cpu, high, low, txt); + +return ret; +#else +return 0 ; +#endif +} + +static void kvm_save_sched_clock_state (void ) { }
@@ -158,6 +183,8 @@ static void kvm_save_sched_clock_state(vstatic void kvm_restore_sched_clock_state (void ) {kvm_register_clock ("primary cpu clock, resume" ); +if (vsyscall_clock_initializable) +kvm_register_vsyscall_clock ("primary cpu vsyscall clock, resume" ); }#ifdef CONFIG_X86_LOCAL_APIC
@@ -168,6 +195,8 @@ static void __cpuinit kvm_setup_secondar* we shouldn't fail. */ WARN_ON (kvm_register_clock ("secondary cpu clock" )); +if (vsyscall_clock_initializable) + kvm_register_vsyscall_clock("secondary cpu vsyscall clock"); }#endif
@@ -182,6 +211,8 @@ static void __cpuinit kvm_setup_secondar#ifdef CONFIG_KEXEC static void kvm_crash_shutdown (struct pt_regs *regs) { +if (vsyscall_clock_initializable) +native_write_msr (MSR_KVM_USERSPACE_TIME ,0 ,0 );native_write_msr (msr_kvm_system_time,0 ,0 );kvm_disable_steal_time ();native_machine_crash_shutdown (regs);
@@ -190,6 +221,8 @@ static void kvm_crash_shutdown(struct ptstatic void kvm_shutdown (void ) { +if (vsyscall_clock_initializable) +native_write_msr (MSR_KVM_USERSPACE_TIME ,0 ,0 );native_write_msr (msr_kvm_system_time,0 ,0 );kvm_disable_steal_time ();native_machine_shutdown ();
@@ -233,3 +266,27 @@ void __init kvmclock_init(void)if (kvm_para_has_feature (KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ))pvclock_set_flags (PVCLOCK_TSC_STABLE_BIT ); } + +int kvm_setup_vsyscall_timeinfo (void ) +{ +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL +int ret; +struct pvclock_vcpu_time_info *vcpu_time; +u8 flags; + + vcpu_time = &get_cpu_var (hv_clock); + flags =pvclock_read_flags (vcpu_time); +put_cpu_var (hv_clock); + +if (!(flags &PVCLOCK_TSC_STABLE_BIT )) +return 1 ; + +if ((ret =pvclock_init_vsyscall ())) +return ret; + + kvm_clock.archdata.vclock_mode =VCLOCK_PVCLOCK ; + vsyscall_clock_initializable = true; +#endif /* CONFIG_PARAVIRT_CLOCK_VSYSCALL */ +return 0 ; +} +
arch/x86/kernel/kvm.c +14 -0
@@ -42,6 +42,7 @@#include <asm/apic.h> #include <asm/apicdef.h> #include <asm/hypervisor.h> +#include <asm/kvm_guest.h> static int kvmapf =1 ;
@@ -62,6 +63,15 @@ static int parse_no_stealacc(char *arg)early_param ("no-steal-acc" , parse_no_stealacc); +static int kvmclock_vsyscall =1 ; +static int parse_no_kvmclock_vsyscall (char *arg) +{ + kvmclock_vsyscall =0 ; +return 0 ; +} + +early_param ("no-kvmclock-vsyscall" , parse_no_kvmclock_vsyscall); + static DEFINE_PER_CPU(struct kvm_vcpu_pv_apf_data, apf_reason) __aligned(64); static DEFINE_PER_CPU(struct kvm_steal_time, steal_time) __aligned(64);static int has_steal_clock =0 ;
@@ -468,6 +478,10 @@ void __init kvm_guest_init(void)if (kvm_para_has_feature (KVM_FEATURE_PV_EOI ))apic_set_eoi_write (kvm_guest_apic_eoi_write); +if (kvm_para_has_feature (KVM_FEATURE_USERSPACE_CLOCKSOURCE ) + && kvmclock_vsyscall) +kvm_setup_vsyscall_timeinfo (); +#ifdef CONFIG_SMP smp_ops.smp_prepare_boot_cpu = kvm_smp_prepare_boot_cpu;register_cpu_notifier (&kvm_cpu_notifier);
arch/x86/include/asm/kvm_guest.h new file +8 -0
@@ -0,0 +1,8 @@ +#ifndef _ASM_X86_KVM_GUEST_H +#define _ASM_X86_KVM_GUEST_H + +extern bool vsyscall_clock_initializable; + +int kvm_setup_vsyscall_timeinfo (void ); + +#endif /* _ASM_X86_KVM_GUEST_H */
PATCH 11/18 x86: vsyscall: pass mode to gettime backend
In reply to Marcelo TosattiRequired by next patch.
arch/x86/vdso/vclock_gettime.c +3 -3
@@ -80,7 +80,7 @@ notrace static long vdso_fallback_gtod(s } -notrace static inline u64 vgetsns(void) +notrace static inline u64 vgetsns(int *mode) {long v;cycles_t cycles;
@@ -107,7 +107,7 @@ notrace static int __always_inline do_re mode = gtod->clock.vclock_mode; ts->tv_sec = gtod->wall_time_sec; ns = gtod->wall_time_snsec; - ns +=vgetsns (); + ns +=vgetsns (&mode); ns >>= gtod->clock.shift; }while (unlikely (read_seqcount_retry (>od->seq, seq)));
@@ -127,7 +127,7 @@ notrace static int do_monotonic(struct t mode = gtod->clock.vclock_mode; ts->tv_sec = gtod->monotonic_time_sec; ns = gtod->monotonic_time_snsec; - ns +=vgetsns (); + ns +=vgetsns (&mode); ns >>= gtod->clock.shift; }while (unlikely (read_seqcount_retry (>od->seq, seq)));timespec_add_ns (ts, ns);
Re: [patch 11/18] x86: vsyscall: pass mode to gettime backend
Quoted 3 lines · Marcelo Tosatti
Required by next patch.
Signed-off-by Marcelo Tosatti <[email protected]>
I don't see where.
Re: [patch 11/18] x86: vsyscall: pass mode to gettime backend
Quoted 5 lines · Glauber Costa
Quoted 3 lines · Marcelo Tosatti
Required by next patch.
Signed-off-by Marcelo Tosatti <[email protected]>I don't see where.
+ if (unlikely(!(flags & PVCLOCK_TSC_STABLE_BIT)))
+ *mode = VCLOCK_NONE;
Re: [patch 11/18] x86: vsyscall: pass mode to gettime backend
Review
Quoted 11 lines · Marcelo Tosatti
Quoted 5 lines · Glauber Costa
Quoted 3 lines · Marcelo Tosatti
Required by next patch.
Signed-off-by Marcelo Tosatti <[email protected]>I don't see where.
(quoted diff) +2
-0+ if (unlikely(!(flags & PVCLOCK_TSC_STABLE_BIT))) + *mode = VCLOCK_NONE;
I see. But I end up thinking that this is a case of oversimplification that gets things more complicated =)
If this would be folded in the next patch, this would be obvious.
PATCH 12/18 x86: vdso: pvclock gettime support
In reply to Marcelo TosattiImprove performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
arch/x86/vdso/vclock_gettime.c +66 -0
@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))
@@ -62,6 +63,69 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} + +static notrace cycle_t vread_pvclock(int *mode) +{ +const struct pvclock_vsyscall_time_info *pvti; +cycle_t ret; +u64 last; +u32 version; +u32 migrate_count; +u8 flags; +unsigned cpu, cpu1; + + +/* +* When looping to get a consistent (time-info, tsc) pair, we +* also need to deal with the possibility we can switch vcpus, +* so make sure we always re-fetch time-info for the current vcpu. +*/ +do { + cpu =__getcpu () &0xfff ; + pvti =get_pvti (cpu); + + migrate_count = pvti->migrate_count; + + version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); + +/* +* Test we're still on the cpu as well as the version. +* We could have been migrated just after the first +* vgetcpu but before fetching the version, so we +* wouldn't notice a version change. +*/ + cpu1 =__getcpu () &0xfff ; + }while (unlikely (cpu != cpu1 || + (pvti->pvti.version &1 ) || + pvti->pvti.version != version || + pvti->migrate_count != migrate_count)); + +if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + *mode =VCLOCK_NONE ; + + last =VVAR (vsyscall_gtod_data).clock.cycle_last; + +if (likely (ret >= last)) +return ret; + +return last; +} +#endif + notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) {long ret;
@@ -88,6 +152,8 @@ notrace static inline u64 vgetsns(int *m cycles =vread_tsc (); else if (gtod->clock.vclock_mode == VCLOCK_HPET) cycles =vread_hpet (); + else if (gtod->clock.vclock_mode == VCLOCK_PVCLOCK) + cycles =vread_pvclock (mode); else return 0; v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask;
arch/x86/include/asm/vsyscall.h +15 -0
@@ -33,6 +33,21 @@ extern void map_vsyscall(void);*/ extern bool emulate_vsyscall (struct pt_regs *regs,unsigned long address); +static inline unsigned int __getcpu (void ) +{ +unsigned int p; + +if (VVAR (vgetcpu_mode) ==VGETCPU_RDTSCP ) { +/* Load per CPU data from RDTSCP */ +native_read_tscp (&p); + }else { +/* Load per CPU data from GDT */ + asm("lsl %1,%0" :"=r" (p) :"r" (__PER_CPU_SEG)); + } + +return p; +} +#endif /* __KERNEL__ */ #endif /* _ASM_X86_VSYSCALL_H */
arch/x86/vdso/vgetcpu.c +2 -7
@@ -17,13 +17,8 @@ __vdso_getcpu(unsigned *cpu, unsigned *n {unsigned int p; -if (VVAR (vgetcpu_mode) ==VGETCPU_RDTSCP ) { -/* Load per CPU data from RDTSCP */ -native_read_tscp (&p); - }else { -/* Load per CPU data from GDT */ - asm("lsl %1,%0" :"=r" (p) :"r" (__PER_CPU_SEG)); - } + p =__getcpu (); +if (cpu) *cpu = p &0xfff ;if (node)
Re: [patch 12/18] x86: vdso: pvclock gettime support
Quoted 28 lines · Marcelo Tosatti
Improve performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/vdso/vclock_gettime.c
===================================================================arch/x86/vdso/vclock_gettime.c +6
-0@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))@@ -62,6 +63,69 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base;
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 10 lines
arch/x86/vdso/vclock_gettime.c +10
-0+int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} +
Unless I am missing something, if gcc decides to not inline get_pvti, this will break, right? I believe you need to mark that function with __always_inline.
Quoted 8 lines
arch/x86/vdso/vclock_gettime.c +8
-0+static notrace cycle_t vread_pvclock(int *mode) +{ +const struct pvclock_vsyscall_time_info *pvti; +cycle_t ret; +u64 last; +u32 version; +u32 migrate_count; +u8 flags;
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 98
Quoted 10 lines
arch/x86/vdso/vclock_gettime.c +10
-0+unsigned cpu, cpu1; + + +/* +* When looping to get a consistent (time-info, tsc) pair, we +* also need to deal with the possibility we can switch vcpus, +* so make sure we always re-fetch time-info for the current vcpu. +*/ + do { + cpu = __getcpu() & 0xfff;
Please wrap this 0xfff into something meaningful.
Quoted 16 lines
arch/x86/vdso/vclock_gettime.c +16
-0+ pvti =get_pvti (cpu); + + migrate_count = pvti->migrate_count; + + version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); + +/* +* Test we're still on the cpu as well as the version. +* We could have been migrated just after the first +* vgetcpu but before fetching the version, so we +* wouldn't notice a version change. +*/ + cpu1 =__getcpu () &0xfff ; + } while (unlikely(cpu != cpu1 || + (pvti->pvti.version & 1) || + pvti->pvti.version != version ||
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 124
Quoted 10 lines
arch/x86/vdso/vclock_gettime.c +10
-0+ pvti->migrate_count != migrate_count)); + +if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + *mode =VCLOCK_NONE ; + + last =VVAR (vsyscall_gtod_data).clock.cycle_last; + +if (likely (ret >= last)) +return ret; +
Please add a comment here referring to tsc.c, where an explanation of this test lives. This is quite non-obvious for the non initiated.
Re: [patch 12/18] x86: vdso: pvclock gettime support
Quoted 35 lines · Glauber Costa
Quoted 34 lines · Marcelo Tosatti
Improve performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/vdso/vclock_gettime.c
===================================================================arch/x86/vdso/vclock_gettime.c +12
-0@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))@@ -62,6 +63,69 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx);
Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 4 lines
arch/x86/vdso/vclock_gettime.c +4
-0+ +return &pvti_base[offset].info; +} +Unless I am missing something, if gcc decides to not inline
get_pvti, this will break, right? I believe you need to mark that function with__always_inline.
Can't see why. Please enlighten me.
Quoted 13 lines
Quoted 13 lines
arch/x86/vdso/vclock_gettime.c +12
-0+static notrace cycle_t vread_pvclock(int *mode) +{ +const struct pvclock_vsyscall_time_info *pvti; +cycle_t ret; +u64 last; +u32 version; +u32 migrate_count; +u8 flags; +unsigned cpu, cpu1; + + +/*
Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 98
Quoted 6 lines
arch/x86/vdso/vclock_gettime.c +6
-0+* When looping to get a consistent (time-info, tsc) pair, we +* also need to deal with the possibility we can switch vcpus, +* so make sure we always re-fetch time-info for the current vcpu. +*/ + do { + cpu = __getcpu() & 0xfff;Please wrap this
0xfffinto something meaningful.
OK.
Quoted 21 lines
Quoted 21 lines
arch/x86/vdso/vclock_gettime.c +21
-0+ pvti =get_pvti (cpu); + + migrate_count = pvti->migrate_count; + + version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); + +/* +* Test we're still on the cpu as well as the version. +* We could have been migrated just after the first +* vgetcpu but before fetching the version, so we +* wouldn't notice a version change. +*/ + cpu1 =__getcpu () &0xfff ; + }while (unlikely (cpu != cpu1 || + (pvti->pvti.version &1 ) || + pvti->pvti.version != version || + pvti->migrate_count != migrate_count)); + +if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + *mode =VCLOCK_NONE ; +
Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 124
Quoted 5 lines
arch/x86/vdso/vclock_gettime.c +5
-0+ last =VVAR (vsyscall_gtod_data).clock.cycle_last; + +if (likely (ret >= last)) +return ret; +Please add a comment here referring to
tsc.c, where an explanation of this test lives. This is quite non-obvious for the non initiated.
OK.
Re: [patch 12/18] x86: vdso: pvclock gettime support
Quoted 45 lines · Marcelo Tosatti
Quoted 35 lines · Glauber Costa
Quoted 34 lines · Marcelo Tosatti
Improve performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/vdso/vclock_gettime.c
===================================================================arch/x86/vdso/vclock_gettime.c +12
-0@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))@@ -62,6 +63,69 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx);Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 4 lines
arch/x86/vdso/vclock_gettime.c +4
-0+ +return &pvti_base[offset].info; +} +Unless I am missing something, if gcc decides to not inline
get_pvti, this will break, right? I believe you need to mark that function with__always_inline.
Quoted 2 lines
Can't see why. Please enlighten me.
I can be wrong, I don't deal with this vdso code for quite a while - so forgive me if my memory tricked me.
But wasn't it the case that vdso functions could not call functions in the kernel address space outside the mapped page? Or does this restriction only apply to accessing data?
Re: [patch 12/18] x86: vdso: pvclock gettime support
Quoted 49 lines · Glauber Costa
Quoted 45 lines · Marcelo Tosatti
Quoted 35 lines · Glauber Costa
Quoted 34 lines · Marcelo Tosatti
Improve performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/vdso/vclock_gettime.c
===================================================================arch/x86/vdso/vclock_gettime.c +12
-0@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))@@ -62,6 +63,69 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK_VSYSCALL + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx);Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 4 lines
arch/x86/vdso/vclock_gettime.c +4
-0+ +return &pvti_base[offset].info; +} +Unless I am missing something, if gcc decides to not inline
get_pvti, this will break, right? I believe you need to mark that function with__always_inline.Quoted 2 lines
Can't see why. Please enlighten me.
Quoted 6 lines
I can be wrong, I don't deal with this vdso code for quite a while - so forgive me if my memory tricked me.
But wasn't it the case that vdso functions could not call functions in the kernel address space outside the mapped page? Or does this restriction only apply to accessing data?
Only code in the vdso page is executed, but this particular function is in the vdso so its fine.
PATCH 13/18 KVM: x86: pass host_tsc to read_l1_tsc
In reply to Marcelo TosattiAllow the caller to pass host tsc value to kvm_x86_ops->read_l1_tsc().
arch/x86/include/asm/kvm_host.h +1 -1
@@ -703,7 +703,7 @@ struct kvm_x86_ops {void (*write_tsc_offset)(struct kvm_vcpu *vcpu,u64 offset);u64 (*compute_tsc_offset)(struct kvm_vcpu *vcpu,u64 target_tsc); -u64 (*read_l1_tsc)(struct kvm_vcpu *vcpu); +u64 (*read_l1_tsc)(struct kvm_vcpu *vcpu,u64 host_tsc);void (*get_exit_info)(struct kvm_vcpu *vcpu,u64 *info1,u64 *info2);
arch/x86/kvm/lapic.c +1 -1
@@ -1011,7 +1011,7 @@ static void start_apic_timer(struct kvm_local_irq_save (flags); now = apic->lapic_timer.timer.base->get_time (); - guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu); + guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu,native_read_tsc ());if (likely (tscdeadline > guest_tsc)) { ns = (tscdeadline - guest_tsc) *1000000ULL ;do_div (ns, this_tsc_khz);
arch/x86/kvm/svm.c +2 -2
@@ -3008,11 +3008,11 @@ static int cr8_write_interception(structreturn 0 ; } -u64 svm_read_l1_tsc (struct kvm_vcpu *vcpu) +u64 svm_read_l1_tsc (struct kvm_vcpu *vcpu,u64 host_tsc) {struct vmcb *vmcb =get_host_vmcb (to_svm (vcpu));return vmcb->control.tsc_offset + -svm_scale_tsc (vcpu,native_read_tsc ()); +svm_scale_tsc (vcpu, host_tsc); }static int svm_get_msr (struct kvm_vcpu *vcpu,unsigned ecx,u64 *data)
arch/x86/kvm/vmx.c +2 -3
@@ -1839,11 +1839,10 @@ static u64 guest_read_tsc(void)* Like guest_read_tsc, but always returns L1's notion of the timestamp * counter, even if a nested guest (L2) is currently running. */ -u64 vmx_read_l1_tsc (struct kvm_vcpu *vcpu) +u64 vmx_read_l1_tsc (struct kvm_vcpu *vcpu,u64 host_tsc) { -u64 host_tsc, tsc_offset; +u64 tsc_offset; -rdtscll (host_tsc); tsc_offset =is_guest_mode (vcpu) ?to_vmx (vcpu)->nested.vmcs01_tsc_offset :vmcs_read64 (TSC_OFFSET );
arch/x86/kvm/x86.c +3 -2
@@ -1175,7 +1175,7 @@ static int kvm_guest_time_update(struct/* Keep irq disabled to prevent changes to the clock */ local_irq_save (flags); - tsc_timestamp = kvm_x86_ops->read_l1_tsc (v); + tsc_timestamp = kvm_x86_ops->read_l1_tsc (v,native_read_tsc ()); kernel_ns =get_kernel_ns (); this_tsc_khz =__get_cpu_var (cpu_tsc_khz);if (unlikely (this_tsc_khz ==0 )) {
@@ -5429,7 +5429,8 @@ static int vcpu_enter_guest(struct kvm_vif (hw_breakpoint_active ())hw_breakpoint_restore (); - vcpu->arch.last_guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu); + vcpu->arch.last_guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu, +native_read_tsc ()); vcpu->mode =OUTSIDE_GUEST_MODE ;smp_wmb ();
Re: [patch 13/18] KVM: x86: pass host_tsc to read_l1_tsc
Quoted 3 lines · Marcelo Tosatti
Allow the caller to pass host tsc value to
kvm_x86_ops->read_l1_tsc().Signed-off-by Marcelo Tosatti <[email protected]>
Would you mind explaining why?
it seems to me that rdtscll() here would be perfectly safe: the only case in which they wouldn't, is in a nested-vm environment running paravirt-linux with a paravirt tsc. In this case, it is quite likely that we'll want rdtscll *anyway*, instead of going to tsc directly.
Re: [patch 13/18] KVM: x86: pass host_tsc to read_l1_tsc
Quoted 11 lines · Glauber Costa
Quoted 3 lines · Marcelo Tosatti
Allow the caller to pass host tsc value to
kvm_x86_ops->read_l1_tsc().Signed-off-by Marcelo Tosatti <[email protected]>Would you mind explaining why?
it seems to me that
rdtscll()here would be perfectly safe: the only case in which they wouldn't, is in a nested-vm environment running paravirt-linux with a paravirt tsc. In this case, it is quite likely that we'll want rdtscll *anyway*, instead of going to tsc directly.
Its something different (from a future patch):
"KVM added a global variable to guarantee monotonicity in the guest. One of the reasons for that is that the time between
1. ktime_get_ts(×pec);
2. rdtscll(tsc);
Is variable. That is, given a host with stable TSC, suppose that two VCPUs read the same time via ktime_get_ts() above.
The time required to execute 2. is not the same on those two instances executing in different VCPUS (cache misses, interrupts...)."
Think step 1. returning the same value on both vcpus (to read the explanation above).
Re: [patch 13/18] KVM: x86: pass host_tsc to read_l1_tsc
Quoted 24 lines · Marcelo Tosatti
Quoted 11 lines · Glauber Costa
Quoted 3 lines · Marcelo Tosatti
Allow the caller to pass host tsc value to
kvm_x86_ops->read_l1_tsc().Signed-off-by Marcelo Tosatti <[email protected]>Would you mind explaining why?
it seems to me that
rdtscll()here would be perfectly safe: the only case in which they wouldn't, is in a nested-vm environment running paravirt-linux with a paravirt tsc. In this case, it is quite likely that we'll want rdtscll *anyway*, instead of going to tsc directly.Its something different (from a future patch):
"KVM added a global variable to guarantee monotonicity in the guest. One of the reasons for that is that the time between
1.
ktime_get_ts(×pec);
2. rdtscll(tsc);Is variable. That is, given a host with stable TSC, suppose that two VCPUs read the same time via
ktime_get_ts()above.
Quoted 7 lines
The time required to execute 2. is not the same on those two instances executing in different VCPUS (cache misses, interrupts...)."
Think step 1. returning the same value on both vcpus (to read the explanation above).
This still doesn't go the core of the question. You are replacing rdtscll with native_read_tsc(). They are equivalent most of the time for the host (except in the case I outlined). So the logic you exposed, is valid for both. But the real problem is another one:
Although I am not very skilled in C, I rock in the Logo programming language (http://en.wikipedia.org/wiki/Logo_(programming_language) ), and with that knowledge, I can understand your C code - with a bit of effort. After reading it, it becomes very clear that what you do is "Allow the caller to pass host tsc value to kvm_x86_ops->read_l1_tsc", so your changelog doesn't really add any data. It would be great if it explained us, readers, why instead of what!
PATCH 14/18 time: export time information for KVM pvclock
In reply to Marcelo TosattiAs suggested by John, export time data similarly to how its done by vsyscall support. This allows KVM to retrieve necessary information to implement vsyscall support in KVM guests.
include/linux/pvclock_gtod.h new file +27 -0
@@ -0,0 +1,27 @@ +#ifndef _PVCLOCK_GTOD_H +#define _PVCLOCK_GTOD_H + +#include <linux/clocksource.h> +#include <linux/notifier.h> + +struct pvclock_gtod_data { +seqcount_t seq; + +struct {/* extract of a clocksource struct */ +int vclock_mode; +cycle_t cycle_last; +cycle_t mask; +u32 mult; +u32 shift; + } clock; + +/* open coded 'struct timespec' */ +u64 monotonic_time_snsec; +time_t monotonic_time_sec; +}; +extern struct pvclock_gtod_data pvclock_gtod_data; + +extern int pvclock_gtod_register_notifier (struct notifier_block *nb); +extern int pvclock_gtod_unregister_notifier (struct notifier_block *nb); + +#endif /* _PVCLOCK_GTOD_H */
kernel/time/timekeeping.c +75 -0
93 lines not on this page · raw message
Re: [patch 14/18] time: export time information for KVM pvclock
Quoted 5 lines · Marcelo Tosatti
As suggested by John, export time data similarly to how its done by vsyscall support. This allows KVM to retrieve necessary information to implement vsyscall support in KVM guests.
Signed-off-by Marcelo Tosatti <[email protected]>
Thanks Marcelo, I like this much better then what you were proposing privately earlier!
Fairly minor nit below.
Quoted 79 lines
Index:
vsyscall/kernel/time/timekeeping.c
===================================================================kernel/time/timekeeping.c +64
-0@@ -21,6 +21,7 @@#include <linux/time.h> #include <linux/tick.h> #include <linux/stop_machine.h> +#include <linux/pvclock_gtod.h> static struct timekeeper timekeeper;@@ -180,6 +181,79 @@ static inline s64 timekeeping_get_ns_rawreturn nsec +arch_gettimeoffset (); } +static RAW_NOTIFIER_HEAD (pvclock_gtod_chain); + +/** +* pvclock_gtod_register_notifier - register a pvclock timedata update listener +* +* Must hold write on timekeeper.lock +*/ +int pvclock_gtod_register_notifier (struct notifier_block *nb) +{ +struct timekeeper *tk = &timekeeper; +unsigned long flags; +int ret; + +write_seqlock_irqsave (&tk->lock, flags); + ret =raw_notifier_chain_register (&pvclock_gtod_chain, nb); +write_sequnlock_irqrestore (&tk->lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (pvclock_gtod_register_notifier); + +/** +* pvclock_gtod_unregister_notifier - unregister a pvclock +* timedata update listener +* +* Must hold write on timekeeper.lock +*/ +int pvclock_gtod_unregister_notifier (struct notifier_block *nb) +{ +struct timekeeper *tk = &timekeeper; +unsigned long flags; +int ret; + +write_seqlock_irqsave (&tk->lock, flags); + ret =raw_notifier_chain_unregister (&pvclock_gtod_chain, nb); +write_sequnlock_irqrestore (&tk->lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (pvclock_gtod_unregister_notifier); + +struct pvclock_gtod_data pvclock_gtod_data; +EXPORT_SYMBOL_GPL (pvclock_gtod_data); + +static void update_pvclock_gtod (struct timekeeper *tk) +{ +struct pvclock_gtod_data *vdata = &pvclock_gtod_data; + +write_seqcount_begin (&vdata->seq); + +/* copy pvclock gtod data */ + vdata->clock.vclock_mode = tk->clock->archdata.vclock_mode; + vdata->clock.cycle_last = tk->clock->cycle_last; + vdata->clock.mask = tk->clock->mask; + vdata->clock.mult = tk->mult; + vdata->clock.shift = tk->shift; + + vdata->monotonic_time_sec = tk->xtime_sec + + tk->wall_to_monotonic.tv_sec; + vdata->monotonic_time_snsec = tk->xtime_nsec + + (tk->wall_to_monotonic.tv_nsec + << tk->shift); + while (vdata->monotonic_time_snsec >=
kernel/time/timekeeping.c static inline s64 timekeeping_get_ns_raw · line 256
Quoted 10 lines
kernel/time/timekeeping.c +10
-0+ (((u64)NSEC_PER_SEC) << tk->shift)) { + vdata->monotonic_time_snsec -= + ((u64 )NSEC_PER_SEC ) << tk->shift; + vdata->monotonic_time_sec++; + } + +write_seqcount_end (&vdata->seq); +raw_notifier_call_chain (&pvclock_gtod_chain,0 ,NULL ); +} +
My only request is could the update_pvclock_gtod() be implemented similarly to the update_vsyscall, where the update function lives in the pvclock code (maybe using a weak symbol or something) so we don't have to have all these pvclock details in the timekeeping core?
thanks
-john
Re: [patch 14/18] time: export time information for KVM pvclock
Quoted 107 lines · John Stultz
Quoted 5 lines · Marcelo Tosatti
As suggested by John, export time data similarly to how its done by vsyscall support. This allows KVM to retrieve necessary information to implement vsyscall support in KVM guests.
Signed-off-by Marcelo Tosatti <[email protected]>Thanks Marcelo, I like this much better then what you were proposing privately earlier!
Fairly minor nit below.
Quoted 79 lines
Index:
vsyscall/kernel/time/timekeeping.c
===================================================================kernel/time/timekeeping.c +64
-0@@ -21,6 +21,7 @@#include <linux/time.h> #include <linux/tick.h> #include <linux/stop_machine.h> +#include <linux/pvclock_gtod.h> static struct timekeeper timekeeper;@@ -180,6 +181,79 @@ static inline s64 timekeeping_get_ns_rawreturn nsec +arch_gettimeoffset (); } +static RAW_NOTIFIER_HEAD (pvclock_gtod_chain); + +/** +* pvclock_gtod_register_notifier - register a pvclock timedata update listener +* +* Must hold write on timekeeper.lock +*/ +int pvclock_gtod_register_notifier (struct notifier_block *nb) +{ +struct timekeeper *tk = &timekeeper; +unsigned long flags; +int ret; + +write_seqlock_irqsave (&tk->lock, flags); + ret =raw_notifier_chain_register (&pvclock_gtod_chain, nb); +write_sequnlock_irqrestore (&tk->lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (pvclock_gtod_register_notifier); + +/** +* pvclock_gtod_unregister_notifier - unregister a pvclock +* timedata update listener +* +* Must hold write on timekeeper.lock +*/ +int pvclock_gtod_unregister_notifier (struct notifier_block *nb) +{ +struct timekeeper *tk = &timekeeper; +unsigned long flags; +int ret; + +write_seqlock_irqsave (&tk->lock, flags); + ret =raw_notifier_chain_unregister (&pvclock_gtod_chain, nb); +write_sequnlock_irqrestore (&tk->lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (pvclock_gtod_unregister_notifier); + +struct pvclock_gtod_data pvclock_gtod_data; +EXPORT_SYMBOL_GPL (pvclock_gtod_data); + +static void update_pvclock_gtod (struct timekeeper *tk) +{ +struct pvclock_gtod_data *vdata = &pvclock_gtod_data; + +write_seqcount_begin (&vdata->seq); + +/* copy pvclock gtod data */ + vdata->clock.vclock_mode = tk->clock->archdata.vclock_mode; + vdata->clock.cycle_last = tk->clock->cycle_last; + vdata->clock.mask = tk->clock->mask; + vdata->clock.mult = tk->mult; + vdata->clock.shift = tk->shift; + + vdata->monotonic_time_sec = tk->xtime_sec + + tk->wall_to_monotonic.tv_sec; + vdata->monotonic_time_snsec = tk->xtime_nsec + + (tk->wall_to_monotonic.tv_nsec + << tk->shift); + while (vdata->monotonic_time_snsec >=kernel/time/timekeeping.c static inline s64 timekeeping_get_ns_raw · line 256
Quoted 10 lines
kernel/time/timekeeping.c +10
-0+ (((u64)NSEC_PER_SEC) << tk->shift)) { + vdata->monotonic_time_snsec -= + ((u64 )NSEC_PER_SEC ) << tk->shift; + vdata->monotonic_time_sec++; + } + +write_seqcount_end (&vdata->seq); +raw_notifier_call_chain (&pvclock_gtod_chain,0 ,NULL ); +} +My only request is could the
update_pvclock_gtod()be implemented similarly to theupdate_vsyscall, where the update function lives in the pvclock code (maybe using a weak symbol or something) so we don't have to have all these pvclock details in the timekeeping core?
Quoted 2 lines
thanks
-john
In KVM code, yes, no problem.
PATCH 15/18 KVM: x86: implement PVCLOCK_TSC_STABLE_BIT pvclock flag
In reply to Marcelo TosattiKVM added a global variable to guarantee monotonicity in the guest. One of the reasons for that is that the time between
1. ktime_get_ts(×pec);
2. rdtscll(tsc);
Is variable. That is, given a host with stable TSC, suppose that two VCPUs read the same time via ktime_get_ts() above.
The time required to execute 2. is not the same on those two instances executing in different VCPUS (cache misses, interrupts...).
If the TSC value that is used by the host to interpolate when calculating the monotonic time is the same value used to calculate the tsc_timestamp value stored in the pvclock data structure, and a single <system_timestamp, tsc_timestamp> tuple is visible to all vcpus simultaneously, this problem disappears. See comment on top of pvclock_update_vm_gtod_copy for details.
Monotonicity is then guaranteed by synchronicity of the host TSCs and guest TSCs.
Set TSC stable pvclock flag in that case, allowing the guest to read clock from userspace.
arch/x86/kvm/x86.c +212 -7
@@ -46,6 +46,7 @@#include <linux/uaccess.h> #include <linux/hash.h> #include <linux/pci.h> +#include <linux/pvclock_gtod.h> #include <trace/events/kvm.h> #define CREATE_TRACE_POINTS
@@ -1135,8 +1136,149 @@ void kvm_write_tsc(struct kvm_vcpu *vcpuEXPORT_SYMBOL_GPL (kvm_write_tsc); +static cycle_t read_tsc (void ) +{ +cycle_t ret; +u64 last; + +/* +* Empirically, a fence (of type that depends on the CPU) +* before rdtsc is enough to ensure that rdtsc is ordered +* with respect to loads. The various CPU manuals are unclear +* as to whether rdtsc can be reordered with later loads, +* but no one has ever seen it happen. +*/ +rdtsc_barrier (); + ret = (cycle_t )vget_cycles (); + + last = pvclock_gtod_data.clock.cycle_last; + +if (likely (ret >= last)) +return ret; + +/* +* GCC likes to generate cmov here, but this branch is extremely +* predictable (it's just a funciton of time and the likely is +* very likely) and there's a data dependence, so force GCC +* to generate a branch instead. I don't barrier() because +* we don't actually need a barrier, and if this function +* ever gets inlined it will generate worse code. +*/ + asmvolatile ("" ); +return last; +} + +static inline u64 vgettsc (cycle_t *cycle_now) +{ +long v; +struct pvclock_gtod_data *gtod = &pvclock_gtod_data; + + *cycle_now =read_tsc (); + + v = (*cycle_now - gtod->clock.cycle_last) & gtod->clock.mask; +return v * gtod->clock.mult; +} + +static int do_monotonic (struct timespec *ts,cycle_t *cycle_now) +{ +unsigned long seq; +u64 ns; +int mode; +struct pvclock_gtod_data *gtod = &pvclock_gtod_data; + + ts->tv_nsec =0 ; +do { + seq =read_seqcount_begin (>od->seq); + mode = gtod->clock.vclock_mode; + ts->tv_sec = gtod->monotonic_time_sec; + ns = gtod->monotonic_time_snsec; + ns +=vgettsc (cycle_now); + ns >>= gtod->clock.shift; + }while (unlikely (read_seqcount_retry (>od->seq, seq))); +timespec_add_ns (ts, ns); + +return mode; +} + +/* returns true if host is using tsc clocksource */ +static bool kvm_get_time_and_clockread (s64 *kernel_ns,cycle_t *cycle_now) +{ +struct timespec ts; + +/* checked again under seqlock below */ +if (pvclock_gtod_data.clock.vclock_mode !=VCLOCK_TSC ) +return false; + +if (do_monotonic (&ts, cycle_now) !=VCLOCK_TSC ) +return false; + +monotonic_to_bootbased (&ts); + *kernel_ns =timespec_to_ns (&ts); + +return true; +} + + +/* +* +* Assuming a stable TSC across physical CPUS, the following condition +* is possible. Each numbered line represents an event visible to both +* CPUs at the next numbered event. +* +* "timespecX" represents host monotonic time. "tscX" represents +* RDTSC value. +* +* VCPU0 on CPU0 | VCPU1 on CPU1 +* +* 1. read timespec0,tsc0 +* 2. | timespec1 = timespec0 + N +* | tsc1 = tsc0 + M +* 3. transition to guest | transition to guest +* 4. ret0 = timespec0 + (rdtsc - tsc0) | +* 5. | ret1 = timespec1 + (rdtsc - tsc1) +* | ret1 = timespec0 + N + (rdtsc - (tsc0 + M)) +* +* Since ret0 update is visible to VCPU1 at time 5, to obey monotonicity: +* +* - ret0 < ret1 +* - timespec0 + (rdtsc - tsc0) < timespec0 + N + (rdtsc - (tsc0 + M)) +* ... +* - 0 < N - M => M < N +* +* That is, when timespec0 != timespec1, M < N. Unfortunately that is not +* always the case (the difference between two distinct xtime instances +* might be smaller then the difference between corresponding TSC reads, +* when updating guest vcpus pvclock areas). +* +* To avoid that problem, do not allow visibility of distinct +* system_timestamp/tsc_timestamp values simultaneously: use a master +* copy of host monotonic time values. Update that master copy +* in lockstep. +* +* Rely on synchronization of host TSCs for monotonicity. +* +*/ + +static void pvclock_update_vm_gtod_copy (struct kvm *kvm) +{ +struct kvm_arch *ka = &kvm->arch; +int vclock_mode; + +/* +* If the host uses TSC clock, then passthrough TSC as stable +* to the guest. +*/ + ka->use_master_clock =kvm_get_time_and_clockread ( + &ka->master_kernel_ns, + &ka->master_cycle_now); + + vclock_mode = pvclock_gtod_data.clock.vclock_mode; +trace_kvm_update_master_clock (ka->use_master_clock, vclock_mode); +} +static void kvm_write_pvtime (struct kvm_vcpu *v,struct page *page, -unsigned int offset_in_page,gpa_t gpa) +unsigned int offset_in_page,gpa_t gpa, +bool use_master_clock) {struct kvm_vcpu_arch *vcpu = &v->arch;void *shared_kaddr;
@@ -1155,6 +1297,10 @@ static void kvm_write_pvtime(struct kvm_ vcpu->pvclock_set_guest_stopped_request = false; } +/* If the host uses TSC clocksource, then it is stable */ +if (use_master_clock) + pvclock_flags |=PVCLOCK_TSC_STABLE_BIT ; + vcpu->hv_clock.flags = pvclock_flags; memcpy(shared_kaddr + offset_in_page, &vcpu->hv_clock,
@@ -1169,14 +1315,18 @@ static int kvm_guest_time_update(struct {unsigned long flags;struct kvm_vcpu_arch *vcpu = &v->arch; +struct kvm_arch *ka = &v->kvm->arch;unsigned long this_tsc_khz;s64 kernel_ns, max_kernel_ns;u64 tsc_timestamp; +u64 host_tsc; +bool use_master_clock; + + kernel_ns =0 ; + host_tsc =0 ;/* Keep irq disabled to prevent changes to the clock */ local_irq_save (flags); - tsc_timestamp = kvm_x86_ops->read_l1_tsc (v,native_read_tsc ()); - kernel_ns =get_kernel_ns (); this_tsc_khz =__get_cpu_var (cpu_tsc_khz);if (unlikely (this_tsc_khz ==0 )) {local_irq_restore (flags);
@@ -1185,6 +1335,24 @@ static int kvm_guest_time_update(struct }/* +* If the host uses TSC clock, then passthrough TSC as stable +* to the guest. +*/ +spin_lock (&ka->pvclock_gtod_sync_lock); + use_master_clock = ka->use_master_clock; +if (use_master_clock) { + host_tsc = ka->master_cycle_now; + kernel_ns = ka->master_kernel_ns; + } +spin_unlock (&ka->pvclock_gtod_sync_lock); +if (!use_master_clock) { + host_tsc =native_read_tsc (); + kernel_ns =get_kernel_ns (); + } + + tsc_timestamp = kvm_x86_ops->read_l1_tsc (v, host_tsc); + +/* * We may have to catch up the TSC to match elapsed wall clock * time for two reasons, even if kvmclock is used. * 1) CPU could have been running below the maximum TSC rate
@@ -1245,8 +1413,14 @@ static int kvm_guest_time_update(struct vcpu->hw_tsc_khz = this_tsc_khz; } -if (max_kernel_ns > kernel_ns) - kernel_ns = max_kernel_ns; +/* with a master <monotonic time, tsc value> tuple, +* pvclock clock reads always increase at the (scaled) rate +* of guest TSC - no need to deal with sampling errors. +*/ +if (!use_master_clock) { +if (max_kernel_ns > kernel_ns) + kernel_ns = max_kernel_ns; + }/* With all the info we got, fill in the values */ vcpu->hv_clock.tsc_timestamp = tsc_timestamp;
@@ -1262,10 +1436,12 @@ static int kvm_guest_time_update(struct*/ vcpu->hv_clock.version +=2 ; -kvm_write_pvtime (v, vcpu->time_page, vcpu->time_offset, vcpu->time); +kvm_write_pvtime (v, vcpu->time_page, vcpu->time_offset, vcpu->time, + use_master_clock);if (vcpu->uspace_time_page)kvm_write_pvtime (v, vcpu->uspace_time_page, - vcpu->uspace_time_offset, vcpu->uspace_time); + vcpu->uspace_time_offset, vcpu->uspace_time, + use_master_clock);return 0 ; }
@@ -5302,6 +5478,28 @@ static void process_nmi(struct kvm_vcpukvm_make_request (KVM_REQ_EVENT , vcpu); } +static void kvm_gen_update_masterclock (struct kvm *kvm) +{ +int i; +struct kvm_vcpu *vcpu; +struct kvm_arch *ka = &kvm->arch; + +spin_lock (&ka->pvclock_gtod_sync_lock); +kvm_make_mclock_inprogress_request (kvm); +/* no guest entries from this point */ +pvclock_update_vm_gtod_copy (kvm); + +kvm_for_each_vcpu (i, vcpu, kvm) +set_bit (KVM_REQ_CLOCK_UPDATE , &vcpu->requests); + +/* guest entries allowed */ +kvm_for_each_vcpu (i, vcpu, kvm) +clear_bit (KVM_REQ_MCLOCK_INPROGRESS , &vcpu->requests); + +spin_unlock (&ka->pvclock_gtod_sync_lock); + +} +static int vcpu_enter_guest (struct kvm_vcpu *vcpu) {int r;
@@ -5314,6 +5512,8 @@ static int vcpu_enter_guest(struct kvm_vkvm_mmu_unload (vcpu);if (kvm_check_request (KVM_REQ_MIGRATE_TIMER , vcpu))__kvm_migrate_timers (vcpu); +if (kvm_check_request (KVM_REQ_MASTERCLOCK_UPDATE , vcpu)) +kvm_gen_update_masterclock (vcpu->kvm);if (kvm_check_request (KVM_REQ_CLOCK_UPDATE , vcpu)) { r =kvm_guest_time_update (vcpu);if (unlikely (r))
@@ -6219,6 +6419,8 @@ int kvm_arch_hardware_enable(void *garbakvm_for_each_vcpu (i, vcpu, kvm) { vcpu->arch.tsc_offset_adjustment += delta_cyc; vcpu->arch.last_host_tsc = local_tsc; +set_bit (KVM_REQ_MASTERCLOCK_UPDATE , + &vcpu->requests); }/*
@@ -6356,6 +6558,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unraw_spin_lock_init (&kvm->arch.tsc_write_lock);mutex_init (&kvm->arch.apic_map_lock); +spin_lock_init (&kvm->arch.pvclock_gtod_sync_lock); + +pvclock_update_vm_gtod_copy (kvm);return 0 ; }
arch/x86/include/asm/kvm_host.h +6 -0
@@ -22,6 +22,7 @@#include <linux/kvm_para.h> #include <linux/kvm_types.h> #include <linux/perf_event.h> +#include <linux/pvclock_gtod.h> #include <asm/pvclock-abi.h> #include <asm/desc.h>
@@ -563,6 +564,11 @@ struct kvm_arch {u64 cur_tsc_offset;u8 cur_tsc_generation; +spinlock_t pvclock_gtod_sync_lock; +bool use_master_clock; +u64 master_kernel_ns; +cycle_t master_cycle_now; +struct kvm_xen_hvm_config xen_hvm_config;/* fields used by HYPER-V emulation */
include/linux/kvm_host.h +3 -0
@@ -118,6 +118,8 @@ static inline bool is_error_page(struct#define KVM_REQ_IMMEDIATE_EXIT 15#define KVM_REQ_PMU 16#define KVM_REQ_PMI 17 +#define KVM_REQ_MASTERCLOCK_UPDATE 18 +#define KVM_REQ_MCLOCK_INPROGRESS 19#define KVM_USERSPACE_IRQ_SOURCE_ID 0#define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1
@@ -527,6 +529,7 @@ void kvm_put_guest_fpu(struct kvm_vcpu *void kvm_flush_remote_tlbs (struct kvm *kvm);void kvm_reload_remote_mmus (struct kvm *kvm); +void kvm_make_mclock_inprogress_request (struct kvm *kvm);long kvm_arch_dev_ioctl (struct file *filp,unsigned int ioctl,unsigned long arg);
virt/kvm/kvm_main.c +5 -0
@@ -212,6 +212,11 @@ void kvm_reload_remote_mmus(struct kvm *make_all_cpus_request (kvm,KVM_REQ_MMU_RELOAD ); } +void kvm_make_mclock_inprogress_request (struct kvm *kvm) +{ +make_all_cpus_request (kvm,KVM_REQ_MCLOCK_INPROGRESS ); +} +int kvm_vcpu_init (struct kvm_vcpu *vcpu,struct kvm *kvm,unsigned id) {struct page *page;
arch/x86/kvm/trace.h +26 -0
@@ -4,6 +4,7 @@#include <linux/tracepoint.h> #include <asm/vmx.h> #include <asm/svm.h> +#include <asm/clocksource.h> #undef TRACE_SYSTEM#define TRACE_SYSTEM kvm
@@ -754,6 +755,31 @@ TRACE_EVENT( __entry->write ?"Write" :"Read" , __entry->gpa_match ? "GPA" : "GVA") ); + +#define host_clocks \ + {VCLOCK_NONE, "none"}, \ + {VCLOCK_TSC, "tsc"}, \ + {VCLOCK_HPET, "hpet"} \ + +TRACE_EVENT (kvm_update_master_clock, + TP_PROTO(bool use_master_clock, unsigned int host_clock), +TP_ARGS (use_master_clock, host_clock), + +TP_STRUCT__entry ( + __field( bool, use_master_clock ) + __field( unsigned int, host_clock ) + ), + +TP_fast_assign ( + __entry->use_master_clock = use_master_clock; + __entry->host_clock = host_clock; + ), + +TP_printk ("masterclock %d hostclock %s" , + __entry->use_master_clock, +__print_symbolic (__entry->host_clock, host_clocks)) +); +#endif /* _TRACE_KVM_H */ #undef TRACE_INCLUDE_PATH
Re: [patch 15/18] KVM: x86: implement PVCLOCK_TSC_STABLE_BIT pvclock flag
Quoted 19 lines · Marcelo Tosatti
KVM added a global variable to guarantee monotonicity in the guest. One of the reasons for that is that the time between
1.
ktime_get_ts(×pec);
2. rdtscll(tsc);Is variable. That is, given a host with stable TSC, suppose that two VCPUs read the same time via
ktime_get_ts()above.The time required to execute 2. is not the same on those two instances executing in different VCPUS (cache misses, interrupts...).
If the TSC value that is used by the host to interpolate when calculating the monotonic time is the same value used to calculate the
tsc_timestampvalue stored in the pvclock data structure, and a single <system_timestamp,tsc_timestamp> tuple is visible to all vcpus simultaneously, this problem disappears. See comment on top ofpvclock_update_vm_gtod_copyfor details.
Quoted 7 lines
Monotonicity is then guaranteed by synchronicity of the host TSCs and guest TSCs.
Set TSC stable pvclock flag in that case, allowing the guest to read clock from userspace.
Signed-off-by Marcelo Tosatti <[email protected]>
If you are using a master copy, with a stable host-side tsc, you can get rid of the normal REQ_CLOCK updates during vcpu load.
Re: [patch 15/18] KVM: x86: implement PVCLOCK_TSC_STABLE_BIT pvclock flag\
Quoted 23 lines · Glauber Costa
Quoted 22 lines · Marcelo Tosatti
KVM added a global variable to guarantee monotonicity in the guest. One of the reasons for that is that the time between
1.
ktime_get_ts(×pec);
2. rdtscll(tsc);Is variable. That is, given a host with stable TSC, suppose that two VCPUs read the same time via
ktime_get_ts()above.The time required to execute 2. is not the same on those two instances executing in different VCPUS (cache misses, interrupts...).
If the TSC value that is used by the host to interpolate when calculating the monotonic time is the same value used to calculate the
tsc_timestampvalue stored in the pvclock data structure, and a single <system_timestamp,tsc_timestamp> tuple is visible to all vcpus simultaneously, this problem disappears. See comment on top ofpvclock_update_vm_gtod_copyfor details.Monotonicity is then guaranteed by synchronicity of the host TSCs and guest TSCs.
Quoted 7 lines
Quoted 4 lines
Set TSC stable pvclock flag in that case, allowing the guest to read clock from userspace.
Signed-off-by Marcelo Tosatti <[email protected]>If you are using a master copy, with a stable host-side tsc, you can get rid of the normal
REQ_CLOCKupdates during vcpu load.
Yes. The updates are harmless and not frequent, though, so i'd rather not touch this.
PATCH 16/18 KVM: x86: notifier for clocksource changes
In reply to Marcelo TosattiRegister a notifier for clocksource change event. In case the host switches to clock other than TSC, disable master clock usage.
arch/x86/kvm/x86.c +44 -0
@@ -1218,6 +1218,7 @@ static bool kvm_get_time_and_clockread(sreturn true; } +static atomic_t kvm_guest_has_master_clock =ATOMIC_INIT (0 );/* *
@@ -1271,6 +1272,8 @@ static void pvclock_update_vm_gtod_copy( ka->use_master_clock =kvm_get_time_and_clockread ( &ka->master_kernel_ns, &ka->master_cycle_now); +if (ka->use_master_clock) +atomic_set (&kvm_guest_has_master_clock,1 ); vclock_mode = pvclock_gtod_data.clock.vclock_mode;trace_kvm_update_master_clock (ka->use_master_clock, vclock_mode);
@@ -5124,6 +5127,44 @@ static void kvm_set_mmio_spte_mask(void)kvm_mmu_set_mmio_spte_mask (mask); } +static void pvclock_gtod_update_fn (struct work_struct *work) +{ +struct kvm *kvm; +struct kvm_vcpu *vcpu; +int i; + +raw_spin_lock (&kvm_lock); +list_for_each_entry (kvm, &vm_list, vm_list) +kvm_for_each_vcpu (i, vcpu, kvm) +set_bit (KVM_REQ_MASTERCLOCK_UPDATE , &vcpu->requests); +atomic_set (&kvm_guest_has_master_clock,0 ); +raw_spin_unlock (&kvm_lock); +} + +static DECLARE_WORK(pvclock_gtod_work, pvclock_gtod_update_fn); + +/* +* Notification about pvclock gtod data update. +*/ +static int pvclock_gtod_notify (struct notifier_block *nb,unsigned long unused, +void *unused2) +{ +struct pvclock_gtod_data *gtod = &pvclock_gtod_data; + +/* disable master clock if host does not trust, or does not +* use, TSC clocksource +*/ +if (gtod->clock.vclock_mode !=VCLOCK_TSC && +atomic_read (&kvm_guest_has_master_clock) !=0 ) +queue_work (system_long_wq, &pvclock_gtod_work); + +return 0 ; +} + +static struct notifier_block pvclock_gtod_notifier = { + .notifier_call = pvclock_gtod_notify, +}; +int kvm_arch_init (void *opaque) {int r;
@@ -5165,6 +5206,8 @@ int kvm_arch_init(void *opaque) host_xcr0 =xgetbv (XCR_XFEATURE_ENABLED_MASK );kvm_lapic_init (); +pvclock_gtod_register_notifier (&pvclock_gtod_notifier); +return 0 ; out:
@@ -5179,6 +5222,7 @@ void kvm_arch_exit(void)cpufreq_unregister_notifier (&kvmclock_cpufreq_notifier_block,CPUFREQ_TRANSITION_NOTIFIER );unregister_hotcpu_notifier (&kvmclock_cpu_notifier_block); +pvclock_gtod_unregister_notifier (&pvclock_gtod_notifier); kvm_x86_ops =NULL ;kvm_mmu_module_exit (); }
PATCH 17/18 KVM: x86: add kvm_arch_vcpu_postcreate callback, move TSC initialization
In reply to Marcelo TosattiTSC initialization will soon make use of online_vcpus.
arch/ia64/kvm/kvm-ia64.c +5 -0
@@ -1330,6 +1330,11 @@ int kvm_arch_vcpu_setup(struct kvm_vcpureturn 0 ; } +int kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +return 0 ; +} +int kvm_arch_vcpu_ioctl_get_fpu (struct kvm_vcpu *vcpu,struct kvm_fpu *fpu) {return -EINVAL ;
arch/powerpc/kvm/powerpc.c +5 -0
@@ -354,6 +354,11 @@ struct kvm_vcpu *kvm_arch_vcpu_create(streturn vcpu; } +void kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +return 0 ; +} +void kvm_arch_vcpu_free (struct kvm_vcpu *vcpu) {/* Make sure we're not using the vcpu anymore */
arch/s390/kvm/kvm-s390.c +5 -0
@@ -355,6 +355,11 @@ static void kvm_s390_vcpu_initial_reset(atomic_set_mask (CPUSTAT_STOPPED , &vcpu->arch.sie_block->cpuflags); } +void kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +return 0 ; +} +int kvm_arch_vcpu_setup (struct kvm_vcpu *vcpu) { atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH |
arch/x86/kvm/svm.c +0 -1
@@ -1254,7 +1254,6 @@ static struct kvm_vcpu *svm_create_vcpu( svm->vmcb_pa =page_to_pfn (page) <<PAGE_SHIFT ; svm->asid_generation =0 ;init_vmcb (svm); -kvm_write_tsc (&svm->vcpu,0 ); err =fx_init (&svm->vcpu);if (err)
arch/x86/kvm/vmx.c +0 -2
@@ -3896,8 +3896,6 @@ static int vmx_vcpu_setup(struct vcpu_vmvmcs_writel (CR0_GUEST_HOST_MASK , ~0UL );set_cr4_guest_host_mask (vmx); -kvm_write_tsc (&vmx->vcpu,0 ); -return 0 ; }
arch/x86/kvm/x86.c +13 -0
@@ -6350,6 +6350,19 @@ int kvm_arch_vcpu_setup(struct kvm_vcpureturn r; } +int kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +int r; + + r =vcpu_load (vcpu); +if (r) +return r; +kvm_write_tsc (vcpu,0 ); +vcpu_put (vcpu); + +return r; +} +void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu) {int r;
include/linux/kvm_host.h +1 -0
@@ -583,6 +583,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpuvoid kvm_arch_vcpu_put (struct kvm_vcpu *vcpu);struct kvm_vcpu *kvm_arch_vcpu_create (struct kvm *kvm,unsigned int id);int kvm_arch_vcpu_setup (struct kvm_vcpu *vcpu); +int kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu);void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu);int kvm_arch_vcpu_reset (struct kvm_vcpu *vcpu);
virt/kvm/kvm_main.c +1 -0
@@ -1855,6 +1855,7 @@ static int kvm_vm_ioctl_create_vcpu(struatomic_inc (&kvm->online_vcpus);mutex_unlock (&kvm->lock); +kvm_arch_vcpu_postcreate (vcpu);return r; unlock_vcpu_destroy:
PATCH 18/18 KVM: x86: require matched TSC offsets for master clock
In reply to Marcelo TosattiWith master clock, a pvclock clock read calculates:
ret = system_timestamp + [ (rdtsc + tsc_offset) - tsc_timestamp ]
Where 'rdtsc' is the host TSC.
system_timestamp and tsc_timestamp are unique, one tuple per VM: the "master clock".
Given a host with synchronized TSCs, its obvious that guest TSC must be matched for the above to guarantee monotonicity.
Allow master clock usage only if guest TSCs are synchronized.
arch/x86/include/asm/kvm_host.h +1 -0
@@ -563,6 +563,7 @@ struct kvm_arch {u64 cur_tsc_write;u64 cur_tsc_offset;u8 cur_tsc_generation; +int nr_vcpus_matched_tsc;spinlock_t pvclock_gtod_sync_lock;bool use_master_clock;
arch/x86/kvm/x86.c +50 -5
114 lines not on this page · raw message
arch/x86/kvm/trace.h +36 -3
@@ -762,21 +762,54 @@ TRACE_EVENT( {VCLOCK_HPET, "hpet"} \TRACE_EVENT (kvm_update_master_clock, - TP_PROTO(bool use_master_clock, unsigned int host_clock), -TP_ARGS (use_master_clock, host_clock), + TP_PROTO(bool use_master_clock, unsigned int host_clock, bool offset_matched), +TP_ARGS (use_master_clock, host_clock, offset_matched),TP_STRUCT__entry ( __field( bool, use_master_clock ) __field( unsigned int, host_clock ) +__field ( bool, offset_matched ) ),TP_fast_assign ( __entry->use_master_clock = use_master_clock; __entry->host_clock = host_clock; + __entry->offset_matched = offset_matched; ), -TP_printk ("masterclock %d hostclock %s" , +TP_printk ("masterclock %d hostclock %s offsetmatched %u" , __entry->use_master_clock, +__print_symbolic (__entry->host_clock, host_clocks), + __entry->offset_matched) +); + +TRACE_EVENT (kvm_track_tsc, + TP_PROTO(unsigned int vcpu_id, unsigned int nr_matched, + unsigned int online_vcpus, bool use_master_clock, + unsigned int host_clock), +TP_ARGS (vcpu_id, nr_matched, online_vcpus, use_master_clock, + host_clock), + +TP_STRUCT__entry ( + __field( unsigned int, vcpu_id ) + __field( unsigned int, nr_vcpus_matched_tsc ) + __field( unsigned int, online_vcpus ) + __field( bool, use_master_clock ) + __field( unsigned int, host_clock ) + ), + +TP_fast_assign ( + __entry->vcpu_id = vcpu_id; + __entry->nr_vcpus_matched_tsc = nr_matched; + __entry->online_vcpus = online_vcpus; + __entry->use_master_clock = use_master_clock; + __entry->host_clock = host_clock; + ), + +TP_printk ("vcpu_id %u masterclock %u offsetmatched %u nr_online %u" +" hostclock %s" , + __entry->vcpu_id, __entry->use_master_clock, + __entry->nr_vcpus_matched_tsc, __entry->online_vcpus,__print_symbolic (__entry->host_clock, host_clocks)) );
cover pvclock vsyscall support + KVM hypervisor support (v3)
In reply to Marcelo TosattiThis patchset, based on earlier work by Jeremy Fitzhardinge, implements paravirtual clock vsyscall support.
It should be possible to implement Xen support relatively easily.
It reduces clock_gettime from 500 cycles to 200 cycles on my testbox.
Please review.
Quoted 1 line
From my POV, this is ready to merge.
v3:
- fix PVCLOCK_VSYSCALL_NR_PAGES definition (glommer)
- fold flags race fix into pvclock refactoring (avi)
- remove CONFIG_PARAVIRT_CLOCK_VSYSCALL (glommer)
- add reference to tsc.c from vclock_gettime.c about cycle_last rationale (glommer)
- fix whitespace damage (glommer)
v2:
- Do not allow visibility of different <system_timestamp, tsc_timestamp> tuples.
- Add option to disable vsyscall.
PATCH 1/16 KVM: x86: retain pvclock guest stopped bit in guest memory
Otherwise its possible for an unrelated KVM_REQ_UPDATE_CLOCK (such as due to CPU migration) to clear the bit.
Noticed by Paolo Bonzini.
arch/x86/kvm/x86.c +13 -7
@@ -1143,6 +1143,7 @@ static int kvm_guest_time_update(structunsigned long this_tsc_khz;s64 kernel_ns, max_kernel_ns;u64 tsc_timestamp; +struct pvclock_vcpu_time_info *guest_hv_clock;u8 pvclock_flags;/* Keep irq disabled to prevent changes to the clock */
@@ -1226,13 +1227,6 @@ static int kvm_guest_time_update(struct vcpu->last_kernel_ns = kernel_ns; vcpu->last_guest_tsc = tsc_timestamp; - pvclock_flags =0 ; -if (vcpu->pvclock_set_guest_stopped_request) { - pvclock_flags |=PVCLOCK_GUEST_STOPPED ; - vcpu->pvclock_set_guest_stopped_request = false; - } - - vcpu->hv_clock.flags = pvclock_flags;/* * The interface expects us to write an even number signaling that the
@@ -1243,6 +1237,18 @@ static int kvm_guest_time_update(struct shared_kaddr =kmap_atomic (vcpu->time_page); + guest_hv_clock = shared_kaddr + vcpu->time_offset; + +/* retain PVCLOCK_GUEST_STOPPED if set in guest copy */ + pvclock_flags = (guest_hv_clock->flags &PVCLOCK_GUEST_STOPPED ); + +if (vcpu->pvclock_set_guest_stopped_request) { + pvclock_flags |=PVCLOCK_GUEST_STOPPED ; + vcpu->pvclock_set_guest_stopped_request = false; + } + + vcpu->hv_clock.flags = pvclock_flags; +memcpy (shared_kaddr + vcpu->time_offset, &vcpu->hv_clock,sizeof (vcpu->hv_clock));
Re: [patch 01/16] KVM: x86: retain pvclock guest stopped bit in guest memory
Quoted 7 lines · Marcelo Tosatti
Otherwise its possible for an unrelated
KVM_REQ_UPDATE_CLOCK(such as due to CPU migration) to clear the bit.Noticed by Paolo Bonzini.
Signed-off-by Marcelo Tosatti <[email protected]>
Small nitpick bellow.
Also do we really need to call kvm_guest_time_update() on a guest pause? Wouldn't separate request bit, which only sets the flag, suffice?
Quoted 33 lines
Index:
vsyscall/arch/x86/kvm/x86.c
===================================================================arch/x86/kvm/x86.c +4
-7@@ -1143,6 +1143,7 @@ static int kvm_guest_time_update(structunsigned long this_tsc_khz;s64 kernel_ns, max_kernel_ns;u64 tsc_timestamp; +struct pvclock_vcpu_time_info *guest_hv_clock;u8 pvclock_flags;/* Keep irq disabled to prevent changes to the clock */ @@ -1226,13 +1227,6 @@ static int kvm_guest_time_update(struct vcpu->last_kernel_ns = kernel_ns; vcpu->last_guest_tsc = tsc_timestamp; - pvclock_flags =0 ; -if (vcpu->pvclock_set_guest_stopped_request) { - pvclock_flags |=PVCLOCK_GUEST_STOPPED ; - vcpu->pvclock_set_guest_stopped_request = false; - } - - vcpu->hv_clock.flags = pvclock_flags;/* * The interface expects us to write an even number signaling that the @@ -1243,6 +1237,18 @@ static int kvm_guest_time_update(struct shared_kaddr =kmap_atomic (vcpu->time_page); + guest_hv_clock = shared_kaddr + vcpu->time_offset; + +/* retain PVCLOCK_GUEST_STOPPED if set in guest copy */
arch/x86/kvm/x86.c static int kvm_guest_time_update(struct · line 1252
Quoted 10 lines
arch/x86/kvm/x86.c +9
-0+ pvclock_flags = (guest_hv_clock->flags &PVCLOCK_GUEST_STOPPED ); + +if (vcpu->pvclock_set_guest_stopped_request) { + pvclock_flags |=PVCLOCK_GUEST_STOPPED ; + vcpu->pvclock_set_guest_stopped_request = false; + } + + vcpu->hv_clock.flags = pvclock_flags; + memcpy(shared_kaddr + vcpu->time_offset, &vcpu->hv_clock,
We can use guest_hv_clock here now.
arch/x86/kvm/x86.c static int kvm_guest_time_update(struct · line 1254
Quoted 3 lines
arch/x86/kvm/x86.c +0
-0sizeof(vcpu->hv_clock));
--
Gleb.
Re: [patch 01/16] KVM: x86: retain pvclock guest stopped bit in guest memory
Quoted 6 lines · Gleb Natapov
Quoted 5 lines · Marcelo Tosatti
Otherwise its possible for an unrelated
KVM_REQ_UPDATE_CLOCK(such as due to CPU migration) to clear the bit.Noticed by Paolo Bonzini.
Quoted 8 lines
Quoted 2 lines
Signed-off-by Marcelo Tosatti <[email protected]>Reviewed-by Gleb Natapov <[email protected]>Small nitpick bellow.
Also do we really need to call
kvm_guest_time_update()on a guest pause? Wouldn't separate request bit, which only sets the flag, suffice?
Management of vcpu clock area better be isolated in a single place.
Quoted 51 lines
Quoted 33 lines
Index:
vsyscall/arch/x86/kvm/x86.c
===================================================================arch/x86/kvm/x86.c +4
-7@@ -1143,6 +1143,7 @@ static int kvm_guest_time_update(structunsigned long this_tsc_khz;s64 kernel_ns, max_kernel_ns;u64 tsc_timestamp; +struct pvclock_vcpu_time_info *guest_hv_clock;u8 pvclock_flags;/* Keep irq disabled to prevent changes to the clock */ @@ -1226,13 +1227,6 @@ static int kvm_guest_time_update(struct vcpu->last_kernel_ns = kernel_ns; vcpu->last_guest_tsc = tsc_timestamp; - pvclock_flags =0 ; -if (vcpu->pvclock_set_guest_stopped_request) { - pvclock_flags |=PVCLOCK_GUEST_STOPPED ; - vcpu->pvclock_set_guest_stopped_request = false; - } - - vcpu->hv_clock.flags = pvclock_flags;/* * The interface expects us to write an even number signaling that the @@ -1243,6 +1237,18 @@ static int kvm_guest_time_update(struct shared_kaddr =kmap_atomic (vcpu->time_page); + guest_hv_clock = shared_kaddr + vcpu->time_offset; + +/* retain PVCLOCK_GUEST_STOPPED if set in guest copy */ arch/x86/kvm/x86.c static int kvm_guest_time_update(struct · line 1252
Quoted 10 lines
arch/x86/kvm/x86.c +9
-0+ pvclock_flags = (guest_hv_clock->flags &PVCLOCK_GUEST_STOPPED ); + +if (vcpu->pvclock_set_guest_stopped_request) { + pvclock_flags |=PVCLOCK_GUEST_STOPPED ; + vcpu->pvclock_set_guest_stopped_request = false; + } + + vcpu->hv_clock.flags = pvclock_flags; + memcpy(shared_kaddr + vcpu->time_offset, &vcpu->hv_clock,We can use
guest_hv_clockhere now.arch/x86/kvm/x86.c static int kvm_guest_time_update(struct · line 1254
Quoted 3 lines
arch/x86/kvm/x86.c +0
-0sizeof(vcpu->hv_clock));--
Gleb.
Re: [patch 01/16] KVM: x86: retain pvclock guest stopped bit in guest memory
In reply to Marcelo TosattiQuoted 9 lines · Marcelo Tosatti
Otherwise its possible for an unrelated
KVM_REQ_UPDATE_CLOCK(such as due to CPU migration) to clear the bit.Noticed by Paolo Bonzini.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kvm/x86.c
===================================================================
PATCH 2/16 x86: pvclock: make sure rdtsc doesnt speculate out of region
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.
arch/x86/kernel/pvclock.c +2 -2
@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(structdo { version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
Re: [patch 02/16] x86: pvclock: make sure rdtsc doesnt speculate out of region
Quoted 15 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +0
-0@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(struct do {
arch/x86/kernel/pvclock.c cycle_t pvclock_clocksource_read(struct · line 106
Quoted 10 lines
arch/x86/kernel/pvclock.c +2
-2version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
On a guest without SSE2 rdtsc_barrier() will be nop while rmb() will be "lock; addl $0,0(%%esp)". I doubt pvclock will work correctly either way though.
--
Gleb.
Re: [patch 02/16] x86: pvclock: make sure rdtsc doesnt speculate out of region
Quoted 24 lines · Gleb Natapov
Quoted 23 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +2
-2@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(structdo { version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version);
Quoted 9 lines
arch/x86/kernel/pvclock.c cycle_t pvclock_clocksource_read(struct · line 106
Quoted 2 lines
arch/x86/kernel/pvclock.c +0
-0if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&On a guest without SSE2
rdtsc_barrier()will be nop whilermb()will be "lock; addl $0,0(%%esp)". I doubt pvclock will work correctly either way though.--
Gleb.
Actually it shouldn't matter for KVM, since the page is only updated by the vcpu, and the guest is never running while it happens. If Jeremy is fine with this, so should I.
Re: [patch 02/16] x86: pvclock: make sure rdtsc doesnt speculate out of region
Quoted 31 lines · Glauber Costa
Quoted 30 lines · Gleb Natapov
Quoted 15 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +0
-0@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(struct do {arch/x86/kernel/pvclock.c cycle_t pvclock_clocksource_read(struct · line 106
Quoted 10 lines
arch/x86/kernel/pvclock.c +2
-2version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&On a guest without SSE2
rdtsc_barrier()will be nop whilermb()will be "lock; addl $0,0(%%esp)". I doubt pvclock will work correctly either way though.
Quoted 6 lines
Quoted 3 lines
--
Gleb.Actually it shouldn't matter for KVM, since the page is only updated by the vcpu, and the guest is never running while it happens. If Jeremy is fine with this, so should I.
If you and Jeremy are fine with this, so should I.
--
Gleb.
Re: [patch 02/16] x86: pvclock: make sure rdtsc doesnt speculate out of region
In reply to Glauber CostaQuoted 31 lines · Glauber Costa
Quoted 30 lines · Gleb Natapov
Quoted 15 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +0
-0@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(struct do {arch/x86/kernel/pvclock.c cycle_t pvclock_clocksource_read(struct · line 106
Quoted 10 lines
arch/x86/kernel/pvclock.c +2
-2version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&On a guest without SSE2
rdtsc_barrier()will be nop whilermb()will be "lock; addl $0,0(%%esp)". I doubt pvclock will work correctly either way though.
Quoted 6 lines
Quoted 3 lines
--
Gleb.Actually it shouldn't matter for KVM, since the page is only updated by the vcpu, and the guest is never running while it happens. If Jeremy is fine with this, so should I.
17.13 TIME-STAMP COUNTER
"The RDTSC instruction is not serializing or ordered with other instructions. It does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the RDTSC instruction operation is performed."
Both instructions are TSC barriers.
Re: [patch 02/16] x86: pvclock: make sure rdtsc doesnt speculate out of region
Quoted 41 lines · Marcelo Tosatti
Quoted 31 lines · Glauber Costa
Quoted 30 lines · Gleb Natapov
Quoted 15 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +0
-0@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(struct do {arch/x86/kernel/pvclock.c cycle_t pvclock_clocksource_read(struct · line 106
Quoted 10 lines
arch/x86/kernel/pvclock.c +2
-2version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&On a guest without SSE2
rdtsc_barrier()will be nop whilermb()will be "lock; addl $0,0(%%esp)". I doubt pvclock will work correctly either way though.Quoted 6 lines
Quoted 3 lines
--
Gleb.Actually it shouldn't matter for KVM, since the page is only updated by the vcpu, and the guest is never running while it happens. If Jeremy is fine with this, so should I.
17.13 TIME-STAMP COUNTER
Quoted 8 lines
"The RDTSC instruction is not serializing or ordered with other instructions. It does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the RDTSC instruction operation is performed."
Both instructions are TSC barriers.
Which both instructions?
--
Gleb.
Re: [patch 02/16] x86: pvclock: make sure rdtsc doesnt speculate out of region
Quoted 48 lines · Gleb Natapov
Quoted 47 lines · Marcelo Tosatti
Quoted 31 lines · Glauber Costa
Quoted 30 lines · Gleb Natapov
Quoted 15 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +0
-0@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(struct do {arch/x86/kernel/pvclock.c cycle_t pvclock_clocksource_read(struct · line 106
Quoted 10 lines
arch/x86/kernel/pvclock.c +2
-2version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&On a guest without SSE2
rdtsc_barrier()will be nop whilermb()will be "lock; addl $0,0(%%esp)". I doubt pvclock will work correctly either way though.Quoted 6 lines
Quoted 3 lines
--
Gleb.Actually it shouldn't matter for KVM, since the page is only updated by the vcpu, and the guest is never running while it happens. If Jeremy is fine with this, so should I.
17.13 TIME-STAMP COUNTER
"The RDTSC instruction is not serializing or ordered with other instructions. It does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the RDTSC instruction operation is performed."
Quoted 3 lines
Quoted 2 lines
Both instructions are TSC barriers.
Which both instructions?
static __always_inline void rdtsc_barrier(void)
{
alternative(ASM_NOP3, "mfence", X86_FEATURE_MFENCE_RDTSC);
alternative(ASM_NOP3, "lfence", X86_FEATURE_LFENCE_RDTSC);
}Re: [patch 02/16] x86: pvclock: make sure rdtsc doesnt speculate out of region
Quoted 51 lines · Marcelo Tosatti
Quoted 50 lines · Gleb Natapov
Quoted 49 lines · Marcelo Tosatti
Quoted 31 lines · Glauber Costa
Quoted 30 lines · Gleb Natapov
Quoted 15 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
pvclock_get_time_values, which contains the memory barriers will be removed by next patch.Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +0
-0@@ -97,10 +97,10 @@ cycle_t pvclock_clocksource_read(struct do {arch/x86/kernel/pvclock.c cycle_t pvclock_clocksource_read(struct · line 106
Quoted 10 lines
arch/x86/kernel/pvclock.c +2
-2version =pvclock_get_time_values (&shadow, src); -barrier (); +rdtsc_barrier (); offset =pvclock_get_nsec_offset (&shadow); ret = shadow.system_timestamp + offset; -barrier (); +rdtsc_barrier (); }while (version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&On a guest without SSE2
rdtsc_barrier()will be nop whilermb()will be "lock; addl $0,0(%%esp)". I doubt pvclock will work correctly either way though.Quoted 6 lines
Quoted 3 lines
--
Gleb.Actually it shouldn't matter for KVM, since the page is only updated by the vcpu, and the guest is never running while it happens. If Jeremy is fine with this, so should I.
17.13 TIME-STAMP COUNTER
"The RDTSC instruction is not serializing or ordered with other instructions. It does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the RDTSC instruction operation is performed."
Both instructions are TSC barriers.
Quoted 7 lines
Quoted 1 line
Which both instructions?
static
__always_inlinevoidrdtsc_barrier(void){ alternative(ASM_NOP3, "mfence", X86_FEATURE_MFENCE_RDTSC); alternative(ASM_NOP3, "lfence", X86_FEATURE_LFENCE_RDTSC); }
Both of them will be patched to nop if guest does not have SSE2 cpuid bit.
--
Gleb.
PATCH 3/16 x86: pvclock: remove pvclock_shadow_time
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
We can copy the information directly from "struct pvclock_vcpu_time_info", remove pvclock_shadow_time.
arch/x86/kernel/pvclock.c +11 -46
@@ -19,21 +19,6 @@#include <linux/percpu.h> #include <asm/pvclock.h> -/* -* These are perodically updated -* xen: magic shared_info page -* kvm: gpa registered via msr -* and then copied here. -*/ -struct pvclock_shadow_time { -u64 tsc_timestamp;/* TSC at last update of time vals. */ -u64 system_timestamp;/* Time, in nanosecs, since boot. */ -u32 tsc_to_nsec_mul; -int tsc_shift; -u32 version; -u8 flags; -}; -static u8 valid_flags__read_mostly =0 ;void pvclock_set_flags (u8 flags)
@@ -41,32 +26,11 @@ void pvclock_set_flags(u8 flags) valid_flags = flags; } -static u64 pvclock_get_nsec_offset (struct pvclock_shadow_time *shadow) +static u64 pvclock_get_nsec_offset (const struct pvclock_vcpu_time_info *src) { -u64 delta =native_read_tsc () - shadow->tsc_timestamp; -return pvclock_scale_delta (delta, shadow->tsc_to_nsec_mul, - shadow->tsc_shift); -} - -/* -* Reads a consistent set of time-base values from hypervisor, -* into a shadow data area. -*/ -static unsigned pvclock_get_time_values (struct pvclock_shadow_time *dst, -struct pvclock_vcpu_time_info *src) -{ -do { - dst->version = src->version; -rmb ();/* fetch version before data */ - dst->tsc_timestamp = src->tsc_timestamp; - dst->system_timestamp = src->system_time; - dst->tsc_to_nsec_mul = src->tsc_to_system_mul; - dst->tsc_shift = src->tsc_shift; - dst->flags = src->flags; -rmb ();/* test version after fetching data */ - }while ((src->version &1 ) || (dst->version != src->version)); - -return dst->version; +u64 delta =native_read_tsc () - src->tsc_timestamp; +return pvclock_scale_delta (delta, src->tsc_to_system_mul, + src->tsc_shift); }unsigned long pvclock_tsc_khz (struct pvclock_vcpu_time_info *src)
@@ -90,21 +54,22 @@ void pvclock_resume(void)cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) { -struct pvclock_shadow_time shadow;unsigned version;cycle_t ret, offset;u64 last; +u8 flags;do { - version =pvclock_get_time_values (&shadow, src); + version = src->version;rdtsc_barrier (); - offset =pvclock_get_nsec_offset (&shadow); - ret = shadow.system_timestamp + offset; + offset =pvclock_get_nsec_offset (src); + ret = src->system_time + offset; + flags = src->flags;rdtsc_barrier (); - }while (version != src->version); + }while ((src->version &1 ) || version != src->version);if ((valid_flags &PVCLOCK_TSC_STABLE_BIT ) && - (shadow.flags &PVCLOCK_TSC_STABLE_BIT )) + (flags &PVCLOCK_TSC_STABLE_BIT ))return ret;/*
Re: [patch 03/16] x86: pvclock: remove pvclock_shadow_time
Quoted 6 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
We can copy the information directly from "struct
pvclock_vcpu_time_info", removepvclock_shadow_time.Signed-off-by Marcelo Tosatti <[email protected]>
PATCH 4/16 x86: pvclock: create helper for pvclock data retrieval
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
So code can be reused.
arch/x86/kernel/pvclock.c +2 -14
@@ -26,13 +26,6 @@ void pvclock_set_flags(u8 flags) valid_flags = flags; } -static u64 pvclock_get_nsec_offset (const struct pvclock_vcpu_time_info *src) -{ -u64 delta =native_read_tsc () - src->tsc_timestamp; -return pvclock_scale_delta (delta, src->tsc_to_system_mul, - src->tsc_shift); -} -unsigned long pvclock_tsc_khz (struct pvclock_vcpu_time_info *src) {u64 pv_tsc_khz =1000000ULL <<32 ;
@@ -55,17 +48,12 @@ void pvclock_resume(void)cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) {unsigned version; -cycle_t ret, offset; +cycle_t ret;u64 last;u8 flags;do { - version = src->version; -rdtsc_barrier (); - offset =pvclock_get_nsec_offset (src); - ret = src->system_time + offset; - flags = src->flags; -rdtsc_barrier (); + version =__pvclock_read_cycles (src, &ret, &flags); }while ((src->version &1 ) || version != src->version); if ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
arch/x86/include/asm/pvclock.h +28 -0
@@ -56,4 +56,32 @@ static inline u64 pvclock_scale_delta(u6return product; } +static __always_inline +u64 pvclock_get_nsec_offset(const struct pvclock_vcpu_time_info *src) +{ +u64 delta =__native_read_tsc () - src->tsc_timestamp; +return pvclock_scale_delta (delta, src->tsc_to_system_mul, + src->tsc_shift); +} + +static __always_inline +unsigned __pvclock_read_cycles (const struct pvclock_vcpu_time_info *src, +cycle_t *cycles,u8 *flags) +{ +unsigned version; +cycle_t ret, offset; +u8 ret_flags; + + version = src->version; +rdtsc_barrier (); + offset =pvclock_get_nsec_offset (src); + ret = src->system_time + offset; + ret_flags = src->flags; +rdtsc_barrier (); + + *cycles = ret; + *flags = ret_flags; +return version; +} +#endif /* _ASM_X86_PVCLOCK_H */
Re: [patch 04/16] x86: pvclock: create helper for pvclock data retrieval
Quoted 10 lines · Marcelo Tosatti
arch/x86/include/asm/pvclock.h +10
-0+static __always_inline +unsigned __pvclock_read_cycles (const struct pvclock_vcpu_time_info *src, +cycle_t *cycles,u8 *flags) +{ +unsigned version; +cycle_t ret, offset; +u8 ret_flags; + + version = src->version; +rdtsc_barrier ();
arch/x86/include/asm/pvclock.h static inline u64 pvclock_scale_delta(u6 · line 86
Quoted 10 lines
arch/x86/include/asm/pvclock.h +10
-0+ offset =pvclock_get_nsec_offset (src); + ret = src->system_time + offset; + ret_flags = src->flags; +rdtsc_barrier (); + + *cycles = ret; + *flags = ret_flags; +return version; +} +
This interface is a bit weird.
The actual value you are interested in is "cycles", so why is it returned through the parameters? I think it would be clearer to have this return cycles, and &version as a parameter.
Re: [patch 04/16] x86: pvclock: create helper for pvclock data retrieval
Quoted 17 lines · Glauber Costa
Quoted 16 lines · Marcelo Tosatti
arch/x86/include/asm/pvclock.h +16
-0+static __always_inline +unsigned __pvclock_read_cycles (const struct pvclock_vcpu_time_info *src, +cycle_t *cycles,u8 *flags) +{ +unsigned version; +cycle_t ret, offset; +u8 ret_flags; + + version = src->version; +rdtsc_barrier (); + offset =pvclock_get_nsec_offset (src); + ret = src->system_time + offset; + ret_flags = src->flags; +rdtsc_barrier (); + + *cycles = ret;
Quoted 8 lines
arch/x86/include/asm/pvclock.h static inline u64 pvclock_scale_delta(u6 · line 86
Quoted 4 lines
arch/x86/include/asm/pvclock.h +4
-0+ *flags = ret_flags; +return version; +} +This interface is a bit weird.
The actual value you are interested in is "cycles", so why is it returned through the parameters? I think it would be clearer to have this return cycles, and &version as a parameter.
I disagree because
do {
version = pvclock_read_cycles();
} while (version != src->version);Looks fine.
PATCH 5/16 x86: pvclock: introduce helper to read flags
In reply to Marcelo Tosattiarch/x86/kernel/pvclock.c +13 -0
@@ -45,6 +45,19 @@ void pvclock_resume(void)atomic64_set (&last_value,0 ); } +u8 pvclock_read_flags (struct pvclock_vcpu_time_info *src) +{ +unsigned version; +cycle_t ret; +u8 flags; + +do { + version =__pvclock_read_cycles (src, &ret, &flags); + }while ((src->version &1 ) || version != src->version); + +return flags & valid_flags; +} +cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src) {unsigned version;
arch/x86/include/asm/pvclock.h +1 -0
@@ -6,6 +6,7 @@/* some helper functions for xen and kvm pv clock sources */ cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src); +u8 pvclock_read_flags (struct pvclock_vcpu_time_info *src);void pvclock_set_flags (u8 flags);unsigned long pvclock_tsc_khz (struct pvclock_vcpu_time_info *src); void pvclock_read_wallclock(struct pvclock_wall_clock *wall,
Re: [patch 05/16] x86: pvclock: introduce helper to read flags
Quoted 4 lines · Marcelo Tosatti
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
If you are resending this series, I don't see a reason for this one to be in a separate patch.
code itself is fine.
Re: [patch 05/16] x86: pvclock: introduce helper to read flags
Quoted 9 lines · Glauber Costa
Quoted 4 lines · Marcelo Tosatti
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.cIf you are resending this series, I don't see a reason for this one to be in a separate patch.
code itself is fine.
Its easier to review smaller patches.
PATCH 6/16 sched: add notifier for cross-cpu migrations
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
include/linux/sched.h +8 -0
@@ -107,6 +107,14 @@ extern unsigned long this_cpu_load(void)extern void calc_global_load (unsigned long ticks);extern void update_cpu_load_nohz (void ); +/* Notifier for when a task gets migrated to a new CPU */ +struct task_migration_notifier { +struct task_struct *task; +int from_cpu; +int to_cpu; +}; +extern void register_task_migration_notifier (struct notifier_block *n); +extern unsigned long get_parent_ip (unsigned long addr);struct seq_file ;
kernel/sched/core.c +15 -0
@@ -922,6 +922,13 @@ void check_preempt_curr(struct rq *rq, s rq->skip_clock_update =1 ; } +static ATOMIC_NOTIFIER_HEAD (task_migration_notifier); + +void register_task_migration_notifier (struct notifier_block *n) +{ +atomic_notifier_chain_register (&task_migration_notifier, n); +} +#ifdef CONFIG_SMP void set_task_cpu (struct task_struct *p,unsigned int new_cpu) {
@@ -952,8 +959,16 @@ void set_task_cpu(struct task_struct *p,trace_sched_migrate_task (p, new_cpu);if (task_cpu (p) != new_cpu) { +struct task_migration_notifier tmn; + p->se.nr_migrations++;perf_sw_event (PERF_COUNT_SW_CPU_MIGRATIONS ,1 ,NULL ,0 ); + + tmn.task = p; + tmn.from_cpu =task_cpu (p); + tmn.to_cpu = new_cpu; + +atomic_notifier_call_chain (&task_migration_notifier,0 , &tmn); }__set_task_cpu (p, new_cpu);
Re: [patch 06/16] sched: add notifier for cross-cpu migrations
Quoted 4 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
Signed-off-by Marcelo Tosatti <[email protected]>
Please collect peterz's ack for this one.
Quoted 56 lines
Index:
vsyscall/include/linux/sched.h
===================================================================include/linux/sched.h +8
-0@@ -107,6 +107,14 @@ extern unsigned long this_cpu_load(void)extern void calc_global_load (unsigned long ticks);extern void update_cpu_load_nohz (void ); +/* Notifier for when a task gets migrated to a new CPU */ +struct task_migration_notifier { +struct task_struct *task; +int from_cpu; +int to_cpu; +}; +extern void register_task_migration_notifier (struct notifier_block *n); +extern unsigned long get_parent_ip (unsigned long addr);struct seq_file ;Index:
vsyscall/kernel/sched/core.c
===================================================================kernel/sched/core.c +15
-0@@ -922,6 +922,13 @@ void check_preempt_curr(struct rq *rq, s rq->skip_clock_update =1 ; } +static ATOMIC_NOTIFIER_HEAD (task_migration_notifier); + +void register_task_migration_notifier (struct notifier_block *n) +{ +atomic_notifier_chain_register (&task_migration_notifier, n); +} +#ifdef CONFIG_SMP void set_task_cpu (struct task_struct *p,unsigned int new_cpu) {@@ -952,8 +959,16 @@ void set_task_cpu(struct task_struct *p,trace_sched_migrate_task (p, new_cpu);if (task_cpu (p) != new_cpu) { +struct task_migration_notifier tmn; + p->se.nr_migrations++;perf_sw_event (PERF_COUNT_SW_CPU_MIGRATIONS ,1 ,NULL ,0 ); + + tmn.task = p; + tmn.from_cpu =task_cpu (p); + tmn.to_cpu = new_cpu; + +atomic_notifier_call_chain (&task_migration_notifier,0 , &tmn); }__set_task_cpu (p, new_cpu);
PATCH 7/16 x86: pvclock: generic pvclock vsyscall initialization
In reply to Marcelo TosattiOriginally from Jeremy Fitzhardinge.
Introduce generic, non hypervisor specific, pvclock initialization routines.
arch/x86/kernel/pvclock.c +68 -0
@@ -17,6 +17,10 @@#include <linux/kernel.h> #include <linux/percpu.h> +#include <linux/notifier.h> +#include <linux/sched.h> +#include <linux/gfp.h> +#include <linux/bootmem.h> #include <asm/pvclock.h> static u8 valid_flags__read_mostly =0 ;
@@ -122,3 +126,67 @@ void pvclock_read_wallclock(struct pvcloset_normalized_timespec (ts, now.tv_sec, now.tv_nsec); } + +static aligned_pvti_t *pvclock_vdso_info; + +static struct pvclock_vsyscall_time_info * +pvclock_get_vsyscall_user_time_info (int cpu) +{ +if (pvclock_vdso_info ==NULL ) { +BUG (); +return NULL ; + } + +return &pvclock_vdso_info[cpu].info; +} + +struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info (int cpu) +{ +return &pvclock_get_vsyscall_user_time_info (cpu)->pvti; +} + +int pvclock_task_migrate (struct notifier_block *nb,unsigned long l,void *v) +{ +struct task_migration_notifier *mn = v; +struct pvclock_vsyscall_time_info *pvti; + + pvti =pvclock_get_vsyscall_user_time_info (mn->from_cpu); + +if (pvti ==NULL ) +return NOTIFY_DONE ; + + pvti->migrate_count++; + +return NOTIFY_DONE ; +} + +static struct notifier_block pvclock_migrate = { + .notifier_call = pvclock_task_migrate, +}; + +/* +* Initialize the generic pvclock vsyscall state. This will allocate +* a/some page(s) for the per-vcpu pvclock information, set up a +* fixmap mapping for the page(s) +*/ +int __init pvclock_init_vsyscall (void ) +{ +int idx; +unsigned int size =PVCLOCK_VSYSCALL_NR_PAGES *PAGE_SIZE ; + + pvclock_vdso_info =__alloc_bootmem (size,PAGE_SIZE ,0 ); +if (!pvclock_vdso_info) +return -ENOMEM ; + +memset (pvclock_vdso_info,0 , size); + +for (idx =0 ; idx <= (PVCLOCK_FIXMAP_END -PVCLOCK_FIXMAP_BEGIN ); idx++) { +__set_fixmap (PVCLOCK_FIXMAP_BEGIN + idx, +__pa_symbol (pvclock_vdso_info) + (idx*PAGE_SIZE ), +PAGE_KERNEL_VVAR ); + } + +register_task_migration_notifier (&pvclock_migrate); + +return 0 ; +}
arch/x86/include/asm/fixmap.h +5 -0
@@ -19,6 +19,7 @@#include <asm/acpi.h> #include <asm/apicdef.h> #include <asm/page.h> +#include <asm/pvclock.h> #ifdef CONFIG_X86_32 #include <linux/threads.h> #include <asm/kmap_types.h>
@@ -81,6 +82,10 @@ enum fixed_addresses { VVAR_PAGE, VSYSCALL_HPET,#endif +#ifdef CONFIG_PARAVIRT_CLOCK +PVCLOCK_FIXMAP_BEGIN , +PVCLOCK_FIXMAP_END =PVCLOCK_FIXMAP_BEGIN +PVCLOCK_VSYSCALL_NR_PAGES -1 , +#endifFIX_DBGP_BASE ,FIX_EARLYCON_MEM_BASE , #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
arch/x86/include/asm/pvclock.h +15 -0
@@ -13,6 +13,8 @@ void pvclock_read_wallclock(struct pvclostruct pvclock_vcpu_time_info *vcpu, struct timespec *ts);void pvclock_resume (void ); +int __init pvclock_init_vsyscall (void ); +struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info (int cpu);/* * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
@@ -85,4 +87,17 @@ unsigned __pvclock_read_cycles(const str return version; } +struct pvclock_vsyscall_time_info { +struct pvclock_vcpu_time_info pvti; +u32 migrate_count; +}; + +typedef union { +struct pvclock_vsyscall_time_info info; +char pad[SMP_CACHE_BYTES ]; +}aligned_pvti_t ____cacheline_aligned ; + +#define PVTI_SIZE sizeof(aligned_pvti_t) +#define PVCLOCK_VSYSCALL_NR_PAGES (((NR_CPUS-1)/(PAGE_SIZE/PVTI_SIZE))+1) +#endif /* _ASM_X86_PVCLOCK_H */
arch/x86/include/asm/clocksource.h +1 -0
@@ -8,6 +8,7 @@#define VCLOCK_NONE 0/* No vDSO clock available. */ #define VCLOCK_TSC 1/* vDSO should use vread_tsc. */ #define VCLOCK_HPET 2/* vDSO should use vread_hpet. */ +#define VCLOCK_PVCLOCK 3/* vDSO should use vread_pvclock. */ struct arch_clocksource_data {int vclock_mode;
Re: [patch 07/16] x86: pvclock: generic pvclock vsyscall initialization
Quoted 23 lines · Marcelo Tosatti
Originally from Jeremy Fitzhardinge.
Introduce generic, non hypervisor specific, pvclock initialization routines.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/kernel/pvclock.c
===================================================================arch/x86/kernel/pvclock.c +4
-0@@ -17,6 +17,10 @@#include <linux/kernel.h> #include <linux/percpu.h> +#include <linux/notifier.h> +#include <linux/sched.h> +#include <linux/gfp.h> +#include <linux/bootmem.h> #include <asm/pvclock.h> static u8 valid_flags__read_mostly =0 ;
arch/x86/kernel/pvclock.c void pvclock_read_wallclock(struct pvclo · line 130
Quoted 5 lines
arch/x86/kernel/pvclock.c +2
-0@@ -122,3 +126,67 @@ void pvclock_read_wallclock(struct pvcloset_normalized_timespec (ts, now.tv_sec, now.tv_nsec); } + +static aligned_pvti_t *pvclock_vdso_info;
is there a non-aligned pvti ?
I would also add that "pvti" is not exactly the most descriptive name I've seen.
arch/x86/kernel/pvclock.c void pvclock_read_wallclock(struct pvclo · line 142
Quoted 12 lines
arch/x86/kernel/pvclock.c +12
-0+ +static struct pvclock_vsyscall_time_info * +pvclock_get_vsyscall_user_time_info (int cpu) +{ +if (pvclock_vdso_info ==NULL ) { +BUG (); +return NULL ; + } + +return &pvclock_vdso_info[cpu].info; +} +
if (!pvclock_vdso_info)
BUG();
return &pvclock...
Quoted 5 lines
arch/x86/kernel/pvclock.c +5
-0+struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info (int cpu) +{ +return &pvclock_get_vsyscall_user_time_info (cpu)->pvti; +} +
arch/x86/kernel/pvclock.c void pvclock_read_wallclock(struct pvclo · line 157
Quoted 10 lines
arch/x86/kernel/pvclock.c +10
-0+int pvclock_task_migrate (struct notifier_block *nb,unsigned long l,void *v) +{ +struct task_migration_notifier *mn = v; +struct pvclock_vsyscall_time_info *pvti; + + pvti =pvclock_get_vsyscall_user_time_info (mn->from_cpu); + +if (pvti ==NULL ) +return NOTIFY_DONE ; +
When is it NULL? IIUC, this is when the vsyscall is disabled, right? Would you mind adding comments for it?
Also, this is supposed to be an unlikely branch, right?
Quoted 10 lines
arch/x86/kernel/pvclock.c +10
-0+ pvti->migrate_count++; + +return NOTIFY_DONE ; +} + +static struct notifier_block pvclock_migrate = { + .notifier_call = pvclock_task_migrate, +}; + +/*
arch/x86/kernel/pvclock.c void pvclock_read_wallclock(struct pvclo · line 177
Quoted 10 lines
arch/x86/kernel/pvclock.c +10
-0+* Initialize the generic pvclock vsyscall state. This will allocate +* a/some page(s) for the per-vcpu pvclock information, set up a +* fixmap mapping for the page(s) +*/ +int __init pvclock_init_vsyscall (void ) +{ +int idx; +unsigned int size =PVCLOCK_VSYSCALL_NR_PAGES *PAGE_SIZE ; + + pvclock_vdso_info =__alloc_bootmem (size,PAGE_SIZE ,0 );
This will be the most critical path for reading pvclock's time data. So why can't we:
1) Make it per-node.
2) Of course, as a consequence, make sure all info structures in the same page are in the same node ?
Quoted 54 lines
arch/x86/kernel/pvclock.c +15
-0+if (!pvclock_vdso_info) +return -ENOMEM ; + +memset (pvclock_vdso_info,0 , size); + +for (idx =0 ; idx <= (PVCLOCK_FIXMAP_END -PVCLOCK_FIXMAP_BEGIN ); idx++) { +__set_fixmap (PVCLOCK_FIXMAP_BEGIN + idx, +__pa_symbol (pvclock_vdso_info) + (idx*PAGE_SIZE ), +PAGE_KERNEL_VVAR ); + } + +register_task_migration_notifier (&pvclock_migrate); + +return 0 ; +}Index:
vsyscall/arch/x86/include/asm/fixmap.h
===================================================================arch/x86/include/asm/fixmap.h +5
-0@@ -19,6 +19,7 @@#include <asm/acpi.h> #include <asm/apicdef.h> #include <asm/page.h> +#include <asm/pvclock.h> #ifdef CONFIG_X86_32 #include <linux/threads.h> #include <asm/kmap_types.h> @@ -81,6 +82,10 @@ enum fixed_addresses { VVAR_PAGE, VSYSCALL_HPET,#endif +#ifdef CONFIG_PARAVIRT_CLOCK +PVCLOCK_FIXMAP_BEGIN , +PVCLOCK_FIXMAP_END =PVCLOCK_FIXMAP_BEGIN +PVCLOCK_VSYSCALL_NR_PAGES -1 , +#endifFIX_DBGP_BASE ,FIX_EARLYCON_MEM_BASE , #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INITIndex:
vsyscall/arch/x86/include/asm/pvclock.h
===================================================================arch/x86/include/asm/pvclock.h +2
-0@@ -13,6 +13,8 @@ void pvclock_read_wallclock(struct pvclostruct pvclock_vcpu_time_info *vcpu, struct timespec *ts);void pvclock_resume (void ); +int __init pvclock_init_vsyscall (void ); +struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info (int cpu);/* * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, @@ -85,4 +87,17 @@ unsigned __pvclock_read_cycles(const strreturn version; }
arch/x86/include/asm/pvclock.h unsigned __pvclock_read_cycles(const str · line 98
Quoted 10 lines
arch/x86/include/asm/pvclock.h +9
-0+struct pvclock_vsyscall_time_info { +struct pvclock_vcpu_time_info pvti; +u32 migrate_count; +}; + +typedef union { +struct pvclock_vsyscall_time_info info; +char pad[SMP_CACHE_BYTES ]; +}aligned_pvti_t ____cacheline_aligned ;
Please, just align pvclock_vsyscall_time_info. It is 10 thousand times more descriptive.
PATCH 8/16 KVM: x86: introduce facility to support vsyscall pvclock, via MSR
In reply to Marcelo TosattiAllow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by MSR_KVM_SYSTEM_TIME_NEW). 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.
arch/x86/include/asm/kvm_para.h +2 -0
@@ -23,6 +23,7 @@#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_USERSPACE_CLOCKSOURCE 7/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored.
@@ -39,6 +40,7 @@#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03#define MSR_KVM_PV_EOI_EN 0x4b564d04 +#define MSR_KVM_USERSPACE_TIME 0x4b564d05struct kvm_steal_time {__u64 steal;
Documentation/virtual/kvm/msr.txt +16 -0
@@ -125,6 +125,22 @@ MSR_KVM_SYSTEM_TIME_NEW: 0x4b564d01 Availability of this MSR must be checked via bit 3 in 0x4000001 cpuid leaf prior to usage. +MSR_KVM_USERSPACE_TIME: 0x4b564d05 + +Allow a guest to register a second location for the VCPU time info +structure for each vcpu (as described by MSR_KVM_SYSTEM_TIME_NEW). +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. + +Relationship with master copy (MSR_KVM_SYSTEM_TIME_NEW): + +- This MSR must be enabled only when the master is enabled. +- Disabling updates to the master automatically disables +updates for this copy. + +Availability of this MSR must be checked via bit 7 in 0x4000001 cpuid +leaf prior to usage. MSR_KVM_WALL_CLOCK: 0x11
arch/x86/include/asm/kvm_host.h +3 -0
@@ -415,10 +415,13 @@ struct kvm_vcpu_arch {int (*complete_userspace_io)(struct kvm_vcpu *vcpu);gpa_t time; +gpa_t uspace_time;struct pvclock_vcpu_time_info hv_clock;unsigned int hw_tsc_khz;unsigned int time_offset; +unsigned int uspace_time_offset;struct page *time_page; +struct page *uspace_time_page;/* set guest stopped flag in pvclock flags field */ bool pvclock_set_guest_stopped_request;
arch/x86/kvm/x86.c +81 -26
182 lines not on this page · raw message
arch/x86/kvm/cpuid.c +3 -1
@@ -411,7 +411,9 @@ static int do_cpuid_ent(struct kvm_cpuid (1 <<KVM_FEATURE_CLOCKSOURCE2 ) | (1 <<KVM_FEATURE_ASYNC_PF ) | (1 <<KVM_FEATURE_PV_EOI ) | - (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ); + (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ) | + (1 <<KVM_FEATURE_USERSPACE_CLOCKSOURCE ); +if (sched_info_on ()) entry->eax |= (1 <<KVM_FEATURE_STEAL_TIME );
include/uapi/linux/kvm.h +1 -0
@@ -626,6 +626,7 @@ struct kvm_ppc_smmu_info {#define KVM_CAP_READONLY_MEM 81#endif #define KVM_CAP_IRQFD_RESAMPLE 82 +#define KVM_CAP_USERSPACE_CLOCKSOURCE 83#ifdef KVM_CAP_IRQ_ROUTING
Re: [patch 08/16] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 9 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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 Marcelo Tosatti <[email protected]>
Changelog doesn't make a lot of sense. (specially from first line to the second). Please add in here the reasons why we can't (or decided not to) use the same page. The info in the last mail thread is good enough, just put it here.
Quoted 13 lines
Index:
vsyscall/arch/x86/include/asm/kvm_para.h
===================================================================arch/x86/include/asm/kvm_para.h +1
-0@@ -23,6 +23,7 @@#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_USERSPACE_CLOCKSOURCE 7/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored.
arch/x86/include/asm/kvm_para.h line 44
Quoted 5 lines
arch/x86/include/asm/kvm_para.h +1
-0@@ -39,6 +40,7 @@#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03#define MSR_KVM_PV_EOI_EN 0x4b564d04 +#define MSR_KVM_USERSPACE_TIME 0x4b564d05
I accept that it is possible that we may be better off with the page mapped twice.
But why do we need an extra MSR? When, and why, would you enable the kernel-based pvclock, but disabled the userspace pvclock?
I believe only the existing MSRs should be used for this. If you write to it, and the host is capable of exporting userspace pvclock information, then it does. If it isn't, then it doesn't.
No reason for the extra setup that is only likely to bring more headache.
arch/x86/kvm/cpuid.c static int do_cpuid_ent(struct kvm_cpuid · line 417
Quoted 13 lines
Index:
vsyscall/arch/x86/kvm/cpuid.c
===================================================================arch/x86/kvm/cpuid.c +3
-1@@ -411,7 +411,9 @@ static int do_cpuid_ent(struct kvm_cpuid (1 <<KVM_FEATURE_CLOCKSOURCE2 ) | (1 <<KVM_FEATURE_ASYNC_PF ) | (1 <<KVM_FEATURE_PV_EOI ) | - (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ); + (1 <<KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ) | + (1 <<KVM_FEATURE_USERSPACE_CLOCKSOURCE ); +
The feature bit itself, is obviously fine.
Re: [patch 08/16] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.
Quoted 7 lines
Quoted 2 lines
Signed-off-by Marcelo Tosatti <[email protected]>Changelog doesn't make a lot of sense. (specially from first line to the second). Please add in here the reasons why we can't (or decided not to) use the same page. The info in the last mail thread is good enough, just put it here.
Fixed.
Quoted 22 lines
Quoted 13 lines
Index:
vsyscall/arch/x86/include/asm/kvm_para.h
===================================================================arch/x86/include/asm/kvm_para.h +1
-0@@ -23,6 +23,7 @@#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_USERSPACE_CLOCKSOURCE 7/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. arch/x86/include/asm/kvm_para.h line 44
Quoted 5 lines
arch/x86/include/asm/kvm_para.h +1
-0@@ -39,6 +40,7 @@#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03#define MSR_KVM_PV_EOI_EN 0x4b564d04 +#define MSR_KVM_USERSPACE_TIME 0x4b564d05I accept that it is possible that we may be better off with the page mapped twice.
Quoted 2 lines
But why do we need an extra MSR? When, and why, would you enable the kernel-based pvclock, but disabled the userspace pvclock?
Because there is no stable TSC available, for example (which cannot be used to measure passage of time).
Quoted 5 lines
I believe only the existing MSRs should be used for this. If you write to it, and the host is capable of exporting userspace pvclock information, then it does. If it isn't, then it doesn't.
No reason for the extra setup that is only likely to bring more headache.
It is necessary.
Re: [patch 08/16] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 41 lines · Marcelo Tosatti
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 7 lines
Quoted 2 lines
Signed-off-by Marcelo Tosatti <[email protected]>Changelog doesn't make a lot of sense. (specially from first line to the second). Please add in here the reasons why we can't (or decided not to) use the same page. The info in the last mail thread is good enough, just put it here.
Fixed.
Quoted 22 lines
Quoted 13 lines
Index:
vsyscall/arch/x86/include/asm/kvm_para.h
===================================================================arch/x86/include/asm/kvm_para.h +1
-0@@ -23,6 +23,7 @@#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_USERSPACE_CLOCKSOURCE 7/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. arch/x86/include/asm/kvm_para.h line 44
Quoted 5 lines
arch/x86/include/asm/kvm_para.h +1
-0@@ -39,6 +40,7 @@#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03#define MSR_KVM_PV_EOI_EN 0x4b564d04 +#define MSR_KVM_USERSPACE_TIME 0x4b564d05I accept that it is possible that we may be better off with the page mapped twice.
Quoted 6 lines
Quoted 2 lines
But why do we need an extra MSR? When, and why, would you enable the kernel-based pvclock, but disabled the userspace pvclock?
Because there is no stable TSC available, for example (which cannot be used to measure passage of time).
What you say is true, but completely unrelated. I am not talking about situations in which userspace pvclock is available and you end up not using it.
I am talking about situations in which it is available, you are capable of using it, but then decides for some reason to permanently disabled - as in not setting it up altogether.
It seems to me that if the host has code to deal with userspace pvclock, and you already coded the guest in a way that you may or may not use it (dependent on the value of the stable bit), you could very well only check for the cpuid flag, and do the guest setup if available - skipping this MSR dance altogether.
Now, of course, there is the problem of communicating the address in which the guest expects the page to be. Skipping the MSR setup would require it to be more or less at a fixed location. We could in principle lay them down together with the already existing pvclock structure. (But granted, I am not sure it is worth it...)
I think in general, this question deserves a bit more of attention. We are about to have just the perfect opportunity for this next week, so let's use it.
Re: [patch 08/16] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 69 lines · Glauber Costa
Quoted 41 lines · Marcelo Tosatti
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 7 lines
Quoted 2 lines
Signed-off-by Marcelo Tosatti <[email protected]>Changelog doesn't make a lot of sense. (specially from first line to the second). Please add in here the reasons why we can't (or decided not to) use the same page. The info in the last mail thread is good enough, just put it here.
Fixed.
Quoted 22 lines
Quoted 13 lines
Index:
vsyscall/arch/x86/include/asm/kvm_para.h
===================================================================arch/x86/include/asm/kvm_para.h +1
-0@@ -23,6 +23,7 @@#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_USERSPACE_CLOCKSOURCE 7/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. arch/x86/include/asm/kvm_para.h line 44
Quoted 5 lines
arch/x86/include/asm/kvm_para.h +1
-0@@ -39,6 +40,7 @@#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03#define MSR_KVM_PV_EOI_EN 0x4b564d04 +#define MSR_KVM_USERSPACE_TIME 0x4b564d05I accept that it is possible that we may be better off with the page mapped twice.
Quoted 6 lines
Quoted 2 lines
But why do we need an extra MSR? When, and why, would you enable the kernel-based pvclock, but disabled the userspace pvclock?
Because there is no stable TSC available, for example (which cannot be used to measure passage of time).
What you say is true, but completely unrelated. I am not talking about situations in which userspace pvclock is available and you end up not using it.
I am talking about situations in which it is available, you are capable of using it, but then decides for some reason to permanently disabled - as in not setting it up altogether.
It seems to me that if the host has code to deal with userspace pvclock, and you already coded the guest in a way that you may or may not use it (dependent on the value of the stable bit), you could very well only check for the cpuid flag, and do the guest setup if available - skipping this MSR dance altogether.
Now, of course, there is the problem of communicating the address in which the guest expects the page to be. Skipping the MSR setup would require it to be more or less at a fixed location. We could in principle lay them down together with the already existing pvclock structure. (But granted, I am not sure it is worth it...)
Quoted 3 lines
I think in general, this question deserves a bit more of attention. We are about to have just the perfect opportunity for this next week, so let's use it.
In essence you are proposing a different interface to communicate the "userspace vsyscall pvclock area", other than the MSR, right?
If so:
1. What is the problem with the MSR interface.
2. What advantage this new interface (which honestly i do not understand) provides?
Re: [patch 08/16] KVM: x86: introduce facility to support vsyscall pvclock, via MSR
Quoted 74 lines · Marcelo Tosatti
Quoted 69 lines · Glauber Costa
Quoted 41 lines · Marcelo Tosatti
Quoted 8 lines · Glauber Costa
Quoted 7 lines · Marcelo Tosatti
Allow a guest to register a second location for the VCPU time info
structure for each vcpu (as described by
MSR_KVM_SYSTEM_TIME_NEW). 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.Quoted 7 lines
Quoted 2 lines
Signed-off-by Marcelo Tosatti <[email protected]>Changelog doesn't make a lot of sense. (specially from first line to the second). Please add in here the reasons why we can't (or decided not to) use the same page. The info in the last mail thread is good enough, just put it here.
Fixed.
Quoted 22 lines
Quoted 13 lines
Index:
vsyscall/arch/x86/include/asm/kvm_para.h
===================================================================arch/x86/include/asm/kvm_para.h +1
-0@@ -23,6 +23,7 @@#define KVM_FEATURE_ASYNC_PF 4#define KVM_FEATURE_STEAL_TIME 5#define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_USERSPACE_CLOCKSOURCE 7/* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. arch/x86/include/asm/kvm_para.h line 44
Quoted 5 lines
arch/x86/include/asm/kvm_para.h +1
-0@@ -39,6 +40,7 @@#define MSR_KVM_ASYNC_PF_EN 0x4b564d02#define MSR_KVM_STEAL_TIME 0x4b564d03#define MSR_KVM_PV_EOI_EN 0x4b564d04 +#define MSR_KVM_USERSPACE_TIME 0x4b564d05I accept that it is possible that we may be better off with the page mapped twice.
Quoted 6 lines
Quoted 2 lines
But why do we need an extra MSR? When, and why, would you enable the kernel-based pvclock, but disabled the userspace pvclock?
Because there is no stable TSC available, for example (which cannot be used to measure passage of time).
What you say is true, but completely unrelated. I am not talking about situations in which userspace pvclock is available and you end up not using it.
I am talking about situations in which it is available, you are capable of using it, but then decides for some reason to permanently disabled - as in not setting it up altogether.
It seems to me that if the host has code to deal with userspace pvclock, and you already coded the guest in a way that you may or may not use it (dependent on the value of the stable bit), you could very well only check for the cpuid flag, and do the guest setup if available - skipping this MSR dance altogether.
Now, of course, there is the problem of communicating the address in which the guest expects the page to be. Skipping the MSR setup would require it to be more or less at a fixed location. We could in principle lay them down together with the already existing pvclock structure. (But granted, I am not sure it is worth it...)
Quoted 3 lines
I think in general, this question deserves a bit more of attention. We are about to have just the perfect opportunity for this next week, so let's use it.
Quoted 9 lines
In essence you are proposing a different interface to communicate the "userspace vsyscall pvclock area", other than the MSR, right?
If so:
1. What is the problem with the MSR interface.
2. What advantage this new interface (which honestly i do not understand) provides?
No, I am not proposing a different interface to communicate this. I am proposing *no* interface to communicate this.
I'll give that if it is absolutely necessary to have it on a random address, and you need to pass it back and forth, of course MSRs would be the choice. But I am not yet terribly convinced that all the pain of syncing this from userspace, migrating those values, bookkeeping what is on, what is not, is worth the pain.
PATCH 9/16 x86: kvm guest: pvclock vsyscall support
In reply to Marcelo TosattiAllow hypervisor to update userspace visible copy of pvclock data.
arch/x86/kernel/kvmclock.c +51 -0
@@ -31,6 +31,9 @@ static int kvmclock = 1;static int msr_kvm_system_time =MSR_KVM_SYSTEM_TIME ;static int msr_kvm_wall_clock =MSR_KVM_WALL_CLOCK ; +/* set when the generic vsyscall pvclock elements are setup */ +bool vsyscall_clock_initializable = false; +static int parse_no_kvmclock (char *arg) { kvmclock =0 ;
@@ -151,6 +154,24 @@ int kvm_register_clock(char *txt)return ret; } +static int kvm_register_vsyscall_clock (char *txt) +{ +int cpu =smp_processor_id (); +int low, high, ret; +struct pvclock_vcpu_time_info *info; + + info =pvclock_get_vsyscall_time_info (cpu); + + low = (int )__pa (info) |1 ; + high = ((u64 )__pa (&per_cpu (hv_clock, cpu)) >>32 ); + ret =native_write_msr_safe (MSR_KVM_USERSPACE_TIME , low, high); +printk (KERN_INFO "kvm-clock: cpu %d, msr %x:%x, %s\n" , + cpu, high, low, txt); + +return ret; +} + +static void kvm_save_sched_clock_state (void ) { }
@@ -158,6 +179,8 @@ static void kvm_save_sched_clock_state(vstatic void kvm_restore_sched_clock_state (void ) {kvm_register_clock ("primary cpu clock, resume" ); +if (vsyscall_clock_initializable) +kvm_register_vsyscall_clock ("primary cpu vsyscall clock, resume" ); }#ifdef CONFIG_X86_LOCAL_APIC
@@ -168,6 +191,8 @@ static void __cpuinit kvm_setup_secondar* we shouldn't fail. */ WARN_ON (kvm_register_clock ("secondary cpu clock" )); +if (vsyscall_clock_initializable) + kvm_register_vsyscall_clock("secondary cpu vsyscall clock"); }#endif
@@ -182,6 +207,8 @@ static void __cpuinit kvm_setup_secondar#ifdef CONFIG_KEXEC static void kvm_crash_shutdown (struct pt_regs *regs) { +if (vsyscall_clock_initializable) +native_write_msr (MSR_KVM_USERSPACE_TIME ,0 ,0 );native_write_msr (msr_kvm_system_time,0 ,0 );kvm_disable_steal_time ();native_machine_crash_shutdown (regs);
@@ -190,6 +217,8 @@ static void kvm_crash_shutdown(struct ptstatic void kvm_shutdown (void ) { +if (vsyscall_clock_initializable) +native_write_msr (MSR_KVM_USERSPACE_TIME ,0 ,0 );native_write_msr (msr_kvm_system_time,0 ,0 );kvm_disable_steal_time ();native_machine_shutdown ();
@@ -233,3 +262,25 @@ void __init kvmclock_init(void)if (kvm_para_has_feature (KVM_FEATURE_CLOCKSOURCE_STABLE_BIT ))pvclock_set_flags (PVCLOCK_TSC_STABLE_BIT ); } + +int kvm_setup_vsyscall_timeinfo (void ) +{ +int ret; +struct pvclock_vcpu_time_info *vcpu_time; +u8 flags; + + vcpu_time = &get_cpu_var (hv_clock); + flags =pvclock_read_flags (vcpu_time); +put_cpu_var (hv_clock); + +if (!(flags &PVCLOCK_TSC_STABLE_BIT )) +return 1 ; + +if ((ret =pvclock_init_vsyscall ())) +return ret; + + kvm_clock.archdata.vclock_mode =VCLOCK_PVCLOCK ; + vsyscall_clock_initializable = true; +return 0 ; +} +
arch/x86/kernel/kvm.c +14 -0
@@ -42,6 +42,7 @@#include <asm/apic.h> #include <asm/apicdef.h> #include <asm/hypervisor.h> +#include <asm/kvm_guest.h> static int kvmapf =1 ;
@@ -62,6 +63,15 @@ static int parse_no_stealacc(char *arg)early_param ("no-steal-acc" , parse_no_stealacc); +static int kvmclock_vsyscall =1 ; +static int parse_no_kvmclock_vsyscall (char *arg) +{ + kvmclock_vsyscall =0 ; +return 0 ; +} + +early_param ("no-kvmclock-vsyscall" , parse_no_kvmclock_vsyscall); + static DEFINE_PER_CPU(struct kvm_vcpu_pv_apf_data, apf_reason) __aligned(64); static DEFINE_PER_CPU(struct kvm_steal_time, steal_time) __aligned(64);static int has_steal_clock =0 ;
@@ -468,6 +478,10 @@ void __init kvm_guest_init(void)if (kvm_para_has_feature (KVM_FEATURE_PV_EOI ))apic_set_eoi_write (kvm_guest_apic_eoi_write); +if (kvm_para_has_feature (KVM_FEATURE_USERSPACE_CLOCKSOURCE ) + && kvmclock_vsyscall) +kvm_setup_vsyscall_timeinfo (); +#ifdef CONFIG_SMP smp_ops.smp_prepare_boot_cpu = kvm_smp_prepare_boot_cpu;register_cpu_notifier (&kvm_cpu_notifier);
arch/x86/include/asm/kvm_guest.h new file +8 -0
@@ -0,0 +1,8 @@ +#ifndef _ASM_X86_KVM_GUEST_H +#define _ASM_X86_KVM_GUEST_H + +extern bool vsyscall_clock_initializable; + +int kvm_setup_vsyscall_timeinfo (void ); + +#endif /* _ASM_X86_KVM_GUEST_H */
Re: [patch 09/16] x86: kvm guest: pvclock vsyscall support
arch/x86/kernel/kvmclock.c int kvm_register_clock(char *txt) · line 170
Quoted 8 lines · Marcelo Tosatti
arch/x86/kernel/kvmclock.c +8
-0+ info =pvclock_get_vsyscall_time_info (cpu); + + low = (int )__pa (info) |1 ; + high = ((u64 )__pa (&per_cpu (hv_clock, cpu)) >>32 ); + ret =native_write_msr_safe (MSR_KVM_USERSPACE_TIME , low, high); +printk (KERN_INFO "kvm-clock: cpu %d, msr %x:%x, %s\n" , + cpu, high, low, txt); +
Why do you put info in the lower half, and the hv_clock in the higher half ?
Re: [patch 09/16] x86: kvm guest: pvclock vsyscall support
Quoted 11 lines · Glauber Costa
arch/x86/kernel/kvmclock.c int kvm_register_clock(char *txt) · line 170
Quoted 8 lines · Marcelo Tosatti
arch/x86/kernel/kvmclock.c +8
-0+ info =pvclock_get_vsyscall_time_info (cpu); + + low = (int )__pa (info) |1 ; + high = ((u64 )__pa (&per_cpu (hv_clock, cpu)) >>32 ); + ret =native_write_msr_safe (MSR_KVM_USERSPACE_TIME , low, high); +printk (KERN_INFO "kvm-clock: cpu %d, msr %x:%x, %s\n" , + cpu, high, low, txt); +Why do you put info in the lower half, and the
hv_clockin the higher half ?
Copy&paste bug. Fixed, thanks.
PATCH 10/16 x86: vdso: pvclock gettime support
In reply to Marcelo TosattiImprove performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
arch/x86/vdso/vclock_gettime.c +70 -3
@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))
@@ -62,6 +63,70 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} + +static notrace cycle_t vread_pvclock(int *mode) +{ +const struct pvclock_vsyscall_time_info *pvti; +cycle_t ret; +u64 last; +u32 version; +u32 migrate_count; +u8 flags; +unsigned cpu, cpu1; + + +/* +* When looping to get a consistent (time-info, tsc) pair, we +* also need to deal with the possibility we can switch vcpus, +* so make sure we always re-fetch time-info for the current vcpu. +*/ +do { + cpu =__getcpu () &VGETCPU_CPU_MASK ; + pvti =get_pvti (cpu); + + migrate_count = pvti->migrate_count; + + version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); + +/* +* Test we're still on the cpu as well as the version. +* We could have been migrated just after the first +* vgetcpu but before fetching the version, so we +* wouldn't notice a version change. +*/ + cpu1 =__getcpu () &VGETCPU_CPU_MASK ; + }while (unlikely (cpu != cpu1 || + (pvti->pvti.version &1 ) || + pvti->pvti.version != version || + pvti->migrate_count != migrate_count)); + +if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + *mode =VCLOCK_NONE ; + +/* refer to tsc.c read_tsc() comment for rationale */ + last =VVAR (vsyscall_gtod_data).clock.cycle_last; + +if (likely (ret >= last)) +return ret; + +return last; +} +#endif + notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) {long ret;
@@ -80,7 +145,7 @@ notrace static long vdso_fallback_gtod(s } -notrace static inline u64 vgetsns(void) +notrace static inline u64 vgetsns(int *mode) {long v;cycles_t cycles;
@@ -88,6 +153,8 @@ notrace static inline u64 vgetsns(void) cycles =vread_tsc (); else if (gtod->clock.vclock_mode == VCLOCK_HPET) cycles =vread_hpet (); + else if (gtod->clock.vclock_mode == VCLOCK_PVCLOCK) + cycles =vread_pvclock (mode); else return 0; v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask;
@@ -107,7 +174,7 @@ notrace static int __always_inline do_re mode = gtod->clock.vclock_mode; ts->tv_sec = gtod->wall_time_sec; ns = gtod->wall_time_snsec; - ns +=vgetsns (); + ns +=vgetsns (&mode); ns >>= gtod->clock.shift; }while (unlikely (read_seqcount_retry (>od->seq, seq)));
@@ -127,7 +194,7 @@ notrace static int do_monotonic(struct t mode = gtod->clock.vclock_mode; ts->tv_sec = gtod->monotonic_time_sec; ns = gtod->monotonic_time_snsec; - ns +=vgetsns (); + ns +=vgetsns (&mode); ns >>= gtod->clock.shift; }while (unlikely (read_seqcount_retry (>od->seq, seq)));timespec_add_ns (ts, ns);
arch/x86/include/asm/vsyscall.h +17 -0
@@ -33,6 +33,23 @@ extern void map_vsyscall(void);*/ extern bool emulate_vsyscall (struct pt_regs *regs,unsigned long address); +#define VGETCPU_CPU_MASK 0xfff + +static inline unsigned int __getcpu (void ) +{ +unsigned int p; + +if (VVAR (vgetcpu_mode) ==VGETCPU_RDTSCP ) { +/* Load per CPU data from RDTSCP */ +native_read_tscp (&p); + }else { +/* Load per CPU data from GDT */ + asm("lsl %1,%0" :"=r" (p) :"r" (__PER_CPU_SEG)); + } + +return p; +} +#endif /* __KERNEL__ */ #endif /* _ASM_X86_VSYSCALL_H */
arch/x86/vdso/vgetcpu.c +3 -8
@@ -17,15 +17,10 @@ __vdso_getcpu(unsigned *cpu, unsigned *n {unsigned int p; -if (VVAR (vgetcpu_mode) ==VGETCPU_RDTSCP ) { -/* Load per CPU data from RDTSCP */ -native_read_tscp (&p); - }else { -/* Load per CPU data from GDT */ - asm("lsl %1,%0" :"=r" (p) :"r" (__PER_CPU_SEG)); - } + p =__getcpu (); +if (cpu) - *cpu = p &0xfff ; + *cpu = p &VGETCPU_CPU_MASK ;if (node) *node = p >>12 ;return 0 ;
Re: [patch 10/16] x86: vdso: pvclock gettime support
Quoted 5 lines · Marcelo Tosatti
arch/x86/vdso/vclock_gettime.c +5
-0+#ifdef CONFIG_PARAVIRT_CLOCK + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base;
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 10 lines
arch/x86/vdso/vclock_gettime.c +10
-0+int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} +
Does BUG_ON() really do what you believe it does while in userspace context? We're not running with the kernel descriptors, so this will probably just kill the process without any explanation
Re: [patch 10/16] x86: vdso: pvclock gettime support
Quoted 11 lines · Glauber Costa
Quoted 10 lines · Marcelo Tosatti
arch/x86/vdso/vclock_gettime.c +10
-0+#ifdef CONFIG_PARAVIRT_CLOCK + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); +
Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 5 lines
arch/x86/vdso/vclock_gettime.c +5
-0+ pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} +Does
BUG_ON()really do what you believe it does while in userspace context? We're not running with the kernel descriptors, so this will probably just kill the process without any explanation
A coredump is generated which can be used to trace back to ud2a instruction at the vdso code.
Re: [patch 10/16] x86: vdso: pvclock gettime support
Quoted 21 lines · Marcelo Tosatti
Quoted 11 lines · Glauber Costa
Quoted 10 lines · Marcelo Tosatti
arch/x86/vdso/vclock_gettime.c +10
-0+#ifdef CONFIG_PARAVIRT_CLOCK + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); +Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 5 lines
arch/x86/vdso/vclock_gettime.c +5
-0+ pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} +Does
BUG_ON()really do what you believe it does while in userspace context? We're not running with the kernel descriptors, so this will probably just kill the process without any explanation
Quoted 2 lines
A coredump is generated which can be used to trace back to ud2a instruction at the vdso code.
All comments have been addressed. Let me know if there is anything else on v3 that you'd like to see done differently.
Re: [patch 10/16] x86: vdso: pvclock gettime support
Quoted 22 lines · Marcelo Tosatti
Quoted 21 lines · Marcelo Tosatti
Quoted 19 lines · Glauber Costa
Quoted 5 lines · Marcelo Tosatti
arch/x86/vdso/vclock_gettime.c +5
-0+#ifdef CONFIG_PARAVIRT_CLOCK + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base;arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 80
Quoted 10 lines
arch/x86/vdso/vclock_gettime.c +10
-0+int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} +Does
BUG_ON()really do what you believe it does while in userspace context? We're not running with the kernel descriptors, so this will probably just kill the process without any explanation
Quoted 6 lines
Quoted 2 lines
A coredump is generated which can be used to trace back to ud2a instruction at the vdso code.
All comments have been addressed. Let me know if there is anything else on v3 that you'd like to see done differently.
Mainly:
1) stick a "v3" string in the subject. You didn't do it for v2, and I got confused at some points while looking for the correct patches
2) The changelogs are, in general, a bit poor. I've pointed to the ones specifically that pops out, but I would appreciate if you would go over them again, making them more informative.
3) Please make sure Peter is okay with the proposed notifier change.
4) Please consider allocating memory with __alloc_bootmem_node instead.
Re: [patch 10/16] x86: vdso: pvclock gettime support
In reply to Marcelo TosattiQuoted 64 lines · Marcelo Tosatti
Improve performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/vdso/vclock_gettime.c
===================================================================arch/x86/vdso/vclock_gettime.c +42
-0@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))@@ -62,6 +63,70 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} + +static notrace cycle_t vread_pvclock(int *mode) +{ +const struct pvclock_vsyscall_time_info *pvti; +cycle_t ret; +u64 last; +u32 version; +u32 migrate_count; +u8 flags; +unsigned cpu, cpu1; + + +/* +* When looping to get a consistent (time-info, tsc) pair, we +* also need to deal with the possibility we can switch vcpus, +* so make sure we always re-fetch time-info for the current vcpu. +*/ + do { + cpu = __getcpu() & VGETCPU_CPU_MASK; + pvti = get_pvti(cpu); + + migrate_count = pvti->migrate_count; + + version = __pvclock_read_cycles(&pvti->pvti, &ret, &flags); + + /* + * Test we're still on the cpu as well as the version.
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 116
Quoted 10 lines
arch/x86/vdso/vclock_gettime.c +10
-0+* We could have been migrated just after the first +* vgetcpu but before fetching the version, so we +* wouldn't notice a version change. +*/ + cpu1 =__getcpu () &VGETCPU_CPU_MASK ; + }while (unlikely (cpu != cpu1 || + (pvti->pvti.version &1 ) || + pvti->pvti.version != version || + pvti->migrate_count != migrate_count)); +
We can put vcpu id into higher bits of pvti.version. This will save a couple of cycles by getting rid of __getcpu() calls.
Quoted 94 lines
arch/x86/vdso/vclock_gettime.c +18
-3+if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + *mode =VCLOCK_NONE ; + +/* refer to tsc.c read_tsc() comment for rationale */ + last =VVAR (vsyscall_gtod_data).clock.cycle_last; + +if (likely (ret >= last)) +return ret; + +return last; +} +#endif + notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) {long ret;@@ -80,7 +145,7 @@ notrace static long vdso_fallback_gtod(s } -notrace static inline u64 vgetsns(void) +notrace static inline u64 vgetsns(int *mode) {long v;cycles_t cycles;@@ -88,6 +153,8 @@ notrace static inline u64 vgetsns(void) cycles =vread_tsc (); else if (gtod->clock.vclock_mode == VCLOCK_HPET) cycles =vread_hpet (); + else if (gtod->clock.vclock_mode == VCLOCK_PVCLOCK) + cycles =vread_pvclock (mode); else return 0; v = (cycles - gtod->clock.cycle_last) & gtod->clock.mask;@@ -107,7 +174,7 @@ notrace static int __always_inline do_re mode = gtod->clock.vclock_mode; ts->tv_sec = gtod->wall_time_sec; ns = gtod->wall_time_snsec; - ns +=vgetsns (); + ns +=vgetsns (&mode); ns >>= gtod->clock.shift; }while (unlikely (read_seqcount_retry (>od->seq, seq)));@@ -127,7 +194,7 @@ notrace static int do_monotonic(struct t mode = gtod->clock.vclock_mode; ts->tv_sec = gtod->monotonic_time_sec; ns = gtod->monotonic_time_snsec; - ns +=vgetsns (); + ns +=vgetsns (&mode); ns >>= gtod->clock.shift; }while (unlikely (read_seqcount_retry (>od->seq, seq)));timespec_add_ns (ts, ns);Index:
vsyscall/arch/x86/include/asm/vsyscall.h
===================================================================arch/x86/include/asm/vsyscall.h +17
-0@@ -33,6 +33,23 @@ extern void map_vsyscall(void);*/ extern bool emulate_vsyscall (struct pt_regs *regs,unsigned long address); +#define VGETCPU_CPU_MASK 0xfff + +static inline unsigned int __getcpu (void ) +{ +unsigned int p; + +if (VVAR (vgetcpu_mode) ==VGETCPU_RDTSCP ) { +/* Load per CPU data from RDTSCP */ +native_read_tscp (&p); + }else { +/* Load per CPU data from GDT */ + asm("lsl %1,%0" :"=r" (p) :"r" (__PER_CPU_SEG)); + } + +return p; +} +#endif /* __KERNEL__ */ #endif /* _ASM_X86_VSYSCALL_H */ Index:
vsyscall/arch/x86/vdso/vgetcpu.c
===================================================================arch/x86/vdso/vgetcpu.c +0
-5@@ -17,15 +17,10 @@ __vdso_getcpu(unsigned *cpu, unsigned *n {unsigned int p; -if (VVAR (vgetcpu_mode) ==VGETCPU_RDTSCP ) { -/* Load per CPU data from RDTSCP */ -native_read_tscp (&p); - }else { -/* Load per CPU data from GDT */
arch/x86/vdso/vgetcpu.c __vdso_getcpu(unsigned *cpu, unsigned *n · line 26
Quoted 10 lines
arch/x86/vdso/vgetcpu.c +3
-3- asm("lsl %1,%0" :"=r" (p) :"r" (__PER_CPU_SEG)); - } + p =__getcpu (); +if (cpu) - *cpu = p &0xfff ; + *cpu = p &VGETCPU_CPU_MASK ;if (node) *node = p >>12 ;return 0 ;
--
Gleb.
Re: [patch 10/16] x86: vdso: pvclock gettime support
Quoted 69 lines · Gleb Natapov
Quoted 68 lines · Marcelo Tosatti
Improve performance of time system calls when using Linux pvclock, by reading time info from fixmap visible copy of pvclock data.
Originally from Jeremy Fitzhardinge.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
vsyscall/arch/x86/vdso/vclock_gettime.c
===================================================================arch/x86/vdso/vclock_gettime.c +46
-0@@ -22,6 +22,7 @@#include <asm/hpet.h> #include <asm/unistd.h> #include <asm/io.h> +#include <asm/pvclock.h> #define gtod (&VVAR(vsyscall_gtod_data))@@ -62,6 +63,70 @@ static notrace cycle_t vread_hpet(void)return readl ((const void __iomem *)fix_to_virt (VSYSCALL_HPET ) +0xf0 ); } +#ifdef CONFIG_PARAVIRT_CLOCK + +static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +{ +const aligned_pvti_t *pvti_base; +int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); +int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); + +BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); + + pvti_base = (aligned_pvti_t *)__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); + +return &pvti_base[offset].info; +} + +static notrace cycle_t vread_pvclock(int *mode) +{ +const struct pvclock_vsyscall_time_info *pvti; +cycle_t ret; +u64 last; +u32 version; +u32 migrate_count; +u8 flags; +unsigned cpu, cpu1; + + +/* +* When looping to get a consistent (time-info, tsc) pair, we +* also need to deal with the possibility we can switch vcpus, +* so make sure we always re-fetch time-info for the current vcpu. +*/ + do { + cpu = __getcpu() & VGETCPU_CPU_MASK; + pvti = get_pvti(cpu); + + migrate_count = pvti->migrate_count; + + version = __pvclock_read_cycles(&pvti->pvti, &ret, &flags); + + /* + * Test we're still on the cpu as well as the version. + * We could have been migrated just after the first + * vgetcpu but before fetching the version, so we + * wouldn't notice a version change. + */
Quoted 8 lines
arch/x86/vdso/vclock_gettime.c static notrace cycle_t vread_hpet(void) · line 116
Quoted 6 lines
arch/x86/vdso/vclock_gettime.c +6
-0+ cpu1 =__getcpu () &VGETCPU_CPU_MASK ; + }while (unlikely (cpu != cpu1 || + (pvti->pvti.version &1 ) || + pvti->pvti.version != version || + pvti->migrate_count != migrate_count)); +We can put vcpu id into higher bits of pvti.version. This will save a couple of cycles by getting rid of
__getcpu()calls.
Yes. Added as comment in the code.
PATCH 11/16 KVM: x86: pass host_tsc to read_l1_tsc
In reply to Marcelo TosattiAllow the caller to pass host tsc value to kvm_x86_ops->read_l1_tsc().
arch/x86/include/asm/kvm_host.h +1 -1
@@ -703,7 +703,7 @@ struct kvm_x86_ops {void (*write_tsc_offset)(struct kvm_vcpu *vcpu,u64 offset);u64 (*compute_tsc_offset)(struct kvm_vcpu *vcpu,u64 target_tsc); -u64 (*read_l1_tsc)(struct kvm_vcpu *vcpu); +u64 (*read_l1_tsc)(struct kvm_vcpu *vcpu,u64 host_tsc);void (*get_exit_info)(struct kvm_vcpu *vcpu,u64 *info1,u64 *info2);
arch/x86/kvm/lapic.c +1 -1
@@ -1011,7 +1011,7 @@ static void start_apic_timer(struct kvm_local_irq_save (flags); now = apic->lapic_timer.timer.base->get_time (); - guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu); + guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu,native_read_tsc ());if (likely (tscdeadline > guest_tsc)) { ns = (tscdeadline - guest_tsc) *1000000ULL ;do_div (ns, this_tsc_khz);
arch/x86/kvm/svm.c +2 -2
@@ -3008,11 +3008,11 @@ static int cr8_write_interception(structreturn 0 ; } -u64 svm_read_l1_tsc (struct kvm_vcpu *vcpu) +u64 svm_read_l1_tsc (struct kvm_vcpu *vcpu,u64 host_tsc) {struct vmcb *vmcb =get_host_vmcb (to_svm (vcpu));return vmcb->control.tsc_offset + -svm_scale_tsc (vcpu,native_read_tsc ()); +svm_scale_tsc (vcpu, host_tsc); }static int svm_get_msr (struct kvm_vcpu *vcpu,unsigned ecx,u64 *data)
arch/x86/kvm/vmx.c +2 -3
@@ -1839,11 +1839,10 @@ static u64 guest_read_tsc(void)* Like guest_read_tsc, but always returns L1's notion of the timestamp * counter, even if a nested guest (L2) is currently running. */ -u64 vmx_read_l1_tsc (struct kvm_vcpu *vcpu) +u64 vmx_read_l1_tsc (struct kvm_vcpu *vcpu,u64 host_tsc) { -u64 host_tsc, tsc_offset; +u64 tsc_offset; -rdtscll (host_tsc); tsc_offset =is_guest_mode (vcpu) ?to_vmx (vcpu)->nested.vmcs01_tsc_offset :vmcs_read64 (TSC_OFFSET );
arch/x86/kvm/x86.c +3 -2
@@ -1175,7 +1175,7 @@ static int kvm_guest_time_update(struct/* Keep irq disabled to prevent changes to the clock */ local_irq_save (flags); - tsc_timestamp = kvm_x86_ops->read_l1_tsc (v); + tsc_timestamp = kvm_x86_ops->read_l1_tsc (v,native_read_tsc ()); kernel_ns =get_kernel_ns (); this_tsc_khz =__get_cpu_var (cpu_tsc_khz);if (unlikely (this_tsc_khz ==0 )) {
@@ -5429,7 +5429,8 @@ static int vcpu_enter_guest(struct kvm_vif (hw_breakpoint_active ())hw_breakpoint_restore (); - vcpu->arch.last_guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu); + vcpu->arch.last_guest_tsc = kvm_x86_ops->read_l1_tsc (vcpu, +native_read_tsc ()); vcpu->mode =OUTSIDE_GUEST_MODE ;smp_wmb ();
PATCH 12/16 time: export time information for KVM pvclock
In reply to Marcelo TosattiAs suggested by John, export time data similarly to how its done by vsyscall support. This allows KVM to retrieve necessary information to implement vsyscall support in KVM guests.
include/linux/pvclock_gtod.h new file +27 -0
@@ -0,0 +1,27 @@ +#ifndef _PVCLOCK_GTOD_H +#define _PVCLOCK_GTOD_H + +#include <linux/clocksource.h> +#include <linux/notifier.h> + +struct pvclock_gtod_data { +seqcount_t seq; + +struct {/* extract of a clocksource struct */ +int vclock_mode; +cycle_t cycle_last; +cycle_t mask; +u32 mult; +u32 shift; + } clock; + +/* open coded 'struct timespec' */ +u64 monotonic_time_snsec; +time_t monotonic_time_sec; +}; +extern struct pvclock_gtod_data pvclock_gtod_data; + +extern int pvclock_gtod_register_notifier (struct notifier_block *nb); +extern int pvclock_gtod_unregister_notifier (struct notifier_block *nb); + +#endif /* _PVCLOCK_GTOD_H */
kernel/time/timekeeping.c +75 -0
93 lines not on this page · raw message
PATCH 13/16 KVM: x86: implement PVCLOCK_TSC_STABLE_BIT pvclock flag
In reply to Marcelo TosattiKVM added a global variable to guarantee monotonicity in the guest. One of the reasons for that is that the time between
1. ktime_get_ts(×pec);
2. rdtscll(tsc);
Is variable. That is, given a host with stable TSC, suppose that two VCPUs read the same time via ktime_get_ts() above.
The time required to execute 2. is not the same on those two instances executing in different VCPUS (cache misses, interrupts...).
If the TSC value that is used by the host to interpolate when calculating the monotonic time is the same value used to calculate the tsc_timestamp value stored in the pvclock data structure, and a single <system_timestamp, tsc_timestamp> tuple is visible to all vcpus simultaneously, this problem disappears. See comment on top of pvclock_update_vm_gtod_copy for details.
Monotonicity is then guaranteed by synchronicity of the host TSCs and guest TSCs.
Set TSC stable pvclock flag in that case, allowing the guest to read clock from userspace.
arch/x86/kvm/x86.c +212 -7
@@ -46,6 +46,7 @@#include <linux/uaccess.h> #include <linux/hash.h> #include <linux/pci.h> +#include <linux/pvclock_gtod.h> #include <trace/events/kvm.h> #define CREATE_TRACE_POINTS
@@ -1135,8 +1136,149 @@ void kvm_write_tsc(struct kvm_vcpu *vcpuEXPORT_SYMBOL_GPL (kvm_write_tsc); +static cycle_t read_tsc (void ) +{ +cycle_t ret; +u64 last; + +/* +* Empirically, a fence (of type that depends on the CPU) +* before rdtsc is enough to ensure that rdtsc is ordered +* with respect to loads. The various CPU manuals are unclear +* as to whether rdtsc can be reordered with later loads, +* but no one has ever seen it happen. +*/ +rdtsc_barrier (); + ret = (cycle_t )vget_cycles (); + + last = pvclock_gtod_data.clock.cycle_last; + +if (likely (ret >= last)) +return ret; + +/* +* GCC likes to generate cmov here, but this branch is extremely +* predictable (it's just a funciton of time and the likely is +* very likely) and there's a data dependence, so force GCC +* to generate a branch instead. I don't barrier() because +* we don't actually need a barrier, and if this function +* ever gets inlined it will generate worse code. +*/ + asmvolatile ("" ); +return last; +} + +static inline u64 vgettsc (cycle_t *cycle_now) +{ +long v; +struct pvclock_gtod_data *gtod = &pvclock_gtod_data; + + *cycle_now =read_tsc (); + + v = (*cycle_now - gtod->clock.cycle_last) & gtod->clock.mask; +return v * gtod->clock.mult; +} + +static int do_monotonic (struct timespec *ts,cycle_t *cycle_now) +{ +unsigned long seq; +u64 ns; +int mode; +struct pvclock_gtod_data *gtod = &pvclock_gtod_data; + + ts->tv_nsec =0 ; +do { + seq =read_seqcount_begin (>od->seq); + mode = gtod->clock.vclock_mode; + ts->tv_sec = gtod->monotonic_time_sec; + ns = gtod->monotonic_time_snsec; + ns +=vgettsc (cycle_now); + ns >>= gtod->clock.shift; + }while (unlikely (read_seqcount_retry (>od->seq, seq))); +timespec_add_ns (ts, ns); + +return mode; +} + +/* returns true if host is using tsc clocksource */ +static bool kvm_get_time_and_clockread (s64 *kernel_ns,cycle_t *cycle_now) +{ +struct timespec ts; + +/* checked again under seqlock below */ +if (pvclock_gtod_data.clock.vclock_mode !=VCLOCK_TSC ) +return false; + +if (do_monotonic (&ts, cycle_now) !=VCLOCK_TSC ) +return false; + +monotonic_to_bootbased (&ts); + *kernel_ns =timespec_to_ns (&ts); + +return true; +} + + +/* +* +* Assuming a stable TSC across physical CPUS, the following condition +* is possible. Each numbered line represents an event visible to both +* CPUs at the next numbered event. +* +* "timespecX" represents host monotonic time. "tscX" represents +* RDTSC value. +* +* VCPU0 on CPU0 | VCPU1 on CPU1 +* +* 1. read timespec0,tsc0 +* 2. | timespec1 = timespec0 + N +* | tsc1 = tsc0 + M +* 3. transition to guest | transition to guest +* 4. ret0 = timespec0 + (rdtsc - tsc0) | +* 5. | ret1 = timespec1 + (rdtsc - tsc1) +* | ret1 = timespec0 + N + (rdtsc - (tsc0 + M)) +* +* Since ret0 update is visible to VCPU1 at time 5, to obey monotonicity: +* +* - ret0 < ret1 +* - timespec0 + (rdtsc - tsc0) < timespec0 + N + (rdtsc - (tsc0 + M)) +* ... +* - 0 < N - M => M < N +* +* That is, when timespec0 != timespec1, M < N. Unfortunately that is not +* always the case (the difference between two distinct xtime instances +* might be smaller then the difference between corresponding TSC reads, +* when updating guest vcpus pvclock areas). +* +* To avoid that problem, do not allow visibility of distinct +* system_timestamp/tsc_timestamp values simultaneously: use a master +* copy of host monotonic time values. Update that master copy +* in lockstep. +* +* Rely on synchronization of host TSCs for monotonicity. +* +*/ + +static void pvclock_update_vm_gtod_copy (struct kvm *kvm) +{ +struct kvm_arch *ka = &kvm->arch; +int vclock_mode; + +/* +* If the host uses TSC clock, then passthrough TSC as stable +* to the guest. +*/ + ka->use_master_clock =kvm_get_time_and_clockread ( + &ka->master_kernel_ns, + &ka->master_cycle_now); + + vclock_mode = pvclock_gtod_data.clock.vclock_mode; +trace_kvm_update_master_clock (ka->use_master_clock, vclock_mode); +} +static void kvm_write_pvtime (struct kvm_vcpu *v,struct page *page, -unsigned int offset_in_page,gpa_t gpa) +unsigned int offset_in_page,gpa_t gpa, +bool use_master_clock) {struct kvm_vcpu_arch *vcpu = &v->arch;void *shared_kaddr;
@@ -1155,6 +1297,10 @@ static void kvm_write_pvtime(struct kvm_ vcpu->pvclock_set_guest_stopped_request = false; } +/* If the host uses TSC clocksource, then it is stable */ +if (use_master_clock) + pvclock_flags |=PVCLOCK_TSC_STABLE_BIT ; + vcpu->hv_clock.flags = pvclock_flags; memcpy(shared_kaddr + offset_in_page, &vcpu->hv_clock,
@@ -1169,14 +1315,18 @@ static int kvm_guest_time_update(struct {unsigned long flags;struct kvm_vcpu_arch *vcpu = &v->arch; +struct kvm_arch *ka = &v->kvm->arch;unsigned long this_tsc_khz;s64 kernel_ns, max_kernel_ns;u64 tsc_timestamp; +u64 host_tsc; +bool use_master_clock; + + kernel_ns =0 ; + host_tsc =0 ;/* Keep irq disabled to prevent changes to the clock */ local_irq_save (flags); - tsc_timestamp = kvm_x86_ops->read_l1_tsc (v,native_read_tsc ()); - kernel_ns =get_kernel_ns (); this_tsc_khz =__get_cpu_var (cpu_tsc_khz);if (unlikely (this_tsc_khz ==0 )) {local_irq_restore (flags);
@@ -1185,6 +1335,24 @@ static int kvm_guest_time_update(struct }/* +* If the host uses TSC clock, then passthrough TSC as stable +* to the guest. +*/ +spin_lock (&ka->pvclock_gtod_sync_lock); + use_master_clock = ka->use_master_clock; +if (use_master_clock) { + host_tsc = ka->master_cycle_now; + kernel_ns = ka->master_kernel_ns; + } +spin_unlock (&ka->pvclock_gtod_sync_lock); +if (!use_master_clock) { + host_tsc =native_read_tsc (); + kernel_ns =get_kernel_ns (); + } + + tsc_timestamp = kvm_x86_ops->read_l1_tsc (v, host_tsc); + +/* * We may have to catch up the TSC to match elapsed wall clock * time for two reasons, even if kvmclock is used. * 1) CPU could have been running below the maximum TSC rate
@@ -1245,8 +1413,14 @@ static int kvm_guest_time_update(struct vcpu->hw_tsc_khz = this_tsc_khz; } -if (max_kernel_ns > kernel_ns) - kernel_ns = max_kernel_ns; +/* with a master <monotonic time, tsc value> tuple, +* pvclock clock reads always increase at the (scaled) rate +* of guest TSC - no need to deal with sampling errors. +*/ +if (!use_master_clock) { +if (max_kernel_ns > kernel_ns) + kernel_ns = max_kernel_ns; + }/* With all the info we got, fill in the values */ vcpu->hv_clock.tsc_timestamp = tsc_timestamp;
@@ -1262,10 +1436,12 @@ static int kvm_guest_time_update(struct*/ vcpu->hv_clock.version +=2 ; -kvm_write_pvtime (v, vcpu->time_page, vcpu->time_offset, vcpu->time); +kvm_write_pvtime (v, vcpu->time_page, vcpu->time_offset, vcpu->time, + use_master_clock);if (vcpu->uspace_time_page)kvm_write_pvtime (v, vcpu->uspace_time_page, - vcpu->uspace_time_offset, vcpu->uspace_time); + vcpu->uspace_time_offset, vcpu->uspace_time, + use_master_clock);return 0 ; }
@@ -5302,6 +5478,28 @@ static void process_nmi(struct kvm_vcpukvm_make_request (KVM_REQ_EVENT , vcpu); } +static void kvm_gen_update_masterclock (struct kvm *kvm) +{ +int i; +struct kvm_vcpu *vcpu; +struct kvm_arch *ka = &kvm->arch; + +spin_lock (&ka->pvclock_gtod_sync_lock); +kvm_make_mclock_inprogress_request (kvm); +/* no guest entries from this point */ +pvclock_update_vm_gtod_copy (kvm); + +kvm_for_each_vcpu (i, vcpu, kvm) +set_bit (KVM_REQ_CLOCK_UPDATE , &vcpu->requests); + +/* guest entries allowed */ +kvm_for_each_vcpu (i, vcpu, kvm) +clear_bit (KVM_REQ_MCLOCK_INPROGRESS , &vcpu->requests); + +spin_unlock (&ka->pvclock_gtod_sync_lock); + +} +static int vcpu_enter_guest (struct kvm_vcpu *vcpu) {int r;
@@ -5314,6 +5512,8 @@ static int vcpu_enter_guest(struct kvm_vkvm_mmu_unload (vcpu);if (kvm_check_request (KVM_REQ_MIGRATE_TIMER , vcpu))__kvm_migrate_timers (vcpu); +if (kvm_check_request (KVM_REQ_MASTERCLOCK_UPDATE , vcpu)) +kvm_gen_update_masterclock (vcpu->kvm);if (kvm_check_request (KVM_REQ_CLOCK_UPDATE , vcpu)) { r =kvm_guest_time_update (vcpu);if (unlikely (r))
@@ -6219,6 +6419,8 @@ int kvm_arch_hardware_enable(void *garbakvm_for_each_vcpu (i, vcpu, kvm) { vcpu->arch.tsc_offset_adjustment += delta_cyc; vcpu->arch.last_host_tsc = local_tsc; +set_bit (KVM_REQ_MASTERCLOCK_UPDATE , + &vcpu->requests); }/*
@@ -6356,6 +6558,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unraw_spin_lock_init (&kvm->arch.tsc_write_lock);mutex_init (&kvm->arch.apic_map_lock); +spin_lock_init (&kvm->arch.pvclock_gtod_sync_lock); + +pvclock_update_vm_gtod_copy (kvm);return 0 ; }
arch/x86/include/asm/kvm_host.h +6 -0
@@ -22,6 +22,7 @@#include <linux/kvm_para.h> #include <linux/kvm_types.h> #include <linux/perf_event.h> +#include <linux/pvclock_gtod.h> #include <asm/pvclock-abi.h> #include <asm/desc.h>
@@ -563,6 +564,11 @@ struct kvm_arch {u64 cur_tsc_offset;u8 cur_tsc_generation; +spinlock_t pvclock_gtod_sync_lock; +bool use_master_clock; +u64 master_kernel_ns; +cycle_t master_cycle_now; +struct kvm_xen_hvm_config xen_hvm_config;/* fields used by HYPER-V emulation */
include/linux/kvm_host.h +3 -0
@@ -118,6 +118,8 @@ static inline bool is_error_page(struct#define KVM_REQ_IMMEDIATE_EXIT 15#define KVM_REQ_PMU 16#define KVM_REQ_PMI 17 +#define KVM_REQ_MASTERCLOCK_UPDATE 18 +#define KVM_REQ_MCLOCK_INPROGRESS 19#define KVM_USERSPACE_IRQ_SOURCE_ID 0#define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1
@@ -527,6 +529,7 @@ void kvm_put_guest_fpu(struct kvm_vcpu *void kvm_flush_remote_tlbs (struct kvm *kvm);void kvm_reload_remote_mmus (struct kvm *kvm); +void kvm_make_mclock_inprogress_request (struct kvm *kvm);long kvm_arch_dev_ioctl (struct file *filp,unsigned int ioctl,unsigned long arg);
virt/kvm/kvm_main.c +5 -0
@@ -212,6 +212,11 @@ void kvm_reload_remote_mmus(struct kvm *make_all_cpus_request (kvm,KVM_REQ_MMU_RELOAD ); } +void kvm_make_mclock_inprogress_request (struct kvm *kvm) +{ +make_all_cpus_request (kvm,KVM_REQ_MCLOCK_INPROGRESS ); +} +int kvm_vcpu_init (struct kvm_vcpu *vcpu,struct kvm *kvm,unsigned id) {struct page *page;
arch/x86/kvm/trace.h +26 -0
@@ -4,6 +4,7 @@#include <linux/tracepoint.h> #include <asm/vmx.h> #include <asm/svm.h> +#include <asm/clocksource.h> #undef TRACE_SYSTEM#define TRACE_SYSTEM kvm
@@ -754,6 +755,31 @@ TRACE_EVENT( __entry->write ?"Write" :"Read" , __entry->gpa_match ? "GPA" : "GVA") ); + +#define host_clocks \ + {VCLOCK_NONE, "none"}, \ + {VCLOCK_TSC, "tsc"}, \ + {VCLOCK_HPET, "hpet"} \ + +TRACE_EVENT (kvm_update_master_clock, + TP_PROTO(bool use_master_clock, unsigned int host_clock), +TP_ARGS (use_master_clock, host_clock), + +TP_STRUCT__entry ( + __field( bool, use_master_clock ) + __field( unsigned int, host_clock ) + ), + +TP_fast_assign ( + __entry->use_master_clock = use_master_clock; + __entry->host_clock = host_clock; + ), + +TP_printk ("masterclock %d hostclock %s" , + __entry->use_master_clock, +__print_symbolic (__entry->host_clock, host_clocks)) +); +#endif /* _TRACE_KVM_H */ #undef TRACE_INCLUDE_PATH
PATCH 14/16 KVM: x86: notifier for clocksource changes
In reply to Marcelo TosattiRegister a notifier for clocksource change event. In case the host switches to clock other than TSC, disable master clock usage.
arch/x86/kvm/x86.c +44 -0
@@ -1218,6 +1218,7 @@ static bool kvm_get_time_and_clockread(sreturn true; } +static atomic_t kvm_guest_has_master_clock =ATOMIC_INIT (0 );/* *
@@ -1271,6 +1272,8 @@ static void pvclock_update_vm_gtod_copy( ka->use_master_clock =kvm_get_time_and_clockread ( &ka->master_kernel_ns, &ka->master_cycle_now); +if (ka->use_master_clock) +atomic_set (&kvm_guest_has_master_clock,1 ); vclock_mode = pvclock_gtod_data.clock.vclock_mode;trace_kvm_update_master_clock (ka->use_master_clock, vclock_mode);
@@ -5124,6 +5127,44 @@ static void kvm_set_mmio_spte_mask(void)kvm_mmu_set_mmio_spte_mask (mask); } +static void pvclock_gtod_update_fn (struct work_struct *work) +{ +struct kvm *kvm; +struct kvm_vcpu *vcpu; +int i; + +raw_spin_lock (&kvm_lock); +list_for_each_entry (kvm, &vm_list, vm_list) +kvm_for_each_vcpu (i, vcpu, kvm) +set_bit (KVM_REQ_MASTERCLOCK_UPDATE , &vcpu->requests); +atomic_set (&kvm_guest_has_master_clock,0 ); +raw_spin_unlock (&kvm_lock); +} + +static DECLARE_WORK(pvclock_gtod_work, pvclock_gtod_update_fn); + +/* +* Notification about pvclock gtod data update. +*/ +static int pvclock_gtod_notify (struct notifier_block *nb,unsigned long unused, +void *unused2) +{ +struct pvclock_gtod_data *gtod = &pvclock_gtod_data; + +/* disable master clock if host does not trust, or does not +* use, TSC clocksource +*/ +if (gtod->clock.vclock_mode !=VCLOCK_TSC && +atomic_read (&kvm_guest_has_master_clock) !=0 ) +queue_work (system_long_wq, &pvclock_gtod_work); + +return 0 ; +} + +static struct notifier_block pvclock_gtod_notifier = { + .notifier_call = pvclock_gtod_notify, +}; +int kvm_arch_init (void *opaque) {int r;
@@ -5165,6 +5206,8 @@ int kvm_arch_init(void *opaque) host_xcr0 =xgetbv (XCR_XFEATURE_ENABLED_MASK );kvm_lapic_init (); +pvclock_gtod_register_notifier (&pvclock_gtod_notifier); +return 0 ; out:
@@ -5179,6 +5222,7 @@ void kvm_arch_exit(void)cpufreq_unregister_notifier (&kvmclock_cpufreq_notifier_block,CPUFREQ_TRANSITION_NOTIFIER );unregister_hotcpu_notifier (&kvmclock_cpu_notifier_block); +pvclock_gtod_unregister_notifier (&pvclock_gtod_notifier); kvm_x86_ops =NULL ;kvm_mmu_module_exit (); }
PATCH 15/16 KVM: x86: add kvm_arch_vcpu_postcreate callback, move TSC initialization
In reply to Marcelo TosattiTSC initialization will soon make use of online_vcpus.
arch/ia64/kvm/kvm-ia64.c +5 -0
@@ -1330,6 +1330,11 @@ int kvm_arch_vcpu_setup(struct kvm_vcpureturn 0 ; } +int kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +return 0 ; +} +int kvm_arch_vcpu_ioctl_get_fpu (struct kvm_vcpu *vcpu,struct kvm_fpu *fpu) {return -EINVAL ;
arch/powerpc/kvm/powerpc.c +5 -0
@@ -354,6 +354,11 @@ struct kvm_vcpu *kvm_arch_vcpu_create(streturn vcpu; } +void kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +return 0 ; +} +void kvm_arch_vcpu_free (struct kvm_vcpu *vcpu) {/* Make sure we're not using the vcpu anymore */
arch/s390/kvm/kvm-s390.c +5 -0
@@ -355,6 +355,11 @@ static void kvm_s390_vcpu_initial_reset(atomic_set_mask (CPUSTAT_STOPPED , &vcpu->arch.sie_block->cpuflags); } +void kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +return 0 ; +} +int kvm_arch_vcpu_setup (struct kvm_vcpu *vcpu) { atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH |
arch/x86/kvm/svm.c +0 -1
@@ -1254,7 +1254,6 @@ static struct kvm_vcpu *svm_create_vcpu( svm->vmcb_pa =page_to_pfn (page) <<PAGE_SHIFT ; svm->asid_generation =0 ;init_vmcb (svm); -kvm_write_tsc (&svm->vcpu,0 ); err =fx_init (&svm->vcpu);if (err)
arch/x86/kvm/vmx.c +0 -2
@@ -3896,8 +3896,6 @@ static int vmx_vcpu_setup(struct vcpu_vmvmcs_writel (CR0_GUEST_HOST_MASK , ~0UL );set_cr4_guest_host_mask (vmx); -kvm_write_tsc (&vmx->vcpu,0 ); -return 0 ; }
arch/x86/kvm/x86.c +13 -0
@@ -6350,6 +6350,19 @@ int kvm_arch_vcpu_setup(struct kvm_vcpureturn r; } +int kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu) +{ +int r; + + r =vcpu_load (vcpu); +if (r) +return r; +kvm_write_tsc (vcpu,0 ); +vcpu_put (vcpu); + +return r; +} +void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu) {int r;
include/linux/kvm_host.h +1 -0
@@ -583,6 +583,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpuvoid kvm_arch_vcpu_put (struct kvm_vcpu *vcpu);struct kvm_vcpu *kvm_arch_vcpu_create (struct kvm *kvm,unsigned int id);int kvm_arch_vcpu_setup (struct kvm_vcpu *vcpu); +int kvm_arch_vcpu_postcreate (struct kvm_vcpu *vcpu);void kvm_arch_vcpu_destroy (struct kvm_vcpu *vcpu);int kvm_arch_vcpu_reset (struct kvm_vcpu *vcpu);
virt/kvm/kvm_main.c +1 -0
@@ -1855,6 +1855,7 @@ static int kvm_vm_ioctl_create_vcpu(struatomic_inc (&kvm->online_vcpus);mutex_unlock (&kvm->lock); +kvm_arch_vcpu_postcreate (vcpu);return r; unlock_vcpu_destroy:
PATCH 16/16 KVM: x86: require matched TSC offsets for master clock
In reply to Marcelo TosattiWith master clock, a pvclock clock read calculates:
ret = system_timestamp + [ (rdtsc + tsc_offset) - tsc_timestamp ]
Where 'rdtsc' is the host TSC.
system_timestamp and tsc_timestamp are unique, one tuple per VM: the "master clock".
Given a host with synchronized TSCs, its obvious that guest TSC must be matched for the above to guarantee monotonicity.
Allow master clock usage only if guest TSCs are synchronized.
arch/x86/include/asm/kvm_host.h +1 -0
@@ -563,6 +563,7 @@ struct kvm_arch {u64 cur_tsc_write;u64 cur_tsc_offset;u8 cur_tsc_generation; +int nr_vcpus_matched_tsc;spinlock_t pvclock_gtod_sync_lock;bool use_master_clock;
arch/x86/kvm/x86.c +50 -5
114 lines not on this page · raw message
arch/x86/kvm/trace.h +36 -3
@@ -762,21 +762,54 @@ TRACE_EVENT( {VCLOCK_HPET, "hpet"} \TRACE_EVENT (kvm_update_master_clock, - TP_PROTO(bool use_master_clock, unsigned int host_clock), -TP_ARGS (use_master_clock, host_clock), + TP_PROTO(bool use_master_clock, unsigned int host_clock, bool offset_matched), +TP_ARGS (use_master_clock, host_clock, offset_matched),TP_STRUCT__entry ( __field( bool, use_master_clock ) __field( unsigned int, host_clock ) +__field ( bool, offset_matched ) ),TP_fast_assign ( __entry->use_master_clock = use_master_clock; __entry->host_clock = host_clock; + __entry->offset_matched = offset_matched; ), -TP_printk ("masterclock %d hostclock %s" , +TP_printk ("masterclock %d hostclock %s offsetmatched %u" , __entry->use_master_clock, +__print_symbolic (__entry->host_clock, host_clocks), + __entry->offset_matched) +); + +TRACE_EVENT (kvm_track_tsc, + TP_PROTO(unsigned int vcpu_id, unsigned int nr_matched, + unsigned int online_vcpus, bool use_master_clock, + unsigned int host_clock), +TP_ARGS (vcpu_id, nr_matched, online_vcpus, use_master_clock, + host_clock), + +TP_STRUCT__entry ( + __field( unsigned int, vcpu_id ) + __field( unsigned int, nr_vcpus_matched_tsc ) + __field( unsigned int, online_vcpus ) + __field( bool, use_master_clock ) + __field( unsigned int, host_clock ) + ), + +TP_fast_assign ( + __entry->vcpu_id = vcpu_id; + __entry->nr_vcpus_matched_tsc = nr_matched; + __entry->online_vcpus = online_vcpus; + __entry->use_master_clock = use_master_clock; + __entry->host_clock = host_clock; + ), + +TP_printk ("vcpu_id %u masterclock %u offsetmatched %u nr_online %u" +" hostclock %s" , + __entry->vcpu_id, __entry->use_master_clock, + __entry->nr_vcpus_matched_tsc, __entry->online_vcpus,__print_symbolic (__entry->host_clock, host_clocks)) );