KVM: MMU: reduce the size of shadow page structure and some cleanup
Subsystems (4)
cover KVM: MMU: reduce the size of shadow page structure and some cleanup
In this patchset, we observably reduce the size of struct kvm_mmu_page and let the code more simple.
And this patchset have already tested by unittest and RHEL.6.1 setup/boot/reboot/shutdown.
PATCH 1/8 KVM: MMU: combine unsync and unsync_children
unsync is only used for the page of level == 1 and unsync_children is only used in the upper page, so combine them to reduce the size of shadow page structure
--- Documentation/virtual/kvm/mmu.txt | 5 ++- arch/x86/include/asm/kvm_host.h | 14 +++++++++++- arch/x86/kvm/mmu.c | 39 +++++++++++++++++++++++++----------- arch/x86/kvm/mmu_audit.c | 6 ++-- arch/x86/kvm/mmutrace.h | 3 +- arch/x86/kvm/paging_tmpl.h | 2 +- 6 files changed, 48 insertions(+), 21 deletions(-)
Documentation/virtual/kvm/mmu.txt +3 -2
@@ -205,14 +205,15 @@ Shadow pages contain the following information: this page's spt. Otherwise, parent_ptes points at a data structure with a list of parent_ptes. unsync: + It is used when role.level == 1, only level 1 shadow pages can be unsync. If true, then the translations in this page may not match the guest's translation. This is equivalent to the state of the tlb when a pte is changed but before the tlb entry is flushed. Accordingly, unsync ptes are synchronized when the guest executes invlpg or flushes its tlb by other means. Valid for leaf pages. unsync_children: - How many sptes in the page point at pages that are unsync (or have - unsynchronized children). + It is used when role.level > 1 and indicates how many sptes in the page + point at unsync pages or unsynchronized children. unsync_child_bitmap: A bitmap indicating which sptes in spt point (directly or indirectly) at pages that may be unsynchronized. Used to quickly locate all unsychronized
arch/x86/include/asm/kvm_host.h +12 -2
@@ -233,9 +233,19 @@ struct kvm_mmu_page {* in this shadow page. */ DECLARE_BITMAP (slot_bitmap,KVM_MEM_SLOTS_NUM ); -bool unsync;int root_count;/* Currently serving as active root */ -unsigned int unsync_children; + +/* +* If role.level == 1, unsync indicates whether the sp is +* unsync, otherwise unsync_children indicates the number +* of sptes which point at unsync sp or unsychronized children. +* See sp_is_unsync() / sp_unsync_children_num. +*/ +union { +bool unsync; +unsigned int unsync_children; + }; +unsigned long parent_ptes;/* Reverse mapping for parent_pte */ DECLARE_BITMAP (unsync_child_bitmap,512 );
arch/x86/kvm/mmu.c +27 -12
@@ -151,6 +151,21 @@ module_param(dbg, bool, 0644);#define SHADOW_PT_INDEX (addr, level) PT64_INDEX(addr, level) +static bool sp_is_unsync (struct kvm_mmu_page *sp) +{ +return sp->role.level ==PT_PAGE_TABLE_LEVEL && sp->unsync; +} + +static unsigned int sp_unsync_children_num (struct kvm_mmu_page *sp) +{ +unsigned int num =0 ; + +if (sp->role.level !=PT_PAGE_TABLE_LEVEL ) + num = sp->unsync_children; + +return num; +} +struct pte_list_desc {u64 *sptes[PTE_LIST_EXT ];struct pte_list_desc *more;
@@ -1401,7 +1416,7 @@ static int mmu_pages_add(struct kvm_mmu_pages *pvec, struct kvm_mmu_page *sp, {int i; -if (sp->unsync) +if (sp_is_unsync (sp))for (i=0 ; i < pvec->nr; i++)if (pvec->page[i].sp == sp)return 0 ;
@@ -1426,7 +1441,7 @@ static int __mmu_unsync_walk(struct kvm_mmu_page *sp, child =page_header (ent &PT64_BASE_ADDR_MASK ); -if (child->unsync_children) { +if (sp_unsync_children_num (child)) {if (mmu_pages_add (pvec, child, i))return -ENOSPC ;
@@ -1437,7 +1452,7 @@ static int __mmu_unsync_walk(struct kvm_mmu_page *sp, nr_unsync_leaf += ret; else return ret; - }else if (child->unsync) { + }else if (sp_is_unsync (child)) { nr_unsync_leaf++;if (mmu_pages_add (pvec, child, i))return -ENOSPC ;
@@ -1468,7 +1483,7 @@ static int mmu_unsync_walk(struct kvm_mmu_page *sp,static void kvm_unlink_unsync_page (struct kvm *kvm,struct kvm_mmu_page *sp) { -WARN_ON (!sp->unsync); +WARN_ON (!sp_is_unsync (sp));trace_kvm_mmu_sync_page (sp); sp->unsync =0 ; --kvm->stat.mmu_unsync;
@@ -1546,7 +1561,7 @@ static void kvm_sync_pages(struct kvm_vcpu *vcpu, gfn_t gfn)bool flush = false;for_each_gfn_indirect_valid_sp (vcpu->kvm, s, gfn, node) { -if (!s->unsync) +if (!sp_is_unsync (s))continue ;WARN_ON (s->role.level !=PT_PAGE_TABLE_LEVEL );
@@ -1699,20 +1714,20 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, role.quadrant = quadrant; }for_each_gfn_sp (vcpu->kvm, sp, gfn, node) { -if (!need_sync && sp->unsync) +if (!need_sync &&sp_is_unsync (sp)) need_sync = true;if (sp->role.word != role.word)continue ; -if (sp->unsync &&kvm_sync_page_transient (vcpu, sp)) +if (sp_is_unsync (sp) &&kvm_sync_page_transient (vcpu, sp))break ;mmu_page_add_parent_pte (vcpu, sp, parent_pte); -if (sp->unsync_children) { +if (sp_unsync_children_num (sp)) {kvm_make_request (KVM_REQ_MMU_SYNC , vcpu);kvm_mmu_mark_parents_unsync (sp); - }else if (sp->unsync) + }else if (sp_is_unsync (sp))kvm_mmu_mark_parents_unsync (sp);__clear_sp_write_flooding_count (sp);
@@ -1914,7 +1929,7 @@ static int kvm_mmu_prepare_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp,kvm_mmu_unlink_parents (kvm, sp);if (!sp->role.invalid && !sp->role.direct)unaccount_shadowed (kvm, sp->gfn); -if (sp->unsync) +if (sp_is_unsync (sp))kvm_unlink_unsync_page (kvm, sp);if (!sp->root_count) {/* Count self */
@@ -2163,7 +2178,7 @@ static void kvm_unsync_pages(struct kvm_vcpu *vcpu, gfn_t gfn)struct hlist_node *node;for_each_gfn_indirect_valid_sp (vcpu->kvm, s, gfn, node) { -if (s->unsync) +if (sp_is_unsync (s))continue ;WARN_ON (s->role.level !=PT_PAGE_TABLE_LEVEL );__kvm_unsync_page (vcpu, s);
@@ -2184,7 +2199,7 @@ static int mmu_need_write_protect(struct kvm_vcpu *vcpu, gfn_t gfn,if (s->role.level !=PT_PAGE_TABLE_LEVEL )return 1 ; -if (!need_unsync && !s->unsync) { +if (!need_unsync && !sp_is_unsync (s)) { need_unsync = true; } }
arch/x86/kvm/mmu_audit.c +3 -3
@@ -102,7 +102,7 @@ static void audit_mappings(struct kvm_vcpu *vcpu, u64 *sptep, int level) sp =page_header (__pa (sptep)); -if (sp->unsync) { +if (sp_is_unsync (sp)) {if (level !=PT_PAGE_TABLE_LEVEL ) {audit_printk (vcpu->kvm,"unsync sp: %p " "level = %d\n" , sp, level);
@@ -168,7 +168,7 @@ static void audit_spte_after_sync(struct kvm_vcpu *vcpu, u64 *sptep, int level) {struct kvm_mmu_page *sp =page_header (__pa (sptep)); -if (vcpu->kvm->arch.audit_point ==AUDIT_POST_SYNC && sp->unsync) +if (vcpu->kvm->arch.audit_point ==AUDIT_POST_SYNC &&sp_is_unsync (sp))audit_printk (vcpu->kvm,"meet unsync sp(%p) after sync " "root.\n" , sp); }
@@ -194,7 +194,7 @@ static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)unsigned long *rmapp;u64 *spte; -if (sp->role.direct || sp->unsync || sp->role.invalid) +if (sp->role.direct ||sp_is_unsync (sp) || sp->role.invalid)return ; slot =gfn_to_memslot (kvm, sp->gfn);
arch/x86/kvm/mmutrace.h +2 -1
@@ -38,7 +38,8 @@ role.invalid ?" invalid" :"" , \ role.nxe ?"" :"!" , \ __entry->root_count, \ - __entry->unsync ? "unsync" : "sync", 0); \ + role.level ==1 && __entry->unsync ? \ + "unsync" : "sync", 0); \ ret; \ })
arch/x86/kvm/paging_tmpl.h +1 -1
@@ -691,7 +691,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)pt_element_t gpte;gpa_t pte_gpa; -if (!sp->unsync) +if (!sp_is_unsync (sp))break ; pte_gpa =FNAME (get_level1_sp_gpa)(sp);
Signature · 1 line
1.7.7.4
PATCH 2/8 KVM: MMU: set the dirty bit for the upper shadow page
In reply to Xiao GuangrongUpper page do not need to be tracked the status bit, it is safe to set the dirty and let cpu to happily prefetch it
--- arch/x86/kvm/mmu.c | 13 +++++-------- 1 files changed, 5 insertions(+), 8 deletions(-)
arch/x86/kvm/mmu.c +5 -8
@@ -150,6 +150,9 @@ module_param(dbg, bool, 0644);#define SPTE_HOST_WRITEABLE (1ULL << PT_FIRST_AVAIL_BITS_SHIFT)#define SHADOW_PT_INDEX (addr, level) PT64_INDEX(addr, level) +#define SHADOW_PAGE_TABLE \ + (PT_PRESENT_MASK | PT_WRITABLE_MASK | shadow_user_mask | \ + shadow_x_mask | shadow_accessed_mask | shadow_dirty_mask)static bool sp_is_unsync (struct kvm_mmu_page *sp) {
@@ -1808,9 +1811,7 @@ static void link_shadow_page(u64 *sptep, struct kvm_mmu_page *sp) {u64 spte; - spte =__pa (sp->spt) - |PT_PRESENT_MASK |PT_ACCESSED_MASK - |PT_WRITABLE_MASK |PT_USER_MASK ; + spte =__pa (sp->spt) |SHADOW_PAGE_TABLE ;mmu_spte_set (sptep, spte); }
@@ -2497,11 +2498,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,return -ENOMEM ; } -mmu_spte_set (iterator.sptep, -__pa (sp->spt) - |PT_PRESENT_MASK |PT_WRITABLE_MASK - | shadow_user_mask | shadow_x_mask - | shadow_accessed_mask); +link_shadow_page (iterator.sptep, sp); } }return emulate;
Signature · 1 line
1.7.7.4
PATCH 3/8 KVM: MMU: do not add a nonpresent spte to rmaps of its child
In reply to Xiao GuangrongSet the spte before adding it to the rmap of its child so that all parent spte are valid when propagate unsync bit from a usnync page / children page
And this feature is needed by the later patch
--- arch/x86/kvm/mmu.c | 74 +++++++++++++++---------------------------- arch/x86/kvm/mmutrace.h | 2 +- arch/x86/kvm/paging_tmpl.h | 14 +++----- 3 files changed, 32 insertions(+), 58 deletions(-)
arch/x86/kvm/mmu.c +26 -48
@@ -1321,12 +1321,14 @@ static unsigned kvm_page_table_hashfn(gfn_t gfn)return gfn & ((1 <<KVM_MMU_HASH_SHIFT ) -1 ); } -static void mmu_page_add_parent_pte (struct kvm_vcpu *vcpu, -struct kvm_mmu_page *sp,u64 *parent_pte) +static void mmu_page_add_set_parent_pte (struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp, +u64 *parent_pte) {if (!parent_pte)return ; +mmu_spte_set (parent_pte,__pa (sp->spt) |SHADOW_PAGE_TABLE );pte_list_add (vcpu, parent_pte, &sp->parent_ptes); }
@@ -1357,7 +1359,7 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu,list_add (&sp->link, &vcpu->kvm->arch.active_mmu_pages);bitmap_zero (sp->slot_bitmap,KVM_MEM_SLOTS_NUM ); sp->parent_ptes =0 ; -mmu_page_add_parent_pte (vcpu, sp, parent_pte); +mmu_page_add_set_parent_pte (vcpu, sp, parent_pte);kvm_mod_used_mmu_pages (vcpu->kvm,+1 );return sp; }
@@ -1690,13 +1692,10 @@ static void clear_sp_write_flooding_count(u64 *spte)__clear_sp_write_flooding_count (sp); } -static struct kvm_mmu_page *kvm_mmu_get_page (struct kvm_vcpu *vcpu, -gfn_t gfn, -gva_t gaddr, -unsigned level, -int direct, -unsigned access, -u64 *parent_pte) +static struct kvm_mmu_page * +kvm_mmu_get_set_page (struct kvm_vcpu *vcpu,gfn_t gfn,gva_t gaddr, +unsigned level,int direct,unsigned access, +u64 *parent_pte) {union kvm_mmu_page_role role;unsigned quadrant;
@@ -1726,7 +1725,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,if (sp_is_unsync (sp) &&kvm_sync_page_transient (vcpu, sp))break ; -mmu_page_add_parent_pte (vcpu, sp, parent_pte); +mmu_page_add_set_parent_pte (vcpu, sp, parent_pte);if (sp_unsync_children_num (sp)) {kvm_make_request (KVM_REQ_MMU_SYNC , vcpu);kvm_mmu_mark_parents_unsync (sp);
@@ -1734,7 +1733,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,kvm_mmu_mark_parents_unsync (sp);__clear_sp_write_flooding_count (sp); -trace_kvm_mmu_get_page (sp, false); +trace_kvm_mmu_get_set_page (sp, false);return sp; } ++vcpu->kvm->stat.mmu_cache_miss;
@@ -1754,7 +1753,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,account_shadowed (vcpu->kvm, gfn); }init_shadow_page_table (sp); -trace_kvm_mmu_get_page (sp, true); +trace_kvm_mmu_get_set_page (sp, true);return sp; }
@@ -1807,14 +1806,6 @@ static void shadow_walk_next(struct kvm_shadow_walk_iterator *iterator)return __shadow_walk_next (iterator, *iterator->sptep); } -static void link_shadow_page (u64 *sptep,struct kvm_mmu_page *sp) -{ -u64 spte; - - spte =__pa (sp->spt) |SHADOW_PAGE_TABLE ; -mmu_spte_set (sptep, spte); -} -static void drop_large_spte (struct kvm_vcpu *vcpu,u64 *sptep) {if (is_large_pte (*sptep)) {
@@ -1879,11 +1870,6 @@ static void kvm_mmu_page_unlink_children(struct kvm *kvm,mmu_page_zap_pte (kvm, sp, sp->spt + i); } -static void kvm_mmu_put_page (struct kvm_mmu_page *sp,u64 *parent_pte) -{ -mmu_page_remove_parent_pte (sp, parent_pte); -} -static void kvm_mmu_unlink_parents (struct kvm *kvm,struct kvm_mmu_page *sp) {u64 *parent_pte;
@@ -2468,7 +2454,6 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, bool prefault) {struct kvm_shadow_walk_iterator iterator; -struct kvm_mmu_page *sp;int emulate =0 ;gfn_t pseudo_gfn;
@@ -2489,16 +2474,9 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, base_addr &=PT64_LVL_ADDR_MASK (iterator.level); pseudo_gfn = base_addr >>PAGE_SHIFT ; - sp =kvm_mmu_get_page (vcpu, pseudo_gfn, iterator.addr, - iterator.level -1 , -1 ,ACC_ALL , iterator.sptep); -if (!sp) { -pgprintk ("nonpaging_map: ENOMEM\n" ); -kvm_release_pfn_clean (pfn); -return -ENOMEM ; - } - -link_shadow_page (iterator.sptep, sp); +kvm_mmu_get_set_page (vcpu, pseudo_gfn, iterator.addr, + iterator.level -1 , +1 ,ACC_ALL , iterator.sptep); } }return emulate;
@@ -2713,8 +2691,8 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)if (vcpu->arch.mmu.shadow_root_level ==PT64_ROOT_LEVEL ) {spin_lock (&vcpu->kvm->mmu_lock);kvm_mmu_free_some_pages (vcpu); - sp =kvm_mmu_get_page (vcpu,0 ,0 ,PT64_ROOT_LEVEL , -1 ,ACC_ALL ,NULL ); + sp =kvm_mmu_get_set_page (vcpu,0 ,0 ,PT64_ROOT_LEVEL , +1 ,ACC_ALL ,NULL ); ++sp->root_count;spin_unlock (&vcpu->kvm->mmu_lock); vcpu->arch.mmu.root_hpa =__pa (sp->spt);
@@ -2725,10 +2703,10 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu)ASSERT (!VALID_PAGE (root));spin_lock (&vcpu->kvm->mmu_lock);kvm_mmu_free_some_pages (vcpu); - sp =kvm_mmu_get_page (vcpu, i << (30 -PAGE_SHIFT ), - i <<30 , -PT32_ROOT_LEVEL ,1 ,ACC_ALL , -NULL ); + sp =kvm_mmu_get_set_page (vcpu, i << (30 -PAGE_SHIFT ), + i <<30 , +PT32_ROOT_LEVEL ,1 ,ACC_ALL , +NULL ); root =__pa (sp->spt); ++sp->root_count;spin_unlock (&vcpu->kvm->mmu_lock);
@@ -2764,8 +2742,8 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu)spin_lock (&vcpu->kvm->mmu_lock);kvm_mmu_free_some_pages (vcpu); - sp =kvm_mmu_get_page (vcpu, root_gfn,0 ,PT64_ROOT_LEVEL , -0 ,ACC_ALL ,NULL ); + sp =kvm_mmu_get_set_page (vcpu, root_gfn,0 ,PT64_ROOT_LEVEL , +0 ,ACC_ALL ,NULL ); root =__pa (sp->spt); ++sp->root_count;spin_unlock (&vcpu->kvm->mmu_lock);
@@ -2798,9 +2776,9 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) }spin_lock (&vcpu->kvm->mmu_lock);kvm_mmu_free_some_pages (vcpu); - sp =kvm_mmu_get_page (vcpu, root_gfn, i <<30 , -PT32_ROOT_LEVEL ,0 , -ACC_ALL ,NULL ); + sp =kvm_mmu_get_set_page (vcpu, root_gfn, i <<30 , +PT32_ROOT_LEVEL ,0 , +ACC_ALL ,NULL ); root =__pa (sp->spt); ++sp->root_count;spin_unlock (&vcpu->kvm->mmu_lock);
arch/x86/kvm/mmutrace.h +1 -1
@@ -145,7 +145,7 @@ TRACE_EVENT( ); TRACE_EVENT( - kvm_mmu_get_page, + kvm_mmu_get_set_page, TP_PROTO(struct kvm_mmu_page *sp, bool created), TP_ARGS(sp, created),
arch/x86/kvm/paging_tmpl.h +5 -9
@@ -503,8 +503,8 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, sp =NULL ;if (!is_shadow_present_pte (*it.sptep)) { table_gfn = gw->table_gfn[it.level -2 ]; - sp =kvm_mmu_get_page (vcpu, table_gfn, addr, it.level-1 , - false, access, it.sptep); + sp =kvm_mmu_get_set_page (vcpu, table_gfn, addr, + it.level -1 , false, access, it.sptep); }/*
@@ -513,9 +513,6 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,*/ if (FNAME (gpte_changed)(vcpu, gw, it.level -1 )) goto out_gpte_changed; - -if (sp) -link_shadow_page (it.sptep, sp); } for (;
@@ -533,9 +530,8 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, direct_gfn = gw->gfn & ~(KVM_PAGES_PER_HPAGE (it.level) -1 ); - sp =kvm_mmu_get_page (vcpu, direct_gfn, addr, it.level-1 , - true, direct_access, it.sptep); -link_shadow_page (it.sptep, sp); +kvm_mmu_get_set_page (vcpu, direct_gfn, addr, it.level -1 , + true, direct_access, it.sptep); }clear_sp_write_flooding_count (it.sptep);
@@ -548,7 +544,7 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, out_gpte_changed:if (sp) -kvm_mmu_put_page (sp, it.sptep); +drop_parent_pte (sp, it.sptep);kvm_release_pfn_clean (pfn);return NULL ; }
Signature · 1 line
1.7.7.4
PATCH 4/8 KVM: MMU: drop unsync_child_bitmap
In reply to Xiao Guangrongunsync_child_bitmap is used to record which spte has unsync page or unsync children, we can set a free bit in the spte instead of it
--- Documentation/virtual/kvm/mmu.txt | 4 -- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/mmu.c | 77 +++++++++++++++++++++++++------------ 3 files changed, 52 insertions(+), 30 deletions(-)
Documentation/virtual/kvm/mmu.txt +0 -4
@@ -214,10 +214,6 @@ Shadow pages contain the following information: unsync_children: It is used when role.level > 1 and indicates how many sptes in the page point at unsync pages or unsynchronized children. - unsync_child_bitmap: - A bitmap indicating which sptes in spt point (directly or indirectly) at - pages that may be unsynchronized. Used to quickly locate all unsychronized - pages reachable from a given page. Reverse map ===========
arch/x86/include/asm/kvm_host.h +0 -1
@@ -247,7 +247,6 @@ struct kvm_mmu_page { };unsigned long parent_ptes;/* Reverse mapping for parent_pte */ -DECLARE_BITMAP (unsync_child_bitmap,512 );#ifdef CONFIG_X86_32 int clear_spte_count;
arch/x86/kvm/mmu.c +52 -25
132 lines not on this page · raw message
Signature · 1 line
1.7.7.4
PATCH 5/8 KVM: MMU: optimize walking unsync shadow page
In reply to Xiao GuangrongExactly, unysnc_children is the number of unsync sptes, we can use to avoid unnecessary spte fetching
--- arch/x86/kvm/mmu.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-)
arch/x86/kvm/mmu.c +4 -1
@@ -1468,12 +1468,15 @@ static int __mmu_unsync_walk(struct kvm_mmu_page *sp, struct kvm_mmu_pages *pvec) {u64 *spte; -int i, ret, nr_unsync_leaf =0 ; +int i, ret, nr_unsync_leaf =0 , unysnc_children = sp->unsync_children;for_each_unsync_children (sp, spte, i) {struct kvm_mmu_page *child;u64 ent = *spte; +if (!unysnc_children--) +break ; +WARN_ON (!is_shadow_present_pte (ent) ||is_large_pte (ent)); child =page_header (ent &PT64_BASE_ADDR_MASK );
Signature · 1 line
1.7.7.4
PATCH 6/8 KVM: MMU: optimize handing invlpg
In reply to Xiao GuangrongUse unsync bit to see if the spte is point to unsync child
--- arch/x86/kvm/paging_tmpl.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
arch/x86/kvm/paging_tmpl.h +1 -1
@@ -706,7 +706,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)FNAME (update_pte)(vcpu, sp, sptep, &gpte); } -if (!is_shadow_present_pte (*sptep) || !sp->unsync_children) +if (!mmu_spte_is_unsync_child (sptep))break ; }spin_unlock (&vcpu->kvm->mmu_lock);
Signature · 1 line
1.7.7.4
PATCH 7/8 KVM: MMU: remove the redundant get_written_sptes
In reply to Xiao Guangrongget_written_sptes is called twice in kvm_mmu_pte_write, one of them can be removed
--- arch/x86/kvm/mmu.c | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-)
arch/x86/kvm/mmu.c +2 -4
@@ -3575,7 +3575,7 @@ static u64 mmu_pte_write_fetch_gpte(struct kvm_vcpu *vcpu, gpa_t *gpa,* If we're seeing too many writes to a page, it may no longer be a page table, * or we may be forking, in which case it is better to unmap the page. */ -static bool detect_write_flooding (struct kvm_mmu_page *sp,u64 *spte) +static bool detect_write_flooding (struct kvm_mmu_page *sp) {/* * Skip write-flooding detected for the sp whose level is 1, because
@@ -3684,10 +3684,8 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, mask.cr0_wp = mask.cr4_pae = mask.nxe =1 ;for_each_gfn_indirect_valid_sp (vcpu->kvm, sp, gfn, node) { - spte =get_written_sptes (sp, gpa, &npte); -if (detect_write_misaligned (sp, gpa, bytes) || -detect_write_flooding (sp, spte)) { +detect_write_flooding (sp)) { zap_page |= !!kvm_mmu_prepare_zap_page (vcpu->kvm, sp, &invalid_list); ++vcpu->kvm->stat.mmu_flooded;
Signature · 1 line
1.7.7.4
PATCH 8/8 KVM: MMU: remove PT64_SECOND_AVAIL_BITS_SHIFT
In reply to Xiao GuangrongIt is not used, remove it
--- arch/x86/kvm/mmu.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)
arch/x86/kvm/mmu.c +0 -1
@@ -91,7 +91,6 @@ module_param(dbg, bool, 0644);#define PTE_PREFETCH_NUM 8#define PT_FIRST_AVAIL_BITS_SHIFT 9 -#define PT64_SECOND_AVAIL_BITS_SHIFT 52#define PT64_LEVEL_BITS 9
Signature · 1 line
1.7.7.4
Re: [PATCH 4/8] KVM: MMU: drop unsync_child_bitmap
In reply to Xiao GuangrongQuoted 3 lines · Xiao Guangrong
unsync_child_bitmapis used to record which spte has unsync page or unsync children, we can set a free bit in the spte instead of it
unsync_child_bitmap takes one cacheline; the shadow page table takes
64. This will make unsync/resync much more expensive.
I suggest to put unsync_child_bitmap at the end (together with unsync_children) and simply not allocate it when unneeded (have two kmem_caches for the two cases).
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 8/8] KVM: MMU: remove PT64_SECOND_AVAIL_BITS_SHIFT
In reply to Xiao GuangrongQuoted 12 lines · Xiao Guangrong
It is not used, remove it
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)arch/x86/kvm/mmu.c +0
-0
arch/x86/kvm/mmu.c module_param(dbg, bool, 0644); · line 95
Quoted 6 lines
arch/x86/kvm/mmu.c +0
-1@@ -91,7 +91,6 @@ module_param(dbg, bool, 0644);#define PTE_PREFETCH_NUM 8#define PT_FIRST_AVAIL_BITS_SHIFT 9 -#define PT64_SECOND_AVAIL_BITS_SHIFT 52
Don't see a reason to drop it, we may use it some day.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 1/8] KVM: MMU: combine unsync and unsync_children
In reply to Xiao GuangrongAbout naming issues in the kvm mmu code.
Not restricted to your patch series, so please take as a suggestion for the future.
arch/x86/kvm/mmu.c module_param(dbg, bool, 0644); · line 157
Quoted 4 lines · Xiao Guangrong
arch/x86/kvm/mmu.c +4
-0+static bool sp_is_unsync (struct kvm_mmu_page *sp) +{ +return sp->role.level ==PT_PAGE_TABLE_LEVEL && sp->unsync; +}
is_unsync_sp() is more consistent with others?
e.g. is_large_pte(), is_writable_pte(), is_last_spte() Takuya
Quoted 11 lines
arch/x86/kvm/mmu.c +11
-0+ +static unsigned int sp_unsync_children_num (struct kvm_mmu_page *sp) +{ +unsigned int num =0 ; + +if (sp->role.level !=PT_PAGE_TABLE_LEVEL ) + num = sp->unsync_children; + +return num; +} +
Re: [PATCH 3/8] KVM: MMU: do not add a nonpresent spte to rmaps of its child
In reply to Xiao Guangrongarch/x86/kvm/mmu.c static unsigned kvm_page_table_hashfn(gfn_t gfn) · line 1333
Quoted 12 lines · Xiao Guangrong
arch/x86/kvm/mmu.c +4
-2-static void mmu_page_add_parent_pte (struct kvm_vcpu *vcpu, -struct kvm_mmu_page *sp,u64 *parent_pte) +static void mmu_page_add_set_parent_pte (struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp, +u64 *parent_pte) {if (!parent_pte)return ; +mmu_spte_set (parent_pte,__pa (sp->spt) |SHADOW_PAGE_TABLE );pte_list_add (vcpu, parent_pte,&sp->parent_ptes); }
There are a few prefixes in the kvm mmu code.
e.g. mmu_page_, kvm_mmu_, kvm_mmu_page_, ...
Sometimes we also use "sp".
How about deciding a consistent way from now on?
E.g.
if the function is static and for local use only, such a prefix can be eliminated,
if it is used outside of mmu.c, kvm_mmu_ is needed,
we use sp for kvm_mmu_page,
...
(just an example)
Takuya
Re: [PATCH 3/8] KVM: MMU: do not add a nonpresent spte to rmaps of its child
Quoted 17 lines · Takuya Yoshikawa
arch/x86/kvm/mmu.c static unsigned kvm_page_table_hashfn(gfn_t gfn) · line 1333
Quoted 12 lines · Xiao Guangrong
arch/x86/kvm/mmu.c +4
-2-static void mmu_page_add_parent_pte (struct kvm_vcpu *vcpu, -struct kvm_mmu_page *sp,u64 *parent_pte) +static void mmu_page_add_set_parent_pte (struct kvm_vcpu *vcpu, +struct kvm_mmu_page *sp, +u64 *parent_pte) {if (!parent_pte)return ; +mmu_spte_set (parent_pte,__pa (sp->spt) |SHADOW_PAGE_TABLE );pte_list_add (vcpu, parent_pte,&sp->parent_ptes); }There are a few prefixes in the kvm mmu code.
Quoted 6 lines
e.g. mmu_page_, kvm_mmu_, kvm_mmu_page_, ...Sometimes we also use "sp".
How about deciding a consistent way from now on?
In general I am in favour of consistency, but I'm not inviting mass-renaming patches. Let's try to be consistent in new patches (like your suggestions on this patchset).
Quoted 8 lines
E.g.
if the function is static and for local use only, such a prefix can be eliminated,if it is used outside of
mmu.c,kvm_mmu_is needed,we use sp for
kvm_mmu_page,
...
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 1/8] KVM: MMU: combine unsync and unsync_children
In reply to Xiao GuangrongQuoted 74 lines · Xiao Guangrong
unsync is only used for the page of level == 1 and
unsync_childrenis only used in the upper page, so combine them to reduce the size of shadow page structureSigned-off-by Xiao Guangrong <[email protected]>--- Documentation/virtual/kvm/mmu.txt | 5 ++- arch/x86/include/asm/kvm_host.h | 14 +++++++++++- arch/x86/kvm/mmu.c | 39 +++++++++++++++++++++++++----------- arch/x86/kvm/mmu_audit.c | 6 ++-- arch/x86/kvm/mmutrace.h | 3 +- arch/x86/kvm/paging_tmpl.h | 2 +- 6 files changed, 48 insertions(+), 21 deletions(-)Documentation/virtual/kvm/mmu.txt +3
-2@@ -205,14 +205,15 @@ Shadow pages contain the following information: this page's spt. Otherwise, parent_ptes points at a data structure with a list of parent_ptes. unsync: + It is used when role.level == 1, only level 1 shadow pages can be unsync. If true, then the translations in this page may not match the guest's translation. This is equivalent to the state of the tlb when a pte is changed but before the tlb entry is flushed. Accordingly, unsync ptes are synchronized when the guest executes invlpg or flushes its tlb by other means. Valid for leaf pages. unsync_children: - How many sptes in the page point at pages that are unsync (or have - unsynchronized children). + It is used when role.level > 1 and indicates how many sptes in the page + point at unsync pages or unsynchronized children. unsync_child_bitmap: A bitmap indicating which sptes in spt point (directly or indirectly) at pages that may be unsynchronized. Used to quickly locate all unsychronizedarch/x86/include/asm/kvm_host.h +12
-2@@ -233,9 +233,19 @@ struct kvm_mmu_page {* in this shadow page. */ DECLARE_BITMAP (slot_bitmap,KVM_MEM_SLOTS_NUM ); -bool unsync;int root_count;/* Currently serving as active root */ -unsigned int unsync_children; + +/* +* If role.level == 1, unsync indicates whether the sp is +* unsync, otherwise unsync_children indicates the number +* of sptes which point at unsync sp or unsychronized children. +* See sp_is_unsync() / sp_unsync_children_num. +*/ +union { +bool unsync; +unsigned int unsync_children; + }; +unsigned long parent_ptes;/* Reverse mapping for parent_pte */ DECLARE_BITMAP (unsync_child_bitmap,512 );arch/x86/kvm/mmu.c +4
-0@@ -151,6 +151,21 @@ module_param(dbg, bool, 0644);#define SHADOW_PT_INDEX (addr, level) PT64_INDEX(addr, level) +static bool sp_is_unsync (struct kvm_mmu_page *sp) +{ +return sp->role.level ==PT_PAGE_TABLE_LEVEL && sp->unsync; +}
arch/x86/kvm/mmu.c module_param(dbg, bool, 0644); · line 167
Quoted 10 lines
arch/x86/kvm/mmu.c +10
-0+ +static unsigned int sp_unsync_children_num (struct kvm_mmu_page *sp) +{ +unsigned int num =0 ; + +if (sp->role.level !=PT_PAGE_TABLE_LEVEL ) + num = sp->unsync_children; + +return num; +}
IIRC there are windows where unsync_children is not accurate. Have you verified this function is not called inside one of those windows?
Re: [PATCH 4/8] KVM: MMU: drop unsync_child_bitmap
In reply to Avi KivityQuoted 8 lines · Avi Kivity
Quoted 3 lines · Xiao Guangrong
unsync_child_bitmapis used to record which spte has unsync page or unsync children, we can set a free bit in the spte instead of it
unsync_child_bitmaptakes one cacheline; the shadow page table takes
64. This will make unsync/resync much more expensive.
Indeed, need more thing about this patch...
Quoted 5 lines
I suggest to put
unsync_child_bitmapat the end (together withunsync_children) and simply not allocate it when unneeded (have twokmem_cachesfor the two cases).
Good point, thanks Avi.
Re: [PATCH 8/8] KVM: MMU: remove PT64_SECOND_AVAIL_BITS_SHIFT
In reply to Avi KivityQuoted 15 lines · Avi Kivity
Quoted 14 lines · Xiao Guangrong
It is not used, remove it
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)arch/x86/kvm/mmu.c +0
-0@@ -91,7 +91,6 @@ module_param(dbg, bool, 0644);#define PTE_PREFETCH_NUM 8
Quoted 7 lines
arch/x86/kvm/mmu.c module_param(dbg, bool, 0644); · line 96
Quoted 4 lines
arch/x86/kvm/mmu.c +0
-1#define PT_FIRST_AVAIL_BITS_SHIFT 9 -#define PT64_SECOND_AVAIL_BITS_SHIFT 52Don't see a reason to drop it, we may use it some day.
Anyway, it is not used in currently code, I do not have strong opinion on it :) Please ignore it if you do not like.
Re: [PATCH 1/8] KVM: MMU: combine unsync and unsync_children
In reply to Marcelo TosattiQuoted 80 lines · Marcelo Tosatti
Quoted 79 lines · Xiao Guangrong
unsync is only used for the page of level == 1 and
unsync_childrenis only used in the upper page, so combine them to reduce the size of shadow page structureSigned-off-by Xiao Guangrong <[email protected]>--- Documentation/virtual/kvm/mmu.txt | 5 ++- arch/x86/include/asm/kvm_host.h | 14 +++++++++++- arch/x86/kvm/mmu.c | 39 +++++++++++++++++++++++++----------- arch/x86/kvm/mmu_audit.c | 6 ++-- arch/x86/kvm/mmutrace.h | 3 +- arch/x86/kvm/paging_tmpl.h | 2 +- 6 files changed, 48 insertions(+), 21 deletions(-)Documentation/virtual/kvm/mmu.txt +3
-2@@ -205,14 +205,15 @@ Shadow pages contain the following information: this page's spt. Otherwise, parent_ptes points at a data structure with a list of parent_ptes. unsync: + It is used when role.level == 1, only level 1 shadow pages can be unsync. If true, then the translations in this page may not match the guest's translation. This is equivalent to the state of the tlb when a pte is changed but before the tlb entry is flushed. Accordingly, unsync ptes are synchronized when the guest executes invlpg or flushes its tlb by other means. Valid for leaf pages. unsync_children: - How many sptes in the page point at pages that are unsync (or have - unsynchronized children). + It is used when role.level > 1 and indicates how many sptes in the page + point at unsync pages or unsynchronized children. unsync_child_bitmap: A bitmap indicating which sptes in spt point (directly or indirectly) at pages that may be unsynchronized. Used to quickly locate all unsychronizedarch/x86/include/asm/kvm_host.h +12
-2@@ -233,9 +233,19 @@ struct kvm_mmu_page {* in this shadow page. */ DECLARE_BITMAP (slot_bitmap,KVM_MEM_SLOTS_NUM ); -bool unsync;int root_count;/* Currently serving as active root */ -unsigned int unsync_children; + +/* +* If role.level == 1, unsync indicates whether the sp is +* unsync, otherwise unsync_children indicates the number +* of sptes which point at unsync sp or unsychronized children. +* See sp_is_unsync() / sp_unsync_children_num. +*/ +union { +bool unsync; +unsigned int unsync_children; + }; +unsigned long parent_ptes;/* Reverse mapping for parent_pte */ DECLARE_BITMAP (unsync_child_bitmap,512 );arch/x86/kvm/mmu.c +9
-0@@ -151,6 +151,21 @@ module_param(dbg, bool, 0644);#define SHADOW_PT_INDEX (addr, level) PT64_INDEX(addr, level) +static bool sp_is_unsync (struct kvm_mmu_page *sp) +{ +return sp->role.level ==PT_PAGE_TABLE_LEVEL && sp->unsync; +} + +static unsigned int sp_unsync_children_num (struct kvm_mmu_page *sp) +{ +unsigned int num =0 ; +
Quoted 9 lines
arch/x86/kvm/mmu.c module_param(dbg, bool, 0644); · line 167
Quoted 5 lines
arch/x86/kvm/mmu.c +5
-0+if (sp->role.level !=PT_PAGE_TABLE_LEVEL ) + num = sp->unsync_children; + +return num; +}IIRC there are windows where
unsync_childrenis not accurate. Have you verified this function is not called inside one of those windows?
Actually, this function is only used to see whether the sp has unsync children in current code.
And this patch did not change the logic, it just use sp_unsync_children_num instead of using sp->unsync_children directly.
Quoted 5 lines · Xiao Guangrong
In this patchset, we observably reduce the size of struct
kvm_mmu_pageand let the code more simple.And this patchset have already tested by unittest and RHEL.6.1 setup/boot/reboot/shutdown.
Hi Avi, Marcelo,
Could you please apply patch 1, 2, 7 if they are good for you? :)
Re: [PATCH 1/8] KVM: MMU: combine unsync and unsync_children
In reply to Xiao GuangrongQuoted 74 lines · Xiao Guangrong
unsync is only used for the page of level == 1 and
unsync_childrenis only used in the upper page, so combine them to reduce the size of shadow page structureSigned-off-by Xiao Guangrong <[email protected]>--- Documentation/virtual/kvm/mmu.txt | 5 ++- arch/x86/include/asm/kvm_host.h | 14 +++++++++++- arch/x86/kvm/mmu.c | 39 +++++++++++++++++++++++++----------- arch/x86/kvm/mmu_audit.c | 6 ++-- arch/x86/kvm/mmutrace.h | 3 +- arch/x86/kvm/paging_tmpl.h | 2 +- 6 files changed, 48 insertions(+), 21 deletions(-)Documentation/virtual/kvm/mmu.txt +3
-2@@ -205,14 +205,15 @@ Shadow pages contain the following information: this page's spt. Otherwise, parent_ptes points at a data structure with a list of parent_ptes. unsync: + It is used when role.level == 1, only level 1 shadow pages can be unsync. If true, then the translations in this page may not match the guest's translation. This is equivalent to the state of the tlb when a pte is changed but before the tlb entry is flushed. Accordingly, unsync ptes are synchronized when the guest executes invlpg or flushes its tlb by other means. Valid for leaf pages. unsync_children: - How many sptes in the page point at pages that are unsync (or have - unsynchronized children). + It is used when role.level > 1 and indicates how many sptes in the page + point at unsync pages or unsynchronized children. unsync_child_bitmap: A bitmap indicating which sptes in spt point (directly or indirectly) at pages that may be unsynchronized. Used to quickly locate all unsychronizedarch/x86/include/asm/kvm_host.h +12
-2@@ -233,9 +233,19 @@ struct kvm_mmu_page {* in this shadow page. */ DECLARE_BITMAP (slot_bitmap,KVM_MEM_SLOTS_NUM ); -bool unsync;int root_count;/* Currently serving as active root */ -unsigned int unsync_children; + +/* +* If role.level == 1, unsync indicates whether the sp is +* unsync, otherwise unsync_children indicates the number +* of sptes which point at unsync sp or unsychronized children. +* See sp_is_unsync() / sp_unsync_children_num. +*/ +union { +bool unsync; +unsigned int unsync_children; + }; +unsigned long parent_ptes;/* Reverse mapping for parent_pte */ DECLARE_BITMAP (unsync_child_bitmap,512 );arch/x86/kvm/mmu.c +4
-0@@ -151,6 +151,21 @@ module_param(dbg, bool, 0644);#define SHADOW_PT_INDEX (addr, level) PT64_INDEX(addr, level) +static bool sp_is_unsync (struct kvm_mmu_page *sp) +{ +return sp->role.level ==PT_PAGE_TABLE_LEVEL && sp->unsync; +}
arch/x86/kvm/mmu.c module_param(dbg, bool, 0644); · line 167
Quoted 10 lines
arch/x86/kvm/mmu.c +10
-0+ +static unsigned int sp_unsync_children_num (struct kvm_mmu_page *sp) +{ +unsigned int num =0 ; + +if (sp->role.level !=PT_PAGE_TABLE_LEVEL ) + num = sp->unsync_children; + +return num; +}
This adds significant complexity/trickery to the code for little gain. For example, in kvm_mmu_page_get, you use sp_is_unsync() which depends on sp->role.level, but that is not set yet.
In general, given the number of optimizations made to the shadow mmu code, and the fact that over time NPT will dominate, i'd suggest time should be spent improving other areas.
Re: [PATCH 2/8] KVM: MMU: set the dirty bit for the upper shadow page
In reply to Xiao GuangrongQuoted 7 lines · Xiao Guangrong
Upper page do not need to be tracked the status bit, it is safe to set the dirty and let cpu to happily prefetch it
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 13 +++++-------- 1 files changed, 5 insertions(+), 8 deletions(-)
Dirty bit is ignored in non-leaf pagetable entries.
Marcelo Tosatti
AppliedRe: [PATCH 7/8] KVM: MMU: remove the redundant get_written_sptes
In reply to Xiao GuangrongQuoted 7 lines · Xiao Guangrong
get_written_sptesis called twice inkvm_mmu_pte_write, one of them can be removedSigned-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-)
Applied, thanks.
Re: [PATCH 1/8] KVM: MMU: combine unsync and unsync_children
In reply to Marcelo TosattiQuoted 4 lines · Marcelo Tosatti
In general, given the number of optimizations made to the shadow mmu code, and the fact that over time NPT will dominate, i'd suggest time should be spent improving other areas.
I totally agree with you, thanks Marcelo!
Re: [PATCH 2/8] KVM: MMU: set the dirty bit for the upper shadow page
In reply to Marcelo TosattiQuoted 11 lines · Marcelo Tosatti
Quoted 7 lines · Xiao Guangrong
Upper page do not need to be tracked the status bit, it is safe to set the dirty and let cpu to happily prefetch it
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/kvm/mmu.c | 13 +++++-------- 1 files changed, 5 insertions(+), 8 deletions(-)Dirty bit is ignored in non-leaf pagetable entries.
Sorry, i forgot it!