On Tuesday, November 27, 2012 11:13:12 PM George-Cristian Bîrzan wrote: > On Tue, Nov 27, 2012 at 10:38 PM, Vadim Rozenfeld <vrozenfe@xxxxxxxxxx> wrote: > > I have some code which do both reference time and invariant TSC but it > > will not work after migration. I will send it later today. > > Do you mean migrating guests? This is not an issue for us. OK, but don't say I didn't warn you :) There are two patches, one for kvm and another one for qemu. you will probably need to rebase them. Add "hv_tsc" cpu parameter to activate this feature. you will probably need to deactivate hpet by adding "-no-hpet" parameter as well. best regards, Vadim. > > Also, it would be much appreciated! > > -- > George-Cristian Bîrzan
diff --git a/arch/x86/include/asm/hyperv.h b/arch/x86/include/asm/hyperv.h index b80420b..9c5ffef 100644 --- a/arch/x86/include/asm/hyperv.h +++ b/arch/x86/include/asm/hyperv.h @@ -136,6 +136,9 @@ /* MSR used to read the per-partition time reference counter */ #define HV_X64_MSR_TIME_REF_COUNT 0x40000020 +/* A partition's reference time stamp counter (TSC) page */ +#define HV_X64_MSR_REFERENCE_TSC 0x40000021 + /* Define the virtual APIC registers */ #define HV_X64_MSR_EOI 0x40000070 #define HV_X64_MSR_ICR 0x40000071 @@ -179,6 +182,10 @@ #define HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_MASK \ (~((1ull << HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT) - 1)) +#define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001 +#define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12 + + #define HV_PROCESSOR_POWER_STATE_C0 0 #define HV_PROCESSOR_POWER_STATE_C1 1 #define HV_PROCESSOR_POWER_STATE_C2 2 @@ -191,4 +198,11 @@ #define HV_STATUS_INVALID_ALIGNMENT 4 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 +typedef struct _HV_REFERENCE_TSC_PAGE { + uint32_t TscSequence; + uint32_t Rserved1; + uint64_t TscScale; + int64_t TscOffset; +} HV_REFERENCE_TSC_PAGE, * PHV_REFERENCE_TSC_PAGE; + #endif diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index b2e11f4..63ee09e 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -565,6 +565,8 @@ struct kvm_arch { /* fields used by HYPER-V emulation */ u64 hv_guest_os_id; u64 hv_hypercall; + u64 hv_ref_count; + u64 hv_tsc_page; #ifdef CONFIG_KVM_MMU_AUDIT int audit_point; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4f76417..4538295 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -813,7 +813,7 @@ EXPORT_SYMBOL_GPL(kvm_rdpmc); static u32 msrs_to_save[] = { MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK, MSR_KVM_SYSTEM_TIME_NEW, MSR_KVM_WALL_CLOCK_NEW, - HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL, + HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL, HV_X64_MSR_REFERENCE_TSC, HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME, MSR_KVM_PV_EOI_EN, MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP, @@ -1428,6 +1428,8 @@ static bool kvm_hv_msr_partition_wide(u32 msr) switch (msr) { case HV_X64_MSR_GUEST_OS_ID: case HV_X64_MSR_HYPERCALL: + case HV_X64_MSR_TIME_REF_COUNT: + case HV_X64_MSR_REFERENCE_TSC: r = true; break; } @@ -1438,6 +1440,7 @@ static bool kvm_hv_msr_partition_wide(u32 msr) static int set_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data) { struct kvm *kvm = vcpu->kvm; + unsigned long addr; switch (msr) { case HV_X64_MSR_GUEST_OS_ID: @@ -1467,6 +1470,27 @@ static int set_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data) if (__copy_to_user((void __user *)addr, instructions, 4)) return 1; kvm->arch.hv_hypercall = data; + kvm->arch.hv_ref_count = get_kernel_ns(); + break; + } + case HV_X64_MSR_REFERENCE_TSC: { + HV_REFERENCE_TSC_PAGE tsc_ref; + tsc_ref.TscSequence = + boot_cpu_has(X86_FEATURE_CONSTANT_TSC) ? 1 : 0; + tsc_ref.TscScale = + ((10000LL << 32) /vcpu->arch.virtual_tsc_khz) << 32; + tsc_ref.TscOffset = 0; + if (!(data & HV_X64_MSR_TSC_REFERENCE_ENABLE)) { + kvm->arch.hv_tsc_page = data; + break; + } + addr = gfn_to_hva(vcpu->kvm, data >> + HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT); + if (kvm_is_error_hva(addr)) + return 1; + if(__copy_to_user((void __user *)addr, &tsc_ref, sizeof(tsc_ref))) + return 1; + kvm->arch.hv_tsc_page = data; break; } default: @@ -1881,6 +1905,13 @@ static int get_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) case HV_X64_MSR_HYPERCALL: data = kvm->arch.hv_hypercall; break; + case HV_X64_MSR_TIME_REF_COUNT: + data = get_kernel_ns() - kvm->arch.hv_ref_count; + do_div(data, 100); + break; + case HV_X64_MSR_REFERENCE_TSC: + data = kvm->arch.hv_tsc_page; + break; default: vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); return 1;
diff --git a/target-i386/cpu.c b/target-i386/cpu.c index f3708e6..ad77b72 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -1250,6 +1250,8 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) hyperv_enable_relaxed_timing(true); } else if (!strcmp(featurestr, "hv_vapic")) { hyperv_enable_vapic_recommended(true); + } else if (!strcmp(featurestr, "hv_tsc")) { + hyperv_enable_tsc_recommended(true); } else { fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr); goto error; diff --git a/target-i386/hyperv.c b/target-i386/hyperv.c index f284e99..bd581a1 100644 --- a/target-i386/hyperv.c +++ b/target-i386/hyperv.c @@ -15,6 +15,12 @@ static bool hyperv_vapic; static bool hyperv_relaxed_timing; static int hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY; +static bool hyperv_tsc; + +void hyperv_enable_tsc_recommended(bool val) +{ + hyperv_tsc = val; +} void hyperv_enable_vapic_recommended(bool val) { @@ -42,12 +48,18 @@ bool hyperv_enabled(void) bool hyperv_hypercall_available(void) { if (hyperv_vapic || + hyperv_tsc || (hyperv_spinlock_attempts != HYPERV_SPINLOCK_NEVER_RETRY)) { return true; } return false; } +bool hyperv_tsc_recommended(void) +{ + return hyperv_tsc; +} + bool hyperv_vapic_recommended(void) { return hyperv_vapic; diff --git a/target-i386/hyperv.h b/target-i386/hyperv.h index bacb1d4..94c2d6e 100644 --- a/target-i386/hyperv.h +++ b/target-i386/hyperv.h @@ -27,10 +27,12 @@ #endif #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_KVM) +void hyperv_enable_tsc_recommended(bool val); void hyperv_enable_vapic_recommended(bool val); void hyperv_enable_relaxed_timing(bool val); void hyperv_set_spinlock_retries(int val); #else +static inline void hyperv_enable_tsc_recommended(bool val) { } static inline void hyperv_enable_vapic_recommended(bool val) { } static inline void hyperv_enable_relaxed_timing(bool val) { } static inline void hyperv_set_spinlock_retries(int val) { } @@ -38,6 +40,7 @@ static inline void hyperv_set_spinlock_retries(int val) { } bool hyperv_enabled(void); bool hyperv_hypercall_available(void); +bool hyperv_tsc_recommended(void); bool hyperv_vapic_recommended(void); bool hyperv_relaxed_timing_enabled(void); int hyperv_get_spinlock_retries(void); diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 5b18383..dc7f259 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -390,13 +390,17 @@ int kvm_arch_init_vcpu(CPUX86State *env) c = &cpuid_data.entries[cpuid_i++]; memset(c, 0, sizeof(*c)); c->function = KVM_CPUID_SIGNATURE; - if (!hyperv_enabled()) { - memcpy(signature, "KVMKVMKVM\0\0\0", 12); - c->eax = 0; - } else { - memcpy(signature, "Microsoft Hv", 12); + memcpy(signature, "KVMKVMKVM\0\0\0", 12); + if (hyperv_enabled()) { c->eax = HYPERV_CPUID_MIN; } +// if (!hyperv_enabled()) { +// memcpy(signature, "KVMKVMKVM\0\0\0", 12); +// c->eax = 0; +// } else { +// memcpy(signature, "Microsoft Hv", 12); +// c->eax = HYPERV_CPUID_MIN; +// } c->ebx = signature[0]; c->ecx = signature[1]; c->edx = signature[2]; @@ -427,7 +431,11 @@ int kvm_arch_init_vcpu(CPUX86State *env) c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; c->eax |= HV_X64_MSR_APIC_ACCESS_AVAILABLE; } - + if (hyperv_tsc_recommended()) { + c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; + c->eax |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE; + c->eax |= 0x200; + } c = &cpuid_data.entries[cpuid_i++]; memset(c, 0, sizeof(*c)); c->function = HYPERV_CPUID_ENLIGHTMENT_INFO; @@ -445,14 +453,14 @@ int kvm_arch_init_vcpu(CPUX86State *env) c->eax = 0x40; c->ebx = 0x40; - c = &cpuid_data.entries[cpuid_i++]; - memset(c, 0, sizeof(*c)); - c->function = KVM_CPUID_SIGNATURE_NEXT; - memcpy(signature, "KVMKVMKVM\0\0\0", 12); - c->eax = 0; - c->ebx = signature[0]; - c->ecx = signature[1]; - c->edx = signature[2]; +// c = &cpuid_data.entries[cpuid_i++]; +// memset(c, 0, sizeof(*c)); +// c->function = KVM_CPUID_SIGNATURE_NEXT; +// memcpy(signature, "KVMKVMKVM\0\0\0", 12); +// c->eax = 0; +// c->ebx = signature[0]; +// c->ecx = signature[1]; +// c->edx = signature[2]; } has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF);