This is the host part of kvm clocksource implementation. As it does not include clockevents, it is a fairly simple implementation. We only have to register a per-vcpu area, and start writting to it periodically.
The area is binary compatible with xen, as we use the same shadow_info structure. Signed-off-by: Glauber de Oliveira Costa <[EMAIL PROTECTED]> --- arch/x86/kvm/x86.c | 64 +++++++++++++++++++++++++++++++++++++++++++- include/asm-x86/kvm_host.h | 5 +++ include/asm-x86/kvm_para.h | 5 +++ include/linux/kvm.h | 1 + 4 files changed, 74 insertions(+), 1 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8a90403..d3fd920 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -19,6 +19,7 @@ #include "irq.h" #include "mmu.h" +#include <linux/clocksource.h> #include <linux/kvm.h> #include <linux/fs.h> #include <linux/vmalloc.h> @@ -412,7 +413,7 @@ static u32 msrs_to_save[] = { #ifdef CONFIG_X86_64 MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR, #endif - MSR_IA32_TIME_STAMP_COUNTER, + MSR_IA32_TIME_STAMP_COUNTER, MSR_PARAVIRT_CLOCK, }; static unsigned num_msrs_to_save; @@ -467,6 +468,50 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data) return kvm_set_msr(vcpu, index, *data); } +#define WC_OFFSET offsetof(struct shared_info, wc_version) + +static void kvm_write_guest_time(struct kvm_vcpu *v) +{ + struct timespec ts, wc_ts; + int wc_args[3]; /* version, wc_sec, wc_nsec */ + unsigned long flags; + struct kvm_vcpu_arch *vcpu = &v->arch; + + if (!vcpu->shared_info) + return; + + /* Make the update of both version numbers visible to guest */ + wc_args[0] = ++vcpu->wc_version; + kvm_write_guest(v->kvm, vcpu->shared_info + WC_OFFSET, wc_args, + sizeof(wc_args)); + vcpu->hv_clock.version++; + kvm_write_guest(v->kvm, vcpu->clock_addr, &vcpu->hv_clock, + sizeof(vcpu->hv_clock)); + + /* Keep irq disabled to prevent changes to the clock */ + local_irq_save(flags); + kvm_get_msr(v, MSR_IA32_TIME_STAMP_COUNTER, + &vcpu->hv_clock.tsc_timestamp); + wc_ts = current_kernel_time(); + ktime_get_ts(&ts); + local_irq_restore(flags); + + /* With all the info we got, fill in the values */ + wc_args[1] = wc_ts.tv_sec; + wc_args[2] = wc_ts.tv_nsec; + wc_args[0] = ++vcpu->wc_version; + + vcpu->hv_clock.system_time = ts.tv_nsec + + (NSEC_PER_SEC * (u64)ts.tv_sec); + vcpu->hv_clock.version++; + + /* And finally, let the guest see them */ + kvm_write_guest(v->kvm, vcpu->shared_info + WC_OFFSET, wc_args, + sizeof(wc_args)); + kvm_write_guest(v->kvm, vcpu->clock_addr, &vcpu->hv_clock, + sizeof(vcpu->hv_clock)); +} + int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) { @@ -494,6 +539,18 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) case MSR_IA32_MISC_ENABLE: vcpu->arch.ia32_misc_enable_msr = data; break; + case MSR_PARAVIRT_CLOCK: { + vcpu->arch.shared_info = data; + + vcpu->arch.clock_addr = data + offsetof(struct vcpu_info, time) + + sizeof(struct vcpu_info) * vcpu->vcpu_id; + + vcpu->arch.hv_clock.tsc_to_system_mul = + clocksource_khz2mult(tsc_khz, 22); + vcpu->arch.hv_clock.tsc_shift = 22; + kvm_write_guest_time(vcpu); + break; + } default: pr_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n", msr, data); return 1; @@ -553,6 +610,9 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) data = vcpu->arch.shadow_efer; break; #endif + case MSR_PARAVIRT_CLOCK: + data = vcpu->arch.shared_info; + break; default: pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr); return 1; @@ -680,6 +740,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_USER_MEMORY: case KVM_CAP_SET_TSS_ADDR: case KVM_CAP_EXT_CPUID: + case KVM_CAP_CLOCKSOURCE: r = 1; break; case KVM_CAP_VAPIC: @@ -737,6 +798,7 @@ out: void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { kvm_x86_ops->vcpu_load(vcpu, cpu); + kvm_write_guest_time(vcpu); } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h index d6db0de..bbc4b51 100644 --- a/include/asm-x86/kvm_host.h +++ b/include/asm-x86/kvm_host.h @@ -261,6 +261,11 @@ struct kvm_vcpu_arch { /* emulate context */ struct x86_emulate_ctxt emulate_ctxt; + + struct vcpu_time_info hv_clock; + gpa_t shared_info; + gpa_t clock_addr; /* so we avoid computing it every time */ + int wc_version; }; struct kvm_mem_alias { diff --git a/include/asm-x86/kvm_para.h b/include/asm-x86/kvm_para.h index c6f3fd8..abe412a 100644 --- a/include/asm-x86/kvm_para.h +++ b/include/asm-x86/kvm_para.h @@ -1,5 +1,6 @@ #ifndef __X86_KVM_PARA_H #define __X86_KVM_PARA_H +#include <xen/interface/xen.h> /* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It * should be used to determine that a VM is running under KVM. @@ -10,9 +11,13 @@ * paravirtualization, the appropriate feature bit should be checked. */ #define KVM_CPUID_FEATURES 0x40000001 +#define KVM_FEATURE_CLOCKSOURCE 0 +#define MSR_PARAVIRT_CLOCK 0x11 #ifdef __KERNEL__ #include <asm/processor.h> +extern void kvmclock_init(void); + /* This instruction is vmcall. On non-VT architectures, it will generate a * trap that we will then rewrite to the appropriate instruction. diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 4de4fd2..78ce53f 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -232,6 +232,7 @@ struct kvm_vapic_addr { #define KVM_CAP_SET_TSS_ADDR 4 #define KVM_CAP_EXT_CPUID 5 #define KVM_CAP_VAPIC 6 +#define KVM_CAP_CLOCKSOURCE 7 /* * ioctls for VM fds -- 1.5.0.6 ------------------------------------------------------------------------- Check out the new SourceForge.net Marketplace. It's the best place to buy or sell services for just about anything Open Source. http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel