We do not need to zap all shadow pages of the guest when we create or destroy a slot in this function. To change this, we make kvm_mmu_zap_all()/kvm_arch_flush_shadow() zap only those which have mappings into a given slot. Furthermore, the condition to see if we have any mmio sptes to clear is changed so that we will not do flush for newly created slots. Note that the way we iterate over the active shadow pages is also changed a bit to avoid checking unrelated pages again and again. With all these changes applied, the total amount of time used to flush the shadow pages of a usual Linux guest, running Fedora with 4GB memory, during a shutdown was reduced from 90ms to 60ms. Furthermore, the total number of flushes needed to boot and shutdown that guest was also reduced from 52 to 31. Signed-off-by: Takuya Yoshikawa <yoshikawa.takuya@xxxxxxxxxxxxx> --- arch/ia64/kvm/kvm-ia64.c | 2 +- arch/powerpc/kvm/powerpc.c | 2 +- arch/s390/kvm/kvm-s390.c | 2 +- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/mmu.c | 22 ++++++++++++++++++---- arch/x86/kvm/x86.c | 13 ++++++++++--- include/linux/kvm_host.h | 2 +- virt/kvm/kvm_main.c | 15 ++++++--------- 8 files changed, 39 insertions(+), 21 deletions(-) diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index d8ddbba..c86799f 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1621,7 +1621,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, return; } -void kvm_arch_flush_shadow(struct kvm *kvm) +void kvm_arch_flush_shadow(struct kvm *kvm, int slot) { kvm_flush_remote_tlbs(kvm); } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 00d7e34..f050637 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -309,7 +309,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, } -void kvm_arch_flush_shadow(struct kvm *kvm) +void kvm_arch_flush_shadow(struct kvm *kvm, int slot) { } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 17ad69d..290fcf0 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -871,7 +871,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, return; } -void kvm_arch_flush_shadow(struct kvm *kvm) +void kvm_arch_flush_shadow(struct kvm *kvm, int slot) { } diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 74c9edf..ecb3f78 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -714,7 +714,7 @@ int kvm_mmu_reset_context(struct kvm_vcpu *vcpu); void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot); int kvm_mmu_rmap_write_protect(struct kvm *kvm, u64 gfn, struct kvm_memory_slot *slot); -void kvm_mmu_zap_all(struct kvm *kvm); +void kvm_mmu_zap_all(struct kvm *kvm, int slot); unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm); void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int kvm_nr_mmu_pages); diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index ff053ca..df3e73a 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -3867,16 +3867,30 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot) kvm_flush_remote_tlbs(kvm); } -void kvm_mmu_zap_all(struct kvm *kvm) +/* + * kvm_mmu_zap_all - zap all shadows which have mappings into a given slot + * @kvm: the kvm instance + * @slot: id of the target slot + * + * If @slot is -1, zap all shadow pages. + */ +void kvm_mmu_zap_all(struct kvm *kvm, int slot) { struct kvm_mmu_page *sp, *node; LIST_HEAD(invalid_list); + int try_again; spin_lock(&kvm->mmu_lock); restart: - list_for_each_entry_safe(sp, node, &kvm->arch.active_mmu_pages, link) - if (kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list)) - goto restart; + try_again = 0; + list_for_each_entry_safe(sp, node, &kvm->arch.active_mmu_pages, link) { + if ((slot >= 0) && !test_bit(slot, sp->slot_bitmap)) + continue; + + try_again |= kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list); + } + if (try_again) + goto restart; kvm_mmu_commit_zap_page(kvm, &invalid_list); spin_unlock(&kvm->mmu_lock); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c9d99e5..7ce8046 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5035,7 +5035,7 @@ int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt) * to ensure that the updated hypercall appears atomically across all * VCPUs. */ - kvm_mmu_zap_all(vcpu->kvm); + kvm_mmu_zap_all(vcpu->kvm, -1); kvm_x86_ops->patch_hypercall(vcpu, instruction); @@ -6368,9 +6368,16 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, spin_unlock(&kvm->mmu_lock); } -void kvm_arch_flush_shadow(struct kvm *kvm) +/* + * kvm_arch_flush_shadow - flush shadows which have mappings into a given slot + * @kvm: the kvm instance + * @slot: id of the target slot + * + * If @slot is -1, flush all shadow pages. + */ +void kvm_arch_flush_shadow(struct kvm *kvm, int slot) { - kvm_mmu_zap_all(kvm); + kvm_mmu_zap_all(kvm, slot); kvm_reload_remote_mmus(kvm); } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 355e445..a5499af 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -385,7 +385,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, int user_alloc); bool kvm_largepages_enabled(void); void kvm_disable_largepages(void); -void kvm_arch_flush_shadow(struct kvm *kvm); +void kvm_arch_flush_shadow(struct kvm *kvm, int slot); int gfn_to_page_many_atomic(struct kvm *kvm, gfn_t gfn, struct page **pages, int nr_pages); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e4431ad..98ae020 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -412,7 +412,7 @@ static void kvm_mmu_notifier_release(struct mmu_notifier *mn, int idx; idx = srcu_read_lock(&kvm->srcu); - kvm_arch_flush_shadow(kvm); + kvm_arch_flush_shadow(kvm, -1); srcu_read_unlock(&kvm->srcu, idx); } @@ -575,7 +575,7 @@ static void kvm_destroy_vm(struct kvm *kvm) #if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) mmu_notifier_unregister(&kvm->mmu_notifier, kvm->mm); #else - kvm_arch_flush_shadow(kvm); + kvm_arch_flush_shadow(kvm, -1); #endif kvm_arch_destroy_vm(kvm); kvm_free_physmem(kvm); @@ -800,7 +800,7 @@ int __kvm_set_memory_region(struct kvm *kvm, * - gfn_to_hva (kvm_read_guest, gfn_to_pfn) * - kvm_is_visible_gfn (mmu_check_roots) */ - kvm_arch_flush_shadow(kvm); + kvm_arch_flush_shadow(kvm, mem->slot); kfree(old_memslots); } @@ -835,12 +835,9 @@ int __kvm_set_memory_region(struct kvm *kvm, kvm_arch_commit_memory_region(kvm, mem, old, user_alloc); - /* - * If the new memory slot is created, we need to clear all - * mmio sptes. - */ - if (npages && old.base_gfn != mem->guest_phys_addr >> PAGE_SHIFT) - kvm_arch_flush_shadow(kvm); + /* Need to clear all mmio sptes used before. */ + if (npages && old.npages && base_gfn != old.base_gfn) + kvm_arch_flush_shadow(kvm, mem->slot); kvm_free_physmem_slot(&old, &new); kfree(old_memslots); -- 1.7.5.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html