KVM: x86: nSVM: implement nested TSC scaling
authorMaxim Levitsky <mlevitsk@redhat.com>
Tue, 14 Sep 2021 15:48:24 +0000 (18:48 +0300)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 1 Oct 2021 07:44:46 +0000 (03:44 -0400)
This was tested by booting a nested guest with TSC=1Ghz,
observing the clocks, and doing about 100 cycles of migration.

Note that qemu patch is needed to support migration because
of a new MSR that needs to be placed in the migration state.

The patch will be sent to the qemu mailing list soon.

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Message-Id: <20210914154825.104886-14-mlevitsk@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/svm/svm.h
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/x86.c

index ec3e926..31fd4bd 100644 (file)
@@ -538,8 +538,17 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
        if (nested_npt_enabled(svm))
                nested_svm_init_mmu_context(vcpu);
 
-       svm->vmcb->control.tsc_offset = vcpu->arch.tsc_offset =
-               vcpu->arch.l1_tsc_offset + svm->nested.ctl.tsc_offset;
+       vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset(
+                       vcpu->arch.l1_tsc_offset,
+                       svm->nested.ctl.tsc_offset,
+                       svm->tsc_ratio_msr);
+
+       svm->vmcb->control.tsc_offset = vcpu->arch.tsc_offset;
+
+       if (svm->tsc_ratio_msr != kvm_default_tsc_scaling_ratio) {
+               WARN_ON(!svm->tsc_scaling_enabled);
+               nested_svm_update_tsc_ratio_msr(vcpu);
+       }
 
        svm->vmcb->control.int_ctl             =
                (svm->nested.ctl.int_ctl & int_ctl_vmcb12_bits) |
@@ -824,6 +833,12 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
                vmcb_mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
        }
 
+       if (svm->tsc_ratio_msr != kvm_default_tsc_scaling_ratio) {
+               WARN_ON(!svm->tsc_scaling_enabled);
+               vcpu->arch.tsc_scaling_ratio = vcpu->arch.l1_tsc_scaling_ratio;
+               svm_write_tsc_multiplier(vcpu, vcpu->arch.tsc_scaling_ratio);
+       }
+
        svm->nested.ctl.nested_cr3 = 0;
 
        /*
@@ -1211,6 +1226,16 @@ int nested_svm_exit_special(struct vcpu_svm *svm)
        return NESTED_EXIT_CONTINUE;
 }
 
+void nested_svm_update_tsc_ratio_msr(struct kvm_vcpu *vcpu)
+{
+       struct vcpu_svm *svm = to_svm(vcpu);
+
+       vcpu->arch.tsc_scaling_ratio =
+               kvm_calc_nested_tsc_multiplier(vcpu->arch.l1_tsc_scaling_ratio,
+                                              svm->tsc_ratio_msr);
+       svm_write_tsc_multiplier(vcpu, vcpu->arch.tsc_scaling_ratio);
+}
+
 static int svm_get_nested_state(struct kvm_vcpu *vcpu,
                                struct kvm_nested_state __user *user_kvm_nested_state,
                                u32 user_data_size)
index 1ff9046..8907716 100644 (file)
@@ -940,6 +940,9 @@ static __init void svm_set_cpu_caps(void)
                if (npt_enabled)
                        kvm_cpu_cap_set(X86_FEATURE_NPT);
 
+               if (tsc_scaling)
+                       kvm_cpu_cap_set(X86_FEATURE_TSCRATEMSR);
+
                /* Nested VM can receive #VMEXIT instead of triggering #GP */
                kvm_cpu_cap_set(X86_FEATURE_SVME_ADDR_CHK);
        }
@@ -1132,7 +1135,9 @@ static u64 svm_get_l2_tsc_offset(struct kvm_vcpu *vcpu)
 
 static u64 svm_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu)
 {
-       return kvm_default_tsc_scaling_ratio;
+       struct vcpu_svm *svm = to_svm(vcpu);
+
+       return svm->tsc_ratio_msr;
 }
 
 static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
@@ -1144,7 +1149,7 @@ static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
        vmcb_mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
 }
 
-static void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 multiplier)
+void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 multiplier)
 {
        wrmsrl(MSR_AMD64_TSC_RATIO, multiplier);
 }
@@ -1356,7 +1361,6 @@ static void init_vmcb(struct kvm_vcpu *vcpu)
        vmcb_mark_all_dirty(svm->vmcb);
 
        enable_gif(svm);
