Re: [PATCH v2 1/2] KVM: nVMX/nSVM: Fix bug which sets vcpu->arch.tsc_offset to L1 tsc_offset

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 08/11/18 10:51, Liran Alon wrote:
> From: Leonid Shatz <leonid.shatz@xxxxxxxxxx>
> 
> 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@xxxxxxxxxx>
> Reviewed-by: Mihai Carabas <mihai.carabas@xxxxxxxxxx>
> Reviewed-by: Krish Sadhukhan <krish.sadhukhan@xxxxxxxxxx>
> Signed-off-by: Leonid Shatz <leonid.shatz@xxxxxxxxxx>
> ---
>  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



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux