Message ID | 20181108095130.64904-1-liran.alon@oracle.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v2,1/2] KVM: nVMX/nSVM: Fix bug which sets vcpu->arch.tsc_offset to L1 tsc_offset | expand |
On 08/11/18 10:51, Liran Alon wrote: > From: Leonid Shatz <leonid.shatz@oracle.com> > > Since commit e79f245ddec1 ("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() argument to clearly indicate > it specifies a L1 TSC offset. > > Fixes: e79f245ddec1 ("X86/KVM: Properly update 'tsc_offset' to represent the running guest") > > Reviewed-by: Liran Alon <liran.alon@oracle.com> > Reviewed-by: Mihai Carabas <mihai.carabas@oracle.com> > Reviewed-by: Krish Sadhukhan <krish.sadhukhan@oracle.com> > Signed-off-by: Leonid Shatz <leonid.shatz@oracle.com> > --- > arch/x86/include/asm/kvm_host.h | 6 +++++- > arch/x86/kvm/svm.c | 9 +++++---- > arch/x86/kvm/vmx.c | 21 +++++++++------------ > arch/x86/kvm/x86.c | 8 ++++---- > 4 files changed, 23 insertions(+), 21 deletions(-) > > diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h > index 55e51ff7e421..cc9df79b1016 100644 > --- a/arch/x86/include/asm/kvm_host.h > +++ b/arch/x86/include/asm/kvm_host.h > @@ -1094,7 +1094,11 @@ struct kvm_x86_ops { > bool (*has_wbinvd_exit)(void); > > u64 (*read_l1_tsc_offset)(struct kvm_vcpu *vcpu); > - void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset); > + /* > + * Updates active guest TSC offset based on given L1 TSC offset > + * and returns actual TSC offset set for the active guest > + */ > + u64 (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 l1_offset); > > void (*get_exit_info)(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2); > > diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c > index 0e21ccc46792..842d4bb053d1 100644 > --- a/arch/x86/kvm/svm.c > +++ b/arch/x86/kvm/svm.c > @@ -1446,7 +1446,7 @@ static u64 svm_read_l1_tsc_offset(struct kvm_vcpu *vcpu) > return vcpu->arch.tsc_offset; > } > > -static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) > +static u64 svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) > { > struct vcpu_svm *svm = to_svm(vcpu); > u64 g_tsc_offset = 0; > @@ -1455,15 +1455,16 @@ static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) > /* Write L1's TSC offset. */ > g_tsc_offset = svm->vmcb->control.tsc_offset - > svm->nested.hsave->control.tsc_offset; > - svm->nested.hsave->control.tsc_offset = offset; > + svm->nested.hsave->control.tsc_offset = l1_offset; > } else > trace_kvm_write_tsc_offset(vcpu->vcpu_id, > svm->vmcb->control.tsc_offset, > - offset); > + l1_offset); > > - svm->vmcb->control.tsc_offset = offset + g_tsc_offset; > + svm->vmcb->control.tsc_offset = l1_offset + g_tsc_offset; > > mark_dirty(svm->vmcb, VMCB_INTERCEPTS); > + return svm->vmcb->control.tsc_offset; > } > > static void avic_init_vmcb(struct vcpu_svm *svm) > diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c > index 4555077d69ce..20d90ebd6cc8 100644 > --- a/arch/x86/kvm/vmx.c > +++ b/arch/x86/kvm/vmx.c > @@ -3452,11 +3452,9 @@ static u64 vmx_read_l1_tsc_offset(struct kvm_vcpu *vcpu) > return vcpu->arch.tsc_offset; > } > > -/* > - * writes 'offset' into guest's timestamp counter offset register > - */ > -static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) > +static u64 vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) > { > + u64 active_offset = l1_offset; > if (is_guest_mode(vcpu)) { > /* > * We're here if L1 chose not to trap WRMSR to TSC. According > @@ -3464,17 +3462,16 @@ static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) > * set for L2 remains unchanged, and still needs to be added > * to the newly set TSC to get L2's TSC. > */ > - struct vmcs12 *vmcs12; > - /* recalculate vmcs02.TSC_OFFSET: */ > - vmcs12 = get_vmcs12(vcpu); > - vmcs_write64(TSC_OFFSET, offset + > - (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING) ? > - vmcs12->tsc_offset : 0)); > + struct vmcs12 *vmcs12 = get_vmcs12(vcpu); > + if (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING)) > + active_offset += vmcs12->tsc_offset; > } else { > trace_kvm_write_tsc_offset(vcpu->vcpu_id, > - vmcs_read64(TSC_OFFSET), offset); > - vmcs_write64(TSC_OFFSET, offset); > + vmcs_read64(TSC_OFFSET), l1_offset); > } > + > + vmcs_write64(TSC_OFFSET, active_offset); > + return active_offset; > } > > /* > diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c > index 8525dc75a79a..c4e80af1a54e 100644 > --- a/arch/x86/kvm/x86.c > +++ b/arch/x86/kvm/x86.c > @@ -1766,10 +1766,9 @@ u64 kvm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc) > } > EXPORT_SYMBOL_GPL(kvm_read_l1_tsc); > > -static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) > +static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) > { > - kvm_x86_ops->write_tsc_offset(vcpu, offset); > - vcpu->arch.tsc_offset = offset; > + vcpu->arch.tsc_offset = kvm_x86_ops->write_tsc_offset(vcpu, l1_offset); > } > > static inline bool kvm_check_tsc_unstable(void) > @@ -1897,7 +1896,8 @@ EXPORT_SYMBOL_GPL(kvm_write_tsc); > static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu, > s64 adjustment) > { > - kvm_vcpu_write_tsc_offset(vcpu, vcpu->arch.tsc_offset + adjustment); > + u64 l1_tsc_offset = kvm_x86_ops->read_l1_tsc_offset(vcpu); > + kvm_vcpu_write_tsc_offset(vcpu, l1_tsc_offset + adjustment); > } > > static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment) > I queued v1, and will send a replacement for patch 2 of this miniseries. Paoo
> On 25 Nov 2018, at 19:47, Paolo Bonzini <pbonzini@redhat.com> wrote: > > On 08/11/18 10:51, Liran Alon wrote: >> From: Leonid Shatz <leonid.shatz@oracle.com> >> >> Since commit e79f245ddec1 ("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() argument to clearly indicate >> it specifies a L1 TSC offset. >> >> Fixes: e79f245ddec1 ("X86/KVM: Properly update 'tsc_offset' to represent the running guest") >> >> Reviewed-by: Liran Alon <liran.alon@oracle.com> >> Reviewed-by: Mihai Carabas <mihai.carabas@oracle.com> >> Reviewed-by: Krish Sadhukhan <krish.sadhukhan@oracle.com> >> Signed-off-by: Leonid Shatz <leonid.shatz@oracle.com> > > I queued v1, and will send a replacement for patch 2 of this miniseries. > > Paoo Paolo, I think this commit should go to the stable tree as it fixes a regression issue. -Liran
On 28/11/18 17:05, Liran Alon wrote: > > >> On 25 Nov 2018, at 19:47, Paolo Bonzini <pbonzini@redhat.com> wrote: >> >> On 08/11/18 10:51, Liran Alon wrote: >>> From: Leonid Shatz <leonid.shatz@oracle.com> >>> >>> Since commit e79f245ddec1 ("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() argument to clearly indicate >>> it specifies a L1 TSC offset. >>> >>> Fixes: e79f245ddec1 ("X86/KVM: Properly update 'tsc_offset' to represent the running guest") >>> >>> Reviewed-by: Liran Alon <liran.alon@oracle.com> >>> Reviewed-by: Mihai Carabas <mihai.carabas@oracle.com> >>> Reviewed-by: Krish Sadhukhan <krish.sadhukhan@oracle.com> >>> Signed-off-by: Leonid Shatz <leonid.shatz@oracle.com> >> >> I queued v1, and will send a replacement for patch 2 of this miniseries. >> >> Paoo > > Paolo, I think this commit should go to the stable tree as it fixes a regression issue. Yes, I added the stable tag in my pull request to Linus. Paolo
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 55e51ff7e421..cc9df79b1016 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1094,7 +1094,11 @@ struct kvm_x86_ops { bool (*has_wbinvd_exit)(void); u64 (*read_l1_tsc_offset)(struct kvm_vcpu *vcpu); - void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset); + /* + * Updates active guest TSC offset based on given L1 TSC offset + * and returns actual TSC offset set for the active guest + */ + u64 (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 l1_offset); void (*get_exit_info)(struct kvm_vcpu *vcpu, u64 *info1, u64 *info2); diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 0e21ccc46792..842d4bb053d1 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -1446,7 +1446,7 @@ static u64 svm_read_l1_tsc_offset(struct kvm_vcpu *vcpu) return vcpu->arch.tsc_offset; } -static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) +static u64 svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) { struct vcpu_svm *svm = to_svm(vcpu); u64 g_tsc_offset = 0; @@ -1455,15 +1455,16 @@ static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) /* Write L1's TSC offset. */ g_tsc_offset = svm->vmcb->control.tsc_offset - svm->nested.hsave->control.tsc_offset; - svm->nested.hsave->control.tsc_offset = offset; + svm->nested.hsave->control.tsc_offset = l1_offset; } else trace_kvm_write_tsc_offset(vcpu->vcpu_id, svm->vmcb->control.tsc_offset, - offset); + l1_offset); - svm->vmcb->control.tsc_offset = offset + g_tsc_offset; + svm->vmcb->control.tsc_offset = l1_offset + g_tsc_offset; mark_dirty(svm->vmcb, VMCB_INTERCEPTS); + return svm->vmcb->control.tsc_offset; } static void avic_init_vmcb(struct vcpu_svm *svm) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 4555077d69ce..20d90ebd6cc8 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3452,11 +3452,9 @@ static u64 vmx_read_l1_tsc_offset(struct kvm_vcpu *vcpu) return vcpu->arch.tsc_offset; } -/* - * writes 'offset' into guest's timestamp counter offset register - */ -static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) +static u64 vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) { + u64 active_offset = l1_offset; if (is_guest_mode(vcpu)) { /* * We're here if L1 chose not to trap WRMSR to TSC. According @@ -3464,17 +3462,16 @@ static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) * set for L2 remains unchanged, and still needs to be added * to the newly set TSC to get L2's TSC. */ - struct vmcs12 *vmcs12; - /* recalculate vmcs02.TSC_OFFSET: */ - vmcs12 = get_vmcs12(vcpu); - vmcs_write64(TSC_OFFSET, offset + - (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING) ? - vmcs12->tsc_offset : 0)); + struct vmcs12 *vmcs12 = get_vmcs12(vcpu); + if (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING)) + active_offset += vmcs12->tsc_offset; } else { trace_kvm_write_tsc_offset(vcpu->vcpu_id, - vmcs_read64(TSC_OFFSET), offset); - vmcs_write64(TSC_OFFSET, offset); + vmcs_read64(TSC_OFFSET), l1_offset); } + + vmcs_write64(TSC_OFFSET, active_offset); + return active_offset; } /* diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8525dc75a79a..c4e80af1a54e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1766,10 +1766,9 @@ u64 kvm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc) } EXPORT_SYMBOL_GPL(kvm_read_l1_tsc); -static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) +static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) { - kvm_x86_ops->write_tsc_offset(vcpu, offset); - vcpu->arch.tsc_offset = offset; + vcpu->arch.tsc_offset = kvm_x86_ops->write_tsc_offset(vcpu, l1_offset); } static inline bool kvm_check_tsc_unstable(void) @@ -1897,7 +1896,8 @@ EXPORT_SYMBOL_GPL(kvm_write_tsc); static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu, s64 adjustment) { - kvm_vcpu_write_tsc_offset(vcpu, vcpu->arch.tsc_offset + adjustment); + u64 l1_tsc_offset = kvm_x86_ops->read_l1_tsc_offset(vcpu); + kvm_vcpu_write_tsc_offset(vcpu, l1_tsc_offset + adjustment); } static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment)