Skip to contentSkip to conversation

report stolen time via pvclock

PATCH New Marcelo Tosatti 1 participant 6 messages Oct 16, 2009

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover report stolen time via pvclock

Stolen time can be useful diagnostic information when available to guests. Xen provides it for sometime, so recent vmstat versions already display it.

Also increases guests sched_clock accuracy.

#1 lore raw

PATCH 1/5 KVM: x86: report stolen time

Report stolen time (run_delay field from schedstat) to guests via pvclock.

arch/x86/include/asm/kvm_para.h +7 -0
Index: kvm/arch/x86/include/asm/kvm_para.h
===================================================================
@@ -15,9 +15,11 @@
 #define KVM_FEATURE_CLOCKSOURCE		0
 #define KVM_FEATURE_NOP_IO_DELAY	1
 #define KVM_FEATURE_MMU_OP		2
+#define KVM_FEATURE_RUNTIME_INFO	3
 
 #define MSR_KVM_WALL_CLOCK  0x11
 #define MSR_KVM_SYSTEM_TIME 0x12
+#define MSR_KVM_RUN_TIME    0x13
 
 #define KVM_MAX_MMU_OP_BATCH           32
 
@@ -50,6 +52,11 @@ struct kvm_mmu_op_release_pt {
 #ifdef __KERNEL__
 #include <asm/processor.h>
 
+struct kvm_vcpu_runtime_info {
+	u64 stolen_time;		/* time spent starving */
+	u64 reserved[3];		/* for future use */
+};
+
 extern void kvmclock_init(void);
 
 
arch/x86/include/asm/kvm_host.h +4 -0
Index: kvm/arch/x86/include/asm/kvm_host.h
===================================================================
@@ -354,6 +354,10 @@ struct kvm_vcpu_arch {
 	unsigned int time_offset;
 	struct page *time_page;
 
+	bool stolen_time_enable;
+	struct kvm_vcpu_runtime_info stolen_time;
+	unsigned int stolen_time_offset;
+
 	bool singlestep; /* guest is single stepped by KVM */
 	bool nmi_pending;
 	bool nmi_injected;
arch/x86/kvm/x86.c +40 -2

79 lines not on this page · raw message

arch/x86/kvm/Kconfig +1 -0
Index: kvm/arch/x86/kvm/Kconfig
===================================================================
@@ -28,6 +28,7 @@ config KVM
 	select HAVE_KVM_IRQCHIP
 	select HAVE_KVM_EVENTFD
 	select KVM_APIC_ARCHITECTURE
+	select SCHEDSTATS
 	---help---
 	  Support hosting fully virtualized guest machines using hardware
 	  virtualization extensions.  You will need a fairly recent
include/linux/kvm.h +1 -0
Index: kvm/include/linux/kvm.h
===================================================================
@@ -436,6 +436,7 @@ struct kvm_ioeventfd {
 #endif
 #define KVM_CAP_IOEVENTFD 36
 #define KVM_CAP_SET_IDENTITY_MAP_ADDR 37
+#define KVM_CAP_PVCLOCK_RUNTIME 38
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
#2 lore raw

PATCH 2/5 pvclock: move code to pvclock.h

In reply to Marcelo Tosatti

To be used by kvmclock.c.

arch/x86/include/asm/pvclock.h +18 -0
Index: kvm/arch/x86/include/asm/pvclock.h
===================================================================
@@ -4,6 +4,20 @@
 #include <linux/clocksource.h>
 #include <asm/pvclock-abi.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;
+};
+
 /* some helper functions for xen and kvm pv clock sources */
 cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src);
 unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src);
@@ -11,4 +25,8 @@ void pvclock_read_wallclock(struct pvclo
 			    struct pvclock_vcpu_time_info *vcpu,
 			    struct timespec *ts);
 
+u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow);
+unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst,
+				 struct pvclock_vcpu_time_info *src);
+
 #endif /* _ASM_X86_PVCLOCK_H */
arch/x86/kernel/pvclock.c +3 -17
Index: kvm/arch/x86/kernel/pvclock.c
===================================================================
@@ -20,20 +20,6 @@
 #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;
-};
-
-/*
  * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
  * yielding a 64-bit result.
  */
@@ -71,7 +57,7 @@ static inline u64 scale_delta(u64 delta,
 	return product;
 }
 