-
 }
 
 static void __svm_vcpu_reset(struct kvm_vcpu *vcpu)
@@ -1367,6 +1371,7 @@ static void __svm_vcpu_reset(struct kvm_vcpu *vcpu)
 
        svm_init_osvw(vcpu);
        vcpu->arch.microcode_version = 0x01000065;
+       svm->tsc_ratio_msr = kvm_default_tsc_scaling_ratio;
 
        if (sev_es_guest(vcpu->kvm))
                sev_es_vcpu_reset(svm);
@@ -2718,6 +2723,11 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        struct vcpu_svm *svm = to_svm(vcpu);
 
        switch (msr_info->index) {
+       case MSR_AMD64_TSC_RATIO:
+               if (!msr_info->host_initiated && !svm->tsc_scaling_enabled)
+                       return 1;
+               msr_info->data = svm->tsc_ratio_msr;
+               break;
        case MSR_STAR:
                msr_info->data = svm->vmcb01.ptr->save.star;
                break;
@@ -2867,6 +2877,19 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
        u32 ecx = msr->index;
        u64 data = msr->data;
        switch (ecx) {
+       case MSR_AMD64_TSC_RATIO:
+               if (!msr->host_initiated && !svm->tsc_scaling_enabled)
+                       return 1;
+
+               if (data & TSC_RATIO_RSVD)
+                       return 1;
+
+               svm->tsc_ratio_msr = data;
+
+               if (svm->tsc_scaling_enabled && is_guest_mode(vcpu))
+                       nested_svm_update_tsc_ratio_msr(vcpu);
+
+               break;
        case MSR_IA32_CR_PAT:
                if (!kvm_mtrr_valid(vcpu, MSR_IA32_CR_PAT, data))
                        return 1;
@@ -4062,6 +4085,8 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
        svm->nrips_enabled = kvm_cpu_cap_has(X86_FEATURE_NRIPS) &&
                             guest_cpuid_has(vcpu, X86_FEATURE_NRIPS);
 
+       svm->tsc_scaling_enabled = tsc_scaling && guest_cpuid_has(vcpu, X86_FEATURE_TSCRATEMSR);
+
        svm_recalc_instruction_intercepts(vcpu, svm);
 
        /* For sev guests, the memory encryption bit is not reserved in CR3.  */
index c19859e..0d7bbe5 100644 (file)
@@ -140,6 +140,8 @@ struct vcpu_svm {
        u64 next_rip;
 
        u64 spec_ctrl;
+
+       u64 tsc_ratio_msr;
        /*
         * Contains guest-controlled bits of VIRT_SPEC_CTRL, which will be
         * translated into the appropriate L2_CFG bits on the host to
@@ -160,7 +162,8 @@ struct vcpu_svm {
        unsigned long int3_rip;
 
        /* cached guest cpuid flags for faster access */
-       bool nrips_enabled      : 1;
+       bool nrips_enabled                : 1;
+       bool tsc_scaling_enabled          : 1;
 
        u32 ldr_reg;
        u32 dfr_reg;
@@ -483,6 +486,8 @@ int nested_svm_check_permissions(struct kvm_vcpu *vcpu);
 int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr,
                               bool has_error_code, u32 error_code);
 int nested_svm_exit_special(struct vcpu_svm *svm);
+void nested_svm_update_tsc_ratio_msr(struct kvm_vcpu *vcpu);
+void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 multiplier);
 void nested_load_control_from_vmcb12(struct vcpu_svm *svm,
                                     struct vmcb_control_area *control);
 void nested_sync_control_from_vmcb02(struct vcpu_svm *svm);
index b8bfb46..1c8b2b6 100644 (file)
@@ -6435,6 +6435,7 @@ static bool vmx_has_emulated_msr(struct kvm *kvm, u32 index)
        case MSR_IA32_VMX_BASIC ... MSR_IA32_VMX_VMFUNC:
                return nested;
        case MSR_AMD64_VIRT_SPEC_CTRL:
+       case MSR_AMD64_TSC_RATIO:
                /* This is AMD only.  */
                return false;
        default:
index ee1b9e1..c028ff7 100644 (file)
@@ -1361,6 +1361,7 @@ static const u32 emulated_msrs_all[] = {
        MSR_PLATFORM_INFO,
        MSR_MISC_FEATURES_ENABLES,
        MSR_AMD64_VIRT_SPEC_CTRL,
+       MSR_AMD64_TSC_RATIO,
        MSR_IA32_POWER_CTL,
        MSR_IA32_UCODE_REV,