All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required
@ 2022-03-15  9:35 Hou Wenlong
  2022-03-16  2:19 ` Lai Jiangshan
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Hou Wenlong @ 2022-03-15  9:35 UTC (permalink / raw)
  To: kvm
  Cc: Paolo Bonzini, Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li,
	Jim Mattson, Joerg Roedel, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Lai Jiangshan,
	linux-kernel

Before Commit c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page()
to return true when remote flush is needed"), the return value
of kvm_sync_page() indicates whether the page is synced, and
kvm_mmu_get_page() would rebuild page when the sync fails.
But now, kvm_sync_page() returns false when the page is
synced and no tlb flushing is required, which leads to
rebuild page in kvm_mmu_get_page(). So return the return
value of mmu->sync_page() directly and check it in
kvm_mmu_get_page(). If the sync fails, the page will be
zapped and the invalid_list is not empty, so set flush as
true is accepted in mmu_sync_children().

Fixes: c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page() to return true when remote flush is needed")
Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
---
 arch/x86/kvm/mmu/mmu.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 3b8da8b0745e..8efd165ee27c 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -1866,17 +1866,14 @@ static void kvm_mmu_commit_zap_page(struct kvm *kvm,
 	  &(_kvm)->arch.mmu_page_hash[kvm_page_table_hashfn(_gfn)])	\
 		if ((_sp)->gfn != (_gfn) || (_sp)->role.direct) {} else
 
-static bool kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
+static int kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
 			 struct list_head *invalid_list)
 {
 	int ret = vcpu->arch.mmu->sync_page(vcpu, sp);
 
-	if (ret < 0) {
+	if (ret < 0)
 		kvm_mmu_prepare_zap_page(vcpu->kvm, sp, invalid_list);
-		return false;
-	}
-
-	return !!ret;
+	return ret;
 }
 
 static bool kvm_mmu_remote_flush_or_zap(struct kvm *kvm,
@@ -2039,6 +2036,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
 	struct hlist_head *sp_list;
 	unsigned quadrant;
 	struct kvm_mmu_page *sp;
+	int ret;
 	int collisions = 0;
 	LIST_HEAD(invalid_list);
 
@@ -2091,11 +2089,13 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
 			 * If the sync fails, the page is zapped.  If so, break
 			 * in order to rebuild it.
 			 */
-			if (!kvm_sync_page(vcpu, sp, &invalid_list))
+			ret = kvm_sync_page(vcpu, sp, &invalid_list);
+			if (ret < 0)
 				break;
 
 			WARN_ON(!list_empty(&invalid_list));
-			kvm_flush_remote_tlbs(vcpu->kvm);
+			if (ret > 0)
+				kvm_flush_remote_tlbs(vcpu->kvm);
 		}
 
 		__clear_sp_write_flooding_count(sp);
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required
  2022-03-15  9:35 [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required Hou Wenlong
@ 2022-03-16  2:19 ` Lai Jiangshan
  2022-03-24 20:17 ` Sean Christopherson
  2022-03-25 11:14 ` Paolo Bonzini
  2 siblings, 0 replies; 5+ messages in thread
From: Lai Jiangshan @ 2022-03-16  2:19 UTC (permalink / raw)
  To: Hou Wenlong
  Cc: kvm, Paolo Bonzini, Sean Christopherson, Vitaly Kuznetsov,
	Wanpeng Li, Jim Mattson, Joerg Roedel, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, X86 ML,
	H. Peter Anvin, Lai Jiangshan, LKML

On Tue, Mar 15, 2022 at 6:45 PM Hou Wenlong <houwenlong.hwl@antgroup.com> wrote:
>
> Before Commit c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page()
> to return true when remote flush is needed"), the return value
> of kvm_sync_page() indicates whether the page is synced, and
> kvm_mmu_get_page() would rebuild page when the sync fails.
> But now, kvm_sync_page() returns false when the page is
> synced and no tlb flushing is required, which leads to
> rebuild page in kvm_mmu_get_page(). So return the return
> value of mmu->sync_page() directly and check it in
> kvm_mmu_get_page(). If the sync fails, the page will be
> zapped and the invalid_list is not empty, so set flush as
> true is accepted in mmu_sync_children().
>

Good catch.

Acked-by: Lai Jiangshan <jiangshanlai@gmail.com>

