> > Waaaay off topic, why do we still bother with stat.max_mmu_page_hash_collision? > I assume it was originally added to tune the hashing logic? At this point is it > anything but wasted cycles? > > static struct kvm_mmu_page *kvm_mmu_find_shadow_page(struct kvm_vcpu *vcpu, > gfn_t gfn, > unsigned int gfn_hash, > union kvm_mmu_page_role role) > { > struct hlist_head *sp_list = &kvm->arch.mmu_page_hash[gfn_hash]; > struct kvm_mmu_page *sp; > LIST_HEAD(invalid_list); > > int collisions = 0; > > for_each_valid_sp(kvm, sp, sp_list) { > if (sp->gfn != gfn) { > collisions++; > continue; > } > > if (sp->role.word != role.word) { > /* > * If the guest is creating an upper-level page, zap > * unsync pages for the same gfn. While it's possible > * the guest is using recursive page tables, in all > * likelihood the guest has stopped using the unsync > * page and is installing a completely unrelated page. > * Unsync pages must not be left as is, because the new > * upper-level page will be write-protected. > */ > if (role.level > PG_LEVEL_4K && sp->unsync) > kvm_mmu_prepare_zap_page(vcpu->kvm, sp, invalid_list); > > continue; > } > > /* unsync and write-flooding only apply to indirect SPs. */ > if (sp->role.direct) > goto out; > > if (sp->unsync) { > /* > * The page is good, but is stale. kvm_sync_page does > * get the latest guest state, but (unlike mmu_unsync_children) > * it doesn't write-protect the page or mark it synchronized! > * 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. > * > * If the sync fails, the page is zapped. If so, break > * in order to rebuild it. > */ > if (!kvm_sync_page(vcpu, sp, &invalid_list)) > break; > > WARN_ON(!list_empty(&invalid_list)); > kvm_flush_remote_tlbs(vcpu->kvm); > } > > __clear_sp_write_flooding_count(sp); > goto out; > } > > sp = NULL; > > out: > if (collisions > kvm->stat.max_mmu_page_hash_collisions) > kvm->stat.max_mmu_page_hash_collisions = collisions; > > kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list); > return sp; > } > > static struct kvm_mmu_page *kvm_mmu_alloc_shadow_page(struct kvm_vcpu *vcpu, > gfn_t gfn, > unsigned int gfn_hash, > union kvm_mmu_page_role role) > { > struct kvm_mmu_page *sp = __kvm_mmu_alloc_shadow_page(vcpu, role.direct); > struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); > struct hlist_head *sp_list = &kvm->arch.mmu_page_hash[gfn_hash]; > > ++kvm->stat.mmu_cache_miss; > > sp->gfn = gfn; > sp->role = role; > sp->mmu_valid_gen = kvm->arch.mmu_valid_gen; > > /* > * active_mmu_pages must be a FIFO list, as kvm_zap_obsolete_pages() > * depends on valid pages being added to the head of the list. See > * comments in kvm_zap_obsolete_pages(). > */ > list_add(&sp->link, &kvm->arch.active_mmu_pages); > kvm_mod_used_mmu_pages(kvm, 1); > > sp_list = &kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)]; > hlist_add_head(&sp->hash_link, sp_list); > > if (!role.direct) > account_shadowed(kvm, slot, sp); > } > > > static struct kvm_mmu_page *kvm_mmu_get_shadow_page(struct kvm_vcpu *vcpu, > gfn_t gfn, > union kvm_mmu_page_role role) > { > unsigned int gfn_hash = kvm_page_table_hashfn(gfn); > struct kvm_mmu_page *sp; > bool created = false; > > sp = kvm_mmu_find_shadow_page(vcpu, gfn, gfn_hash, role); > if (!sp) { > created = true; > sp = kvm_mmu_alloc_shadow_page(vcpu, gfn, gfn_hash, role); > } > > trace_kvm_mmu_get_page(sp, created); > return sp; > }