diff mbox series

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

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

Commit Message

Liran Alon Nov. 8, 2018, 9:51 a.m. UTC
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(-)

Comments

Paolo Bonzini Nov. 25, 2018, 5:47 p.m. UTC | #1
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
Liran Alon Nov. 28, 2018, 4:05 p.m. UTC | #2
> 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
Paolo Bonzini Nov. 28, 2018, 4:39 p.m. UTC | #3
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 mbox series

Patch

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)