-static u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow)
+u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow)
 {
 	u64 delta = native_read_tsc() - shadow->tsc_timestamp;
 	return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift);
@@ -81,8 +67,8 @@ static u64 pvclock_get_nsec_offset(struc
  * 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)
+unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst,
+				 struct pvclock_vcpu_time_info *src)
 {
 	do {
 		dst->version = src->version;
#3 lore raw

PATCH 3/5 kvmclock: stolen time aware sched_clock

In reply to Marcelo Tosatti

sched_clock() should time the vcpu run time. Subtract stolen time from realtime pvclock.

arch/x86/kernel/kvmclock.c +68 -3
Index: kvm/arch/x86/kernel/kvmclock.c
===================================================================
@@ -38,7 +38,16 @@ static int parse_no_kvmclock(char *arg)
 early_param("no-kvmclock", parse_no_kvmclock);
 
 /* The hypervisor will put information about time periodically here */
-static DEFINE_PER_CPU_SHARED_ALIGNED(struct pvclock_vcpu_time_info, hv_clock);
+struct time_info {
+	struct pvclock_vcpu_time_info hv_clock;
+	struct kvm_vcpu_runtime_info run_info;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct time_info, time_info);
+
+#define hv_clock time_info.hv_clock
+#define run_info time_info.run_info
+
 static struct pvclock_wall_clock wall_clock;
 
 /*
@@ -84,6 +93,40 @@ static cycle_t kvm_clock_get_cycles(stru
 	return kvm_clock_read();
 }
 
+cycle_t kvm_runtime_read(struct pvclock_vcpu_time_info *src,
+		  	 struct kvm_vcpu_runtime_info *rinfo)
+{
+	struct pvclock_shadow_time shadow;
+	unsigned version;
+	cycle_t ret, offset;
+	unsigned long long stolen;
+
+	do {
+		version = pvclock_get_time_values(&shadow, src);
+		barrier();
+		offset = pvclock_get_nsec_offset(&shadow);
+		stolen = rinfo->stolen_time;
+		ret = shadow.system_timestamp + offset - stolen;
+		barrier();
+	} while (version != src->version);
+
+	return ret;
+}
+
+static cycle_t kvm_clock_read_unstolen(void)
+{
+	struct pvclock_vcpu_time_info *src;
+	struct kvm_vcpu_runtime_info *rinfo;
+	cycle_t ret;
+
+	src = &get_cpu_var(hv_clock);
+	rinfo = &get_cpu_var(run_info);
+	ret = kvm_runtime_read(src, rinfo);
+	put_cpu_var(run_info);
+	put_cpu_var(hv_clock);
+	return ret;
+}
+
 /*
  * If we don't do that, there is the possibility that the guest
  * will calibrate under heavy load - thus, getting a lower lpj -
@@ -133,14 +176,30 @@ static int kvm_register_clock(char *txt)
 	return native_write_msr_safe(MSR_KVM_SYSTEM_TIME, low, high);
 }
 
+static int kvm_register_run_info(char *txt)
+{
+	int cpu = smp_processor_id();
+	int low, high;
+
+	low = (int) __pa(&per_cpu(run_info, cpu)) | 1;
+	high = ((u64)__pa(&per_cpu(run_info, cpu)) >> 32);
+	printk(KERN_INFO "kvm-runtime-info: cpu %d, msr %x:%x, %s\n",
+	       cpu, high, low, txt);
+	return native_write_msr_safe(MSR_KVM_RUN_TIME, low, high);
+}
+
 #ifdef CONFIG_X86_LOCAL_APIC
 static void __cpuinit kvm_setup_secondary_clock(void)
 {
+	char *txt = "secondary cpu clock";
+
 	/*
 	 * Now that the first cpu already had this clocksource initialized,
 	 * we shouldn't fail.
 	 */
-	WARN_ON(kvm_register_clock("secondary cpu clock"));
+	WARN_ON(kvm_register_clock(txt));
+	if (kvm_para_has_feature(KVM_FEATURE_RUNTIME_INFO))
+		kvm_register_run_info(txt);
 	/* ok, done with our trickery, call native */
 	setup_secondary_APIC_clock();
 }
@@ -149,7 +208,11 @@ static void __cpuinit kvm_setup_secondar
 #ifdef CONFIG_SMP
 static void __init kvm_smp_prepare_boot_cpu(void)
 {
-	WARN_ON(kvm_register_clock("primary cpu clock"));
+	char *txt = "primary cpu clock";
+
+	WARN_ON(kvm_register_clock(txt));
+	if (kvm_para_has_feature(KVM_FEATURE_RUNTIME_INFO))
+		kvm_register_run_info(txt);
 	native_smp_prepare_boot_cpu();
 }
 #endif
@@ -204,4 +267,6 @@ void __init kvmclock_init(void)
 		pv_info.paravirt_enabled = 1;
 		pv_info.name = "KVM";
 	}