> Fixes: c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page() to return true when remote flush is needed")
> Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
> ---
>  arch/x86/kvm/mmu/mmu.c | 16 ++++++++--------
>  1 file changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
> index 3b8da8b0745e..8efd165ee27c 100644
> --- a/arch/x86/kvm/mmu/mmu.c
> +++ b/arch/x86/kvm/mmu/mmu.c
> @@ -1866,17 +1866,14 @@ static void kvm_mmu_commit_zap_page(struct kvm *kvm,
>           &(_kvm)->arch.mmu_page_hash[kvm_page_table_hashfn(_gfn)])     \
>                 if ((_sp)->gfn != (_gfn) || (_sp)->role.direct) {} else
>
> -static bool kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
> +static int kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
>                          struct list_head *invalid_list)

The comments for FNAME(sync_page) can be copied here.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required
  2022-03-15  9:35 [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required Hou Wenlong
  2022-03-16  2:19 ` Lai Jiangshan
@ 2022-03-24 20:17 ` Sean Christopherson
  2022-03-25 11:26   ` Paolo Bonzini
  2022-03-25 11:14 ` Paolo Bonzini
  2 siblings, 1 reply; 5+ messages in thread
From: Sean Christopherson @ 2022-03-24 20:17 UTC (permalink / raw)
  To: Hou Wenlong
  Cc: kvm, Paolo Bonzini, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson,
	Joerg Roedel, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
	Dave Hansen, x86, H. Peter Anvin, Lai Jiangshan, linux-kernel

On Tue, Mar 15, 2022, Hou Wenlong wrote:
> Before Commit c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page()
> to return true when remote flush is needed"), the return value
> of kvm_sync_page() indicates whether the page is synced, and
> kvm_mmu_get_page() would rebuild page when the sync fails.
> But now, kvm_sync_page() returns false when the page is
> synced and no tlb flushing is required, which leads to
> rebuild page in kvm_mmu_get_page(). So return the return
> value of mmu->sync_page() directly and check it in
> kvm_mmu_get_page(). If the sync fails, the page will be
> zapped and the invalid_list is not empty, so set flush as
> true is accepted in mmu_sync_children().
> 
> Fixes: c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page() to return true when remote flush is needed")
> Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
> ---
>  arch/x86/kvm/mmu/mmu.c | 16 ++++++++--------
>  1 file changed, 8 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
> index 3b8da8b0745e..8efd165ee27c 100644
> --- a/arch/x86/kvm/mmu/mmu.c
> +++ b/arch/x86/kvm/mmu/mmu.c
> @@ -1866,17 +1866,14 @@ static void kvm_mmu_commit_zap_page(struct kvm *kvm,
>  	  &(_kvm)->arch.mmu_page_hash[kvm_page_table_hashfn(_gfn)])	\
>  		if ((_sp)->gfn != (_gfn) || (_sp)->role.direct) {} else
>  
> -static bool kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
> +static int kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
>  			 struct list_head *invalid_list)
>  {
>  	int ret = vcpu->arch.mmu->sync_page(vcpu, sp);
>  
> -	if (ret < 0) {
> +	if (ret < 0)
>  		kvm_mmu_prepare_zap_page(vcpu->kvm, sp, invalid_list);
> -		return false;
> -	}
> -
> -	return !!ret;
> +	return ret;

Hrm, this creates an oddity in mmu_sync_children(), which does a logical-OR of
the result into a boolean.  It doesn't actually change the functionality since
kvm_mmu_remote_flush_or_zap() will prioritize invalid_list, but it's weird.

What about checking invalid_list directly and keeping the boolean return?  Compile
tested only.

From: Sean Christopherson <seanjc@google.com>
Date: Thu, 24 Mar 2022 13:07:32 -0700
Subject: [PATCH] KVM: x86/mmu: Fix shadow reuse when unsync sp doesn't need
 TLB flush

Use invalid_list to detect if synchronizing an unsync shadow page failed
and resulted in the page being zapped.  Since commit c3e5e415bc1e ("KVM:
X86: Change kvm_sync_page() to return true when remote flush is needed"),
kvm_sync_page() returns whether or not a TLB flush is required, it doesn't
provide any indication as to whether or not the sync was successful.  If
the sync is successful but doesn't require a TLB flush, checking the
TLB flush result will cause KVM to unnecessarily rebuild the shadow page.

Fixes: c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page() to return true when remote flush is needed")
Cc: stable@vger.kernel.org
Reported-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/mmu/mmu.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 1361eb4599b4..b6350fec1b11 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -2086,16 +2086,19 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
 			 * This way the validity of the mapping is ensured, but the
 			 * overhead of write protection is not incurred until the
 			 * guest invalidates the TLB mapping.  This allows multiple
-			 * SPs for a single gfn to be unsync.
-			 *
+			 * SPs for a single gfn to be unsync.  kvm_sync_page()
+			 * returns true if a TLB flush is needed to ensure the
+			 * guest sees the synchronized shadow page.
+			 */
+			if (kvm_sync_page(vcpu, sp, &invalid_list))
+				kvm_flush_remote_tlbs(vcpu->kvm);
+
+			/*
 			 * If the sync fails, the page is zapped.  If so, break
 			 * in order to rebuild it.
 			 */
-			if (!kvm_sync_page(vcpu, sp, &invalid_list))
+			if (!list_empty(&invalid_list))
 				break;
-
-			WARN_ON(!list_empty(&invalid_list));
-			kvm_flush_remote_tlbs(vcpu->kvm);
 		}

 		__clear_sp_write_flooding_count(sp);

base-commit: 9b6a3be37eacee49a659232e86019e733597c045
--



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required
  2022-03-15  9:35 [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required Hou Wenlong
  2022-03-16  2:19 ` Lai Jiangshan
  2022-03-24 20:17 ` Sean Christopherson
@ 2022-03-25 11:14 ` Paolo Bonzini
  2 siblings, 0 replies; 5+ messages in thread
From: Paolo Bonzini @ 2022-03-25 11:14 UTC (permalink / raw)
  To: Hou Wenlong
  Cc: kvm, Sean Christopherson, Vitaly Kuznetsov, Wanpeng Li,
	Jim Mattson, Joerg Roedel, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H . Peter Anvin,
	Lai Jiangshan, linux-kernel

> Before Commit c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page()
> to return true when remote flush is needed"), the return value
> of kvm_sync_page() indicates whether the page is synced, and
> kvm_mmu_get_page() would rebuild page when the sync fails.
> But now, kvm_sync_page() returns false when the page is
> synced and no tlb flushing is required, which leads to
> rebuild page in kvm_mmu_get_page(). So return the return
> value of mmu->sync_page() directly and check it in
> kvm_mmu_get_page(). If the sync fails, the page will be
> zapped and the invalid_list is not empty, so set flush as
> true is accepted in mmu_sync_children().
> 
> Fixes: c3e5e415bc1e6 ("KVM: X86: Change kvm_sync_page() to return true when remote flush is needed")
> Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>

Queued and Cc'ed to stable@, thanks.

Paolo



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required
  2022-03-24 20:17 ` Sean Christopherson
@ 2022-03-25 11:26   ` Paolo Bonzini
  0 siblings, 0 replies; 5+ messages in thread
From: Paolo Bonzini @ 2022-03-25 11:26 UTC (permalink / raw)
  To: Sean Christopherson, Hou Wenlong
  Cc: kvm, Vitaly Kuznetsov, Wanpeng Li, Jim Mattson, Joerg Roedel,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Lai Jiangshan, linux-kernel

On 3/24/22 21:17, Sean Christopherson wrote:
>> +static int kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
>>   			 struct list_head *invalid_list)
>>   {
>>   	int ret = vcpu->arch.mmu->sync_page(vcpu, sp);
>>   
>> -	if (ret < 0) {
>> +	if (ret < 0)
>>   		kvm_mmu_prepare_zap_page(vcpu->kvm, sp, invalid_list);
>> -		return false;
>> -	}
>> -
>> -	return !!ret;
>> +	return ret;
> Hrm, this creates an oddity in mmu_sync_children(), which does a logical-OR of
> the result into a boolean.  It doesn't actually change the functionality since
> kvm_mmu_remote_flush_or_zap() will prioritize invalid_list, but it's weird.
> 
> What about checking invalid_list directly and keeping the boolean return?  Compile
> tested only.

It's even better to check

         flush |= kvm_sync_page(vcpu, sp, &invalid_list) > 0;

in mmu_sync_children.  If the returned value is <0, then the page is 
added to invalid_list and there is no need to set flush = true, just 
like there is no need to call kvm_flush_remote_tlbs() in kvm_mmu_get_page().

Paolo


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-03-25 11:27 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-15  9:35 [PATCH] KVM: x86/mmu: Don't rebuild page when the page is synced and no tlb flushing is required Hou Wenlong
2022-03-16  2:19 ` Lai Jiangshan
2022-03-24 20:17 ` Sean Christopherson
2022-03-25 11:26   ` Paolo Bonzini
2022-03-25 11:14 ` Paolo Bonzini

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.