Skip to content

Commit 326e742

Browse files
Leonid Shatzbonzini
authored andcommitted
KVM: nVMX/nSVM: Fix bug which sets vcpu->arch.tsc_offset to L1 tsc_offset
Since commit e79f245 ("X86/KVM: Properly update 'tsc_offset' to represent the running guest"), vcpu->arch.tsc_offset meaning was changed to always reflect the tsc_offset value set on active VMCS. Regardless if vCPU is currently running L1 or L2. However, above mentioned commit failed to also change kvm_vcpu_write_tsc_offset() to set vcpu->arch.tsc_offset correctly. This is because vmx_write_tsc_offset() could set the tsc_offset value in active VMCS to given offset parameter *plus vmcs12->tsc_offset*. However, kvm_vcpu_write_tsc_offset() just sets vcpu->arch.tsc_offset to given offset parameter. Without taking into account the possible addition of vmcs12->tsc_offset. (Same is true for SVM case). Fix this issue by changing kvm_x86_ops->write_tsc_offset() to return actually set tsc_offset in active VMCS and modify kvm_vcpu_write_tsc_offset() to set returned value in vcpu->arch.tsc_offset. In addition, rename write_tsc_offset() callback to write_l1_tsc_offset() to make it clear that it is meant to set L1 TSC offset. Fixes: e79f245 ("X86/KVM: Properly update 'tsc_offset' to represent the running guest") Reviewed-by: Liran Alon <[email protected]> Reviewed-by: Mihai Carabas <[email protected]> Reviewed-by: Krish Sadhukhan <[email protected]> Signed-off-by: Leonid Shatz <[email protected]> Cc: [email protected] Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 1e4329e commit 326e742

File tree

4 files changed

+17
-18
lines changed

4 files changed

+17
-18
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,8 @@ struct kvm_x86_ops {
10941094
bool (*has_wbinvd_exit)(void);
10951095

10961096
u64 (*read_l1_tsc_offset)(struct kvm_vcpu *vcpu);
1097-
void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset);
1097+
/* Returns actual tsc_offset set in active VMCS */
1098+
u64 (*write_l1_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset);
10981099

10991100
void (*get_exit_info)(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2);
11001101

arch/x86/kvm/svm.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,7 +1446,7 @@ static u64 svm_read_l1_tsc_offset(struct kvm_vcpu *vcpu)
14461446
return vcpu->arch.tsc_offset;
14471447
}
14481448

1449-
static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
1449+
static u64 svm_write_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
14501450
{
14511451
struct vcpu_svm *svm = to_svm(vcpu);
14521452
u64 g_tsc_offset = 0;
@@ -1464,6 +1464,7 @@ static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
14641464
svm->vmcb->control.tsc_offset = offset + g_tsc_offset;
14651465

14661466
mark_dirty(svm->vmcb, VMCB_INTERCEPTS);
1467+
return svm->vmcb->control.tsc_offset;
14671468
}
14681469

14691470
static void avic_init_vmcb(struct vcpu_svm *svm)
@@ -7152,7 +7153,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
71527153
.has_wbinvd_exit = svm_has_wbinvd_exit,
71537154

71547155
.read_l1_tsc_offset = svm_read_l1_tsc_offset,
7155-
.write_tsc_offset = svm_write_tsc_offset,
7156+
.write_l1_tsc_offset = svm_write_l1_tsc_offset,
71567157

71577158
.set_tdp_cr3 = set_tdp_cr3,
71587159

arch/x86/kvm/vmx.c

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3464,29 +3464,26 @@ static u64 vmx_read_l1_tsc_offset(struct kvm_vcpu *vcpu)
34643464
return vcpu->arch.tsc_offset;
34653465
}
34663466

3467-
/*
3468-
* writes 'offset' into guest's timestamp counter offset register
3469-
*/
3470-
static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
3467+
static u64 vmx_write_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
34713468
{
3469+
u64 active_offset = offset;
34723470
if (is_guest_mode(vcpu)) {
34733471
/*
34743472
* We're here if L1 chose not to trap WRMSR to TSC. According
34753473
* to the spec, this should set L1's TSC; The offset that L1
34763474
* set for L2 remains unchanged, and still needs to be added
34773475
* to the newly set TSC to get L2's TSC.
34783476
*/
3479-
struct vmcs12 *vmcs12;
3480-
/* recalculate vmcs02.TSC_OFFSET: */
3481-
vmcs12 = get_vmcs12(vcpu);
3482-
vmcs_write64(TSC_OFFSET, offset +
3483-
(nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING) ?
3484-
vmcs12->tsc_offset : 0));
3477+
struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
3478+
if (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING))
3479+
active_offset += vmcs12->tsc_offset;
34853480
} else {
34863481
trace_kvm_write_tsc_offset(vcpu->vcpu_id,
34873482
vmcs_read64(TSC_OFFSET), offset);
3488-
vmcs_write64(TSC_OFFSET, offset);
34893483
}
3484+
3485+
vmcs_write64(TSC_OFFSET, active_offset);
3486+
return active_offset;
34903487
}
34913488

34923489
/*
@@ -15074,7 +15071,7 @@ static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
1507415071
.has_wbinvd_exit = cpu_has_vmx_wbinvd_exit,
1507515072

1507615073
.read_l1_tsc_offset = vmx_read_l1_tsc_offset,
15077-
.write_tsc_offset = vmx_write_tsc_offset,
15074+
.write_l1_tsc_offset = vmx_write_l1_tsc_offset,
1507815075

1507915076
.set_tdp_cr3 = vmx_set_cr3,
1508015077

arch/x86/kvm/x86.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,8 +1665,7 @@ EXPORT_SYMBOL_GPL(kvm_read_l1_tsc);
16651665

16661666
static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset)
16671667
{
1668-
kvm_x86_ops->write_tsc_offset(vcpu, offset);
1669-
vcpu->arch.tsc_offset = offset;
1668+
vcpu->arch.tsc_offset = kvm_x86_ops->write_l1_tsc_offset(vcpu, offset);
16701669
}
16711670

16721671
static inline bool kvm_check_tsc_unstable(void)
@@ -1794,7 +1793,8 @@ EXPORT_SYMBOL_GPL(kvm_write_tsc);
17941793
static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu,
17951794
s64 adjustment)
17961795
{
1797-
kvm_vcpu_write_tsc_offset(vcpu, vcpu->arch.tsc_offset + adjustment);
1796+
u64 tsc_offset = kvm_x86_ops->read_l1_tsc_offset(vcpu);
1797+
kvm_vcpu_write_tsc_offset(vcpu, tsc_offset + adjustment);
17981798
}
17991799

18001800
static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment)

0 commit comments

Comments
 (0)