+	if (kvm_para_has_feature(KVM_FEATURE_RUNTIME_INFO))
+		pv_time_ops.sched_clock = kvm_clock_read_unstolen;
 }
#4 lore raw

PATCH 4/5 kvmclock: account stolen time

In reply to Marcelo Tosatti

Which makes stolen time information available in procfs/vmstat.

arch/x86/kernel/kvmclock.c +26 -0
Index: kvm/arch/x86/kernel/kvmclock.c
===================================================================
@@ -22,11 +22,14 @@
 #include <asm/msr.h>
 #include <asm/apic.h>
 #include <linux/percpu.h>
+#include <linux/kernel_stat.h>
 
 #include <asm/x86_init.h>
 #include <asm/reboot.h>
+#include <asm/cputime.h>
 
 #define KVM_SCALE 22
+#define NS_PER_TICK (1000000000LL / HZ)
 
 static int kvmclock = 1;
 
@@ -50,6 +53,29 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(str
 
 static struct pvclock_wall_clock wall_clock;
 
+static DEFINE_PER_CPU(u64, total_stolen);
+static DEFINE_PER_CPU(u64, residual_stolen);
+
+void kvm_account_steal_time(void)
+{
+	struct kvm_vcpu_runtime_info *rinfo;
+	cputime_t ticks;
+	u64 stolen_time, stolen_delta;
+
+	rinfo = &get_cpu_var(run_info);
+	stolen_time = rinfo->stolen_time;
+	stolen_delta = stolen_time - __get_cpu_var(total_stolen);
+
+	__get_cpu_var(total_stolen) = stolen_time;
+	put_cpu_var(rinfo);
+
+	stolen_delta += __get_cpu_var(residual_stolen);
+
+	ticks = iter_div_u64_rem(stolen_delta, NS_PER_TICK, &stolen_delta);
+	__get_cpu_var(residual_stolen) = stolen_delta;
+	account_steal_ticks(ticks);
+}
+
 /*
  * The wallclock is the time of day when we booted. Since then, some time may
  * have elapsed since the hypervisor wrote the data. So we try to account for
kernel/sched.c +6 -0
Index: kvm/kernel/sched.c
===================================================================
@@ -74,6 +74,9 @@
 
 #include <asm/tlb.h>
 #include <asm/irq_regs.h>
+#ifdef CONFIG_KVM_CLOCK
+#include <asm/kvm_para.h>
+#endif
 
 #include "sched_cpupri.h"
 
@@ -5102,6 +5105,9 @@ void account_process_tick(struct task_st
 				    one_jiffy_scaled);
 	else
 		account_idle_time(cputime_one_jiffy);
+#ifdef CONFIG_KVM_CLOCK
+	kvm_account_steal_time();
+#endif
 }
 
 /*
arch/x86/include/asm/kvm_para.h +1 -0
Index: kvm/arch/x86/include/asm/kvm_para.h
===================================================================
@@ -58,6 +58,7 @@ struct kvm_vcpu_runtime_info {
 };
 
 extern void kvmclock_init(void);
+extern void kvm_account_steal_time(void);
 
 
 /* This instruction is vmcall.  On non-VT architectures, it will generate a
#5 lore raw

PATCH 5/5 qemu-kvm-x86: report pvclock runtime capability

In reply to Marcelo Tosatti
qemu-kvm-x86.c +3 -0
index fffcfd8..0b8a858 100644
@@ -1223,6 +1223,9 @@ struct kvm_para_features {
 #ifdef KVM_CAP_CR3_CACHE
 	{ KVM_CAP_CR3_CACHE, KVM_FEATURE_CR3_CACHE },
 #endif
+#ifdef KVM_CAP_PVCLOCK_RUNTIME
+	{ KVM_CAP_PVCLOCK_RUNTIME, KVM_FEATURE_RUNTIME_INFO },
+#endif
 	{ -1, -1 }
 };