aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kvm
diff options
context:
space:
mode:
authorNadav Har'El <nyh@il.ibm.com>2011-08-02 08:54:20 -0400
committerAvi Kivity <avi@redhat.com>2011-09-25 12:18:02 -0400
commitd5c1785d2f3aabe284d91bc7fc8f0abc58525dc9 (patch)
tree1db45488a6ecee728245e9af6f4fe59c4dc6ac4a /arch/x86/kvm
parente1a72ae287f3ea4fea63b6f14a662a15e8c11960 (diff)
KVM: L1 TSC handling
KVM assumed in several places that reading the TSC MSR returns the value for L1. This is incorrect, because when L2 is running, the correct TSC read exit emulation is to return L2's value. We therefore add a new x86_ops function, read_l1_tsc, to use in places that specifically need to read the L1 TSC, NOT the TSC of the current level of guest. Note that one change, of one line in kvm_arch_vcpu_load, is made redundant by a different patch sent by Zachary Amsden (and not yet applied): kvm_arch_vcpu_load() should not read the guest TSC, and if it didn't, of course we didn't have to change the call of kvm_get_msr() to read_l1_tsc(). [avi: moved callback to kvm_x86_ops tsc block] Signed-off-by: Nadav Har'El <nyh@il.ibm.com> Acked-by: Zachary Amsdem <zamsden@gmail.com> Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm')
-rw-r--r--arch/x86/kvm/svm.c8
-rw-r--r--arch/x86/kvm/vmx.c16
-rw-r--r--arch/x86/kvm/x86.c8
3 files changed, 28 insertions, 4 deletions
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index f043168a5ab..590d1d2d620 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -2910,6 +2910,13 @@ static int cr8_write_interception(struct vcpu_svm *svm)
2910 return 0; 2910 return 0;
2911} 2911}
2912 2912
2913u64 svm_read_l1_tsc(struct kvm_vcpu *vcpu)
2914{
2915 struct vmcb *vmcb = get_host_vmcb(to_svm(vcpu));
2916 return vmcb->control.tsc_offset +
2917 svm_scale_tsc(vcpu, native_read_tsc());
2918}
2919
2913static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data) 2920static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
2914{ 2921{
2915 struct vcpu_svm *svm = to_svm(vcpu); 2922 struct vcpu_svm *svm = to_svm(vcpu);
@@ -4201,6 +4208,7 @@ static struct kvm_x86_ops svm_x86_ops = {
4201 .write_tsc_offset = svm_write_tsc_offset, 4208 .write_tsc_offset = svm_write_tsc_offset,
4202 .adjust_tsc_offset = svm_adjust_tsc_offset, 4209 .adjust_tsc_offset = svm_adjust_tsc_offset,
4203 .compute_tsc_offset = svm_compute_tsc_offset, 4210 .compute_tsc_offset = svm_compute_tsc_offset,
4211 .read_l1_tsc = svm_read_l1_tsc,
4204 4212
4205 .set_tdp_cr3 = set_tdp_cr3, 4213 .set_tdp_cr3 = set_tdp_cr3,
4206 4214
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 03df703c8f2..97b64543d4e 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -1748,6 +1748,21 @@ static u64 guest_read_tsc(void)
1748} 1748}
1749 1749
1750/* 1750/*
1751 * Like guest_read_tsc, but always returns L1's notion of the timestamp
1752 * counter, even if a nested guest (L2) is currently running.
1753 */
1754u64 vmx_read_l1_tsc(struct kvm_vcpu *vcpu)
1755{
1756 u64 host_tsc, tsc_offset;
1757
1758 rdtscll(host_tsc);
1759 tsc_offset = is_guest_mode(vcpu) ?
1760 to_vmx(vcpu)->nested.vmcs01_tsc_offset :
1761 vmcs_read64(TSC_OFFSET);
1762 return host_tsc + tsc_offset;
1763}
1764
1765/*
1751 * Empty call-back. Needs to be implemented when VMX enables the SET_TSC_KHZ 1766 * Empty call-back. Needs to be implemented when VMX enables the SET_TSC_KHZ
1752 * ioctl. In this case the call-back should update internal vmx state to make 1767 * ioctl. In this case the call-back should update internal vmx state to make
1753 * the changes effective. 1768 * the changes effective.
@@ -7010,6 +7025,7 @@ static struct kvm_x86_ops vmx_x86_ops = {
7010 .write_tsc_offset = vmx_write_tsc_offset, 7025 .write_tsc_offset = vmx_write_tsc_offset,
7011 .adjust_tsc_offset = vmx_adjust_tsc_offset, 7026 .adjust_tsc_offset = vmx_adjust_tsc_offset,
7012 .compute_tsc_offset = vmx_compute_tsc_offset, 7027 .compute_tsc_offset = vmx_compute_tsc_offset,
7028 .read_l1_tsc = vmx_read_l1_tsc,
7013 7029
7014 .set_tdp_cr3 = vmx_set_cr3, 7030 .set_tdp_cr3 = vmx_set_cr3,
7015 7031
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ea8f9f03e92..6b37f18a166 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -1098,7 +1098,7 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
1098 1098
1099 /* Keep irq disabled to prevent changes to the clock */ 1099 /* Keep irq disabled to prevent changes to the clock */
1100 local_irq_save(flags); 1100 local_irq_save(flags);
1101 kvm_get_msr(v, MSR_IA32_TSC, &tsc_timestamp); 1101 tsc_timestamp = kvm_x86_ops->read_l1_tsc(v);
1102 kernel_ns = get_kernel_ns(); 1102 kernel_ns = get_kernel_ns();
1103 this_tsc_khz = vcpu_tsc_khz(v); 1103 this_tsc_khz = vcpu_tsc_khz(v);
1104 if (unlikely(this_tsc_khz == 0)) { 1104 if (unlikely(this_tsc_khz == 0)) {
@@ -2218,7 +2218,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
2218 s64 tsc_delta; 2218 s64 tsc_delta;
2219 u64 tsc; 2219 u64 tsc;
2220 2220
2221 kvm_get_msr(vcpu, MSR_IA32_TSC, &tsc); 2221 tsc = kvm_x86_ops->read_l1_tsc(vcpu);
2222 tsc_delta = !vcpu->arch.last_guest_tsc ? 0 : 2222 tsc_delta = !vcpu->arch.last_guest_tsc ? 0 :
2223 tsc - vcpu->arch.last_guest_tsc; 2223 tsc - vcpu->arch.last_guest_tsc;
2224 2224
@@ -2242,7 +2242,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
2242{ 2242{
2243 kvm_x86_ops->vcpu_put(vcpu); 2243 kvm_x86_ops->vcpu_put(vcpu);
2244 kvm_put_guest_fpu(vcpu); 2244 kvm_put_guest_fpu(vcpu);
2245 kvm_get_msr(vcpu, MSR_IA32_TSC, &vcpu->arch.last_guest_tsc); 2245 vcpu->arch.last_guest_tsc = kvm_x86_ops->read_l1_tsc(vcpu);
2246} 2246}
2247 2247
2248static int is_efer_nx(void) 2248static int is_efer_nx(void)
@@ -5729,7 +5729,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
5729 if (hw_breakpoint_active()) 5729 if (hw_breakpoint_active())
5730 hw_breakpoint_restore(); 5730 hw_breakpoint_restore();
5731 5731
5732 kvm_get_msr(vcpu, MSR_IA32_TSC, &vcpu->arch.last_guest_tsc); 5732 vcpu->arch.last_guest_tsc = kvm_x86_ops->read_l1_tsc(vcpu);
5733 5733
5734 vcpu->mode = OUTSIDE_GUEST_MODE; 5734 vcpu->mode = OUTSIDE_GUEST_MODE;
5735 smp_wmb(); 5735 smp_wmb();