KVM: MMU: don't drop spte if overwrite it from W to RO
PATCH 1/4 KVM: MMU: don't drop spte if overwrite it from W to RO
We just need flush tlb if overwrite a writable spte with a read-only one
--- arch/x86/kvm/mmu.c | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-)
arch/x86/kvm/mmu.c +9 -10
@@ -2044,6 +2044,15 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,if (pte_access &ACC_WRITE_MASK )mark_page_dirty (vcpu->kvm, gfn); +/* +* If we overwrite a writable spte with a read-only one, +* flush remote TLBs. Otherwise rmap_write_protect will +* find a read-only spte, even though the writable spte +* might be cached on a CPU's TLB. +*/ +else if (is_writable_pte (*sptep)) + ret =1 ; + set_pte:update_spte (sptep, spte); done:
@@ -2084,16 +2093,6 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte_to_pfn(*sptep), pfn);drop_spte (vcpu->kvm, sptep, shadow_trap_nonpresent_pte);kvm_flush_remote_tlbs (vcpu->kvm); -/* -* If we overwrite a writable spte with a read-only one, -* drop it and flush remote TLBs. Otherwise rmap_write_protect -* will find a read-only spte, even though the writable spte -* might be cached on a CPU's TLB. -*/ - }else if (is_writable_pte (*sptep) && - (!(pte_access &ACC_WRITE_MASK ) || !dirty)) { -drop_spte (vcpu->kvm, sptep, shadow_trap_nonpresent_pte); -kvm_flush_remote_tlbs (vcpu->kvm); }else was_rmapped =1 ; }
Signature · 1 line
1.7.0.4
PATCH 2/4 KVM: MMU: rename 'reset_host_protection' to 'host_writeable'
From: Lai Jiangshan <[email protected]>
Rename it to fix the sense better
--- arch/x86/kvm/mmu.c | 8 ++++---- arch/x86/kvm/paging_tmpl.h | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-)
arch/x86/kvm/mmu.c +4 -4
@@ -1973,7 +1973,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pte_access, int user_fault, int write_fault, int dirty, int level, gfn_t gfn, pfn_t pfn, bool speculative, - bool can_unsync, bool reset_host_protection) + bool can_unsync, bool host_writeable) { u64 spte;int ret =0 ;
@@ -2000,7 +2000,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte |= kvm_x86_ops->get_mt_mask (vcpu, gfn,kvm_is_mmio_pfn (pfn)); -if (reset_host_protection) +if (host_writeable) spte |=SPTE_HOST_WRITEABLE ; spte |= (u64 )pfn <<PAGE_SHIFT ;
@@ -2064,7 +2064,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, int user_fault, int write_fault, int dirty, int *ptwrite, int level, gfn_t gfn, pfn_t pfn, bool speculative, - bool reset_host_protection) + bool host_writeable) { int was_rmapped = 0;int rmap_count;
@@ -2099,7 +2099,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,if (set_spte (vcpu, sptep, pte_access, user_fault, write_fault, dirty, level, gfn, pfn, speculative, true, - reset_host_protection)) { + host_writeable)) {if (write_fault) *ptwrite =1 ;kvm_mmu_flush_tlb (vcpu);
arch/x86/kvm/paging_tmpl.h +5 -5
@@ -329,7 +329,7 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,return ;kvm_get_pfn (pfn);/* -* we call mmu_set_spte() with reset_host_protection = true beacuse that +* we call mmu_set_spte() with host_writeable = true beacuse that * vcpu->arch.update_pte.pfn was fetched from get_user_pages(write = 1). */ mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0,
@@ -742,7 +742,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, bool clear_unsync) {int i, offset, nr_present; -bool reset_host_protection; +bool host_writeable;gpa_t first_pte_gpa; offset = nr_present =0 ;
@@ -788,14 +788,14 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, pte_access = sp->role.access &FNAME (gpte_access)(vcpu, gpte);if (!(sp->spt[i] &SPTE_HOST_WRITEABLE )) { pte_access &= ~ACC_WRITE_MASK ; - reset_host_protection =0 ; + host_writeable =0 ; }else { - reset_host_protection =1 ; + host_writeable =1 ; }set_spte (vcpu, &sp->spt[i], pte_access,0 ,0 ,is_dirty_gpte (gpte),PT_PAGE_TABLE_LEVEL , gfn,spte_to_pfn (sp->spt[i]), true, false, - reset_host_protection); + host_writeable); }return !nr_present;
Signature · 1 line
1.7.0.4
Re: [PATCH 2/4] KVM: MMU: rename 'reset_host_protection' to 'host_writeable'
Quoted 4 lines · Xiao Guangrong
From: Lai Jiangshan <[email protected]>
Rename it to fix the sense better
CCed to Lai Jiangshan
PATCH 3/4 KVM: MMU: notrap it if gpte's reserved is set
In reply to Xiao GuangrongWe can past the page fault to guest directly if gpte's reserved is set
--- arch/x86/kvm/paging_tmpl.h | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-)
arch/x86/kvm/paging_tmpl.h +6 -4
@@ -760,6 +760,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,pt_element_t gpte;gpa_t pte_gpa;gfn_t gfn; +bool gpte_invalid;if (!is_shadow_present_pte (sp->spt[i]))continue ;
@@ -771,12 +772,13 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,return -EINVAL ; gfn =gpte_to_gfn (gpte); -if (is_rsvd_bits_set (&vcpu->arch.mmu, gpte,PT_PAGE_TABLE_LEVEL ) - || gfn != sp->gfns[i] || !is_present_gpte (gpte) - || !(gpte &PT_ACCESSED_MASK )) { + gpte_invalid =is_present_gpte (gpte) || +is_rsvd_bits_set (&vcpu->arch.mmu, gpte,PT_PAGE_TABLE_LEVEL ); +if (gpte_invalid || gfn != sp->gfns[i] || + !(gpte &PT_ACCESSED_MASK )) {u64 nonpresent; -if (is_present_gpte (gpte) || !clear_unsync) +if (gpte_invalid || !clear_unsync) nonpresent = shadow_trap_nonpresent_pte;else nonpresent = shadow_notrap_nonpresent_pte;
Signature · 1 line
1.7.0.4
Re: [PATCH 3/4] KVM: MMU: notrap it if gpte's reserved is set
Quoted 3 lines · Xiao Guangrong
We can past the page fault to guest directly if gpte's reserved is set
How can that work? shadow_notrap_nonpresent_pte causes a fault with PFEC.P=PFEC.RSVD=0, while we need PFEC.P=PFEC.RSVD=1.
Quoted 18 lines
Signed-off-by Xiao Guangrong<[email protected]>--- arch/x86/kvm/paging_tmpl.h | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-)arch/x86/kvm/paging_tmpl.h +1
-0@@ -760,6 +760,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,pt_element_t gpte;gpa_t pte_gpa;gfn_t gfn; +bool gpte_invalid;if (!is_shadow_present_pte (sp->spt[i]))continue ;
arch/x86/kvm/paging_tmpl.h static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, · line 776
Quoted 8 lines
arch/x86/kvm/paging_tmpl.h +2
-3@@ -771,12 +772,13 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,return -EINVAL ; gfn =gpte_to_gfn (gpte); -if (is_rsvd_bits_set (&vcpu->arch.mmu, gpte,PT_PAGE_TABLE_LEVEL ) - || gfn != sp->gfns[i] || !is_present_gpte (gpte) - || !(gpte&PT_ACCESSED_MASK )) { + gpte_invalid =is_present_gpte (gpte) || +is_rsvd_bits_set (&vcpu->arch.mmu, gpte,PT_PAGE_TABLE_LEVEL );
Shouldn't that be &&?
Quoted 9 lines
arch/x86/kvm/paging_tmpl.h +3
-1+if (gpte_invalid || gfn != sp->gfns[i] || + !(gpte&PT_ACCESSED_MASK )) {u64 nonpresent; -if (is_present_gpte (gpte) || !clear_unsync) +if (gpte_invalid || !clear_unsync) nonpresent = shadow_trap_nonpresent_pte;else nonpresent = shadow_notrap_nonpresent_pte;
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 3/4] KVM: MMU: notrap it if gpte's reserved is set
Quoted 8 lines · Avi Kivity
Quoted 3 lines · Xiao Guangrong
We can past the page fault to guest directly if gpte's reserved is set
How can that work?
shadow_notrap_nonpresent_ptecauses a fault with PFEC.P=PFEC.RSVD=0, while we need PFEC.P=PFEC.RSVD=1.
Ah, i missed it for a long time, thanks for you point it out.
The same mistake is in 'prefetch' path, i'll fix it in the v2 version.
Re: [PATCH 3/4] KVM: MMU: notrap it if gpte's reserved is set
Quoted 6 lines · Xiao Guangrong
Quoted 5 lines · Avi Kivity
Quoted 3 lines · Xiao Guangrong
We can past the page fault to guest directly if gpte's reserved is set
Quoted 7 lines
Quoted 3 lines
How can that work?
shadow_notrap_nonpresent_ptecauses a fault with PFEC.P=PFEC.RSVD=0, while we need PFEC.P=PFEC.RSVD=1.Ah, i missed it for a long time, thanks for you point it out.
The same mistake is in 'prefetch' path, i'll fix it in the v2 version.
Doesn't access.flat catch this?
Ideally we'd have a test case to catch this, but it may be hard to write.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 3/4] KVM: MMU: notrap it if gpte's reserved is set
Quoted 11 lines · Avi Kivity
Quoted 10 lines · Xiao Guangrong
Quoted 8 lines · Avi Kivity
Quoted 3 lines · Xiao Guangrong
We can past the page fault to guest directly if gpte's reserved is set
How can that work?
shadow_notrap_nonpresent_ptecauses a fault with PFEC.P=PFEC.RSVD=0, while we need PFEC.P=PFEC.RSVD=1.
Quoted 5 lines
Quoted 3 lines
Ah, i missed it for a long time, thanks for you point it out.
The same mistake is in 'prefetch' path, i'll fix it in the v2 version.
Doesn't access.flat catch this?
Unfortunately, it can't catch this.
Quoted 3 lines
Ideally we'd have a test case to catch this, but it may be hard to write.
Um. i'll do it later.
PATCH 4/4 KVM: MMU: cleanup update_pte, pte_prefetch and sync_page functions
In reply to Xiao GuangrongSome operation of these functions is very similar, so introduce a common function to cleanup them
--- arch/x86/kvm/mmu.c | 3 - arch/x86/kvm/paging_tmpl.h | 191 ++++++++++++++++++++++++------------------- 2 files changed, 107 insertions(+), 87 deletions(-)
arch/x86/kvm/mmu.c +0 -3
@@ -3108,9 +3108,6 @@ static void mmu_pte_write_new_pte(struct kvm_vcpu *vcpu,return ; } -if (is_rsvd_bits_set (&vcpu->arch.mmu, *(u64 *)new,PT_PAGE_TABLE_LEVEL )) -return ; - ++vcpu->kvm->stat.mmu_pte_updated;if (!sp->role.cr4_pae)paging32_update_pte (vcpu, sp, spte, new);
arch/x86/kvm/paging_tmpl.h +107 -84
257 lines not on this page · raw message
Signature · 1 line
1.7.0.4
Re: [PATCH 4/4] KVM: MMU: cleanup update_pte, pte_prefetch and sync_page functions
Quoted 8 lines · Xiao Guangrong
Some operation of these functions is very similar, so introduce a common function to cleanup them
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 3 - arch/x86/kvm/paging_tmpl.h | 191 ++++++++++++++++++++++++------------------- 2 files changed, 107 insertions(+), 87 deletions(-)
This makes the code more complicated and error prone IMO, because there are specialities of
Quoted 68 lines
arch/x86/kvm/mmu.c +0
-3@@ -3108,9 +3108,6 @@ static void mmu_pte_write_new_pte(struct kvm_vcpu *vcpu,return ; } -if (is_rsvd_bits_set (&vcpu->arch.mmu, *(u64 *)new,PT_PAGE_TABLE_LEVEL )) -return ; - ++vcpu->kvm->stat.mmu_pte_updated;if (!sp->role.cr4_pae)paging32_update_pte (vcpu, sp, spte, new);arch/x86/kvm/paging_tmpl.h +25
-15@@ -299,42 +299,90 @@ static int FNAME(walk_addr_nested)(struct guest_walker *walker, addr, access); } -static void FNAME (update_pte )(struct kvm_vcpu *vcpu,struct kvm_mmu_page *sp, -u64 *spte,const void *pte) +static bool FNAME (fetch_guest_pte )(struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp,u64 *spte, +bool clear_unsync,pt_element_t gpte, +pfn_t (get_pfn )(struct kvm_vcpu *,u64 *, +pt_element_t ,unsigned ,bool *)) { -pt_element_t gpte;unsigned pte_access; +u64 nonpresent = shadow_trap_nonpresent_pte; +gfn_t gfn;pfn_t pfn; -u64 new_spte; +bool dirty, host_writeable; - gpte = *(const pt_element_t *)pte; -if (~gpte & (PT_PRESENT_MASK |PT_ACCESSED_MASK )) { -if (!is_present_gpte (gpte)) { -if (sp->unsync) - new_spte = shadow_trap_nonpresent_pte; -else - new_spte = shadow_notrap_nonpresent_pte; -__set_spte (spte, new_spte); - } -return ; +if (!is_present_gpte (gpte) || +is_rsvd_bits_set (&vcpu->arch.mmu, gpte,PT_PAGE_TABLE_LEVEL )) { +if (!sp->unsync && !clear_unsync) + nonpresent = shadow_notrap_nonpresent_pte; + goto no_present; } -pgprintk ("%s: gpte %llx spte %p\n" , __func__, (u64 )gpte, spte); + +if (!(gpte &PT_ACCESSED_MASK )) + goto no_present; + pte_access = sp->role.access &FNAME (gpte_access)(vcpu, gpte); + gfn =gpte_to_gfn (gpte); + dirty =is_dirty_gpte (gpte); + pfn =get_pfn (vcpu, spte, gpte, pte_access, &host_writeable); + +if (is_error_pfn (pfn)) + goto no_present; + +if (!host_writeable)
arch/x86/kvm/paging_tmpl.h static int FNAME(walk_addr_nested)(struct guest_walker *walker, · line 342
Quoted 10 lines
arch/x86/kvm/paging_tmpl.h +10
-0+ pte_access &= ~ACC_WRITE_MASK ; + +if (spte_to_pfn (*spte) == pfn) +set_spte (vcpu, spte, pte_access,0 ,0 , + dirty,PT_PAGE_TABLE_LEVEL , gfn, + pfn, true, false, host_writeable); +else +mmu_set_spte (vcpu, spte, sp->role.access, pte_access,0 ,0 , + dirty,NULL ,PT_PAGE_TABLE_LEVEL , gfn, + pfn, true, host_writeable);
For example, the update path should always go through mmu_set_spte to update last_pte_updated, last_pte_gfn.
Also the callbacks make it harder to read the code. Maybe the unification works if you use common functions for common parts.
Re: [PATCH 4/4] KVM: MMU: cleanup update_pte, pte_prefetch and sync_page functions
Quoted 8 lines · Marcelo Tosatti
arch/x86/kvm/paging_tmpl.h static int FNAME(walk_addr_nested)(struct guest_walker *walker, · line 342
Quoted 4 lines
arch/x86/kvm/paging_tmpl.h +4
-0+else +mmu_set_spte (vcpu, spte, sp->role.access, pte_access,0 ,0 , + dirty,NULL ,PT_PAGE_TABLE_LEVEL , gfn, + pfn, true, host_writeable);For example, the update path should always go through
mmu_set_spteto updatelast_pte_updated,last_pte_gfn.
Actually, the set_spte() just works for sync path ;-)
Quoted 3 lines
Also the callbacks make it harder to read the code. Maybe the unification works if you use common functions for common parts.
Um. your advice is reasonable, i'll improve it. Thanks.
Quoted 2 lines · Xiao Guangrong
We just need flush tlb if overwrite a writable spte with a read-only one
What are the advantages? Avoid playing with rmap, and avoid a window where the spte is missing?
(they are worth the patch, just seeing if I'm not missing something)
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 7 lines · Avi Kivity
Quoted 2 lines · Xiao Guangrong
We just need flush tlb if overwrite a writable spte with a read-only one
What are the advantages? Avoid playing with rmap, and avoid a window where the spte is missing?
Both, but only the first was in my mind when i'm making the patch :-)
Quoted 14 lines · Xiao Guangrong
We just need flush tlb if overwrite a writable spte with a read-only one
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-)arch/x86/kvm/mmu.c +0
-0@@ -2044,6 +2044,15 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,if (pte_access &ACC_WRITE_MASK )mark_page_dirty (vcpu->kvm, gfn);
arch/x86/kvm/mmu.c static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, · line 2055
Quoted 10 lines
arch/x86/kvm/mmu.c +9
-0+/* +* If we overwrite a writable spte with a read-only one, +* flush remote TLBs. Otherwise rmap_write_protect will +* find a read-only spte, even though the writable spte +* might be cached on a CPU's TLB. +*/ + else if (is_writable_pte(*sptep)) + ret =1 ; +
The return value of set_spte indicates whether the gfn being mapped to was write protected, not if a TLB flush is necessary.
Quoted 9 lines
arch/x86/kvm/mmu.c +0
-2set_pte:update_spte (sptep, spte); done:@@ -2084,16 +2093,6 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte_to_pfn(*sptep), pfn);drop_spte (vcpu->kvm, sptep, shadow_trap_nonpresent_pte);kvm_flush_remote_tlbs (vcpu->kvm); -/* -* If we overwrite a writable spte with a read-only one,
arch/x86/kvm/mmu.c static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, · line 2097
Quoted 10 lines
arch/x86/kvm/mmu.c +0
-8-* drop it and flush remote TLBs. Otherwise rmap_write_protect -* will find a read-only spte, even though the writable spte -* might be cached on a CPU's TLB. -*/ - }else if (is_writable_pte (*sptep) && - (!(pte_access &ACC_WRITE_MASK ) || !dirty)) { -drop_spte (vcpu->kvm, sptep, shadow_trap_nonpresent_pte); -kvm_flush_remote_tlbs (vcpu->kvm); }else was_rmapped =1 ;
And here, flush will only happen if overwrite is RW->RO.
Quoted 16 lines · Marcelo Tosatti
Quoted 15 lines · Xiao Guangrong
We just need flush tlb if overwrite a writable spte with a read-only one
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-)arch/x86/kvm/mmu.c +0
-0@@ -2044,6 +2044,15 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,if (pte_access &ACC_WRITE_MASK )mark_page_dirty (vcpu->kvm, gfn);
Quoted 13 lines
arch/x86/kvm/mmu.c static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, · line 2055
Quoted 9 lines
arch/x86/kvm/mmu.c +9
-0+/* +* If we overwrite a writable spte with a read-only one, +* flush remote TLBs. Otherwise rmap_write_protect will +* find a read-only spte, even though the writable spte +* might be cached on a CPU's TLB. +*/ + else if (is_writable_pte(*sptep)) + ret =1 ; +The return value of
set_spteindicates whether the gfn being mapped to was write protected, not if a TLB flush is necessary.
Yes, i also noticed this and have fixed in the v2 queue, thanks Marcelo!