From patchwork Mon Aug 6 10:06:03 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Mackerras X-Patchwork-Id: 175311 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3B7B02C00A3 for ; Mon, 6 Aug 2012 20:08:54 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755727Ab2HFKIo (ORCPT ); Mon, 6 Aug 2012 06:08:44 -0400 Received: from ozlabs.org ([203.10.76.45]:44783 "EHLO ozlabs.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755641Ab2HFKIf (ORCPT ); Mon, 6 Aug 2012 06:08:35 -0400 Received: by ozlabs.org (Postfix, from userid 1003) id ACE652C00A2; Mon, 6 Aug 2012 20:08:32 +1000 (EST) Date: Mon, 6 Aug 2012 20:06:03 +1000 From: Paul Mackerras To: Alexander Graf , kvm-ppc@vger.kernel.org Cc: kvm@vger.kernel.org Subject: [PATCH 3/5] KVM: PPC: Book3S HV: Handle memory slot deletion and modification correctly Message-ID: <20120806100603.GD8980@bloggs.ozlabs.ibm.com> References: <20120806100207.GA8980@bloggs.ozlabs.ibm.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20120806100207.GA8980@bloggs.ozlabs.ibm.com> User-Agent: Mutt/1.5.21 (2010-09-15) Sender: kvm-ppc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm-ppc@vger.kernel.org From 44067a8ee15021583636bea4cc1d47e5370b8397 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 30 Jul 2012 16:40:54 +1000 Subject: At present the HV style of KVM doesn't handle deletion or modification of memory slots correctly. Deletion occurs when userspace does the KVM_SET_USER_MEMORY_REGION with arguments specifying a new length of zero for a slot that contains memory. Modification occurs when the arguments specify a new guest_phys_addr or flags. To allow the HV code to know which operation (creation, deletion or modification) is being requested, it needs to see the old and new contents of the memslot. kvm_arch_prepare_memory_region has this information, so we modify it to pass it down to kvmppc_core_prepare_memory_region. We then modify the HV version of that to check which operation is being performed. If it is a deletion, we call a new function kvmppc_unmap_memslot to remove any HPT (hashed page table) entries referencing the memory being removed. Similarly, if we are modifying the guest physical address we also remove any HPT entries. If the KVM_MEM_LOG_DIRTY_PAGES flag is now set and wasn't before, we call kvmppc_hv_get_dirty_log to clear any dirty bits so we can detect all modifications from now on. Signed-off-by: Paul Mackerras --- arch/powerpc/include/asm/kvm_ppc.h | 4 +++ arch/powerpc/kvm/book3s_64_mmu_hv.c | 27 ++++++++++++++-- arch/powerpc/kvm/book3s_hv.c | 61 +++++++++++++++++++++++------------ arch/powerpc/kvm/book3s_hv_rm_mmu.c | 2 +- arch/powerpc/kvm/book3s_pr.c | 2 ++ arch/powerpc/kvm/booke.c | 2 ++ arch/powerpc/kvm/powerpc.c | 2 +- 7 files changed, 76 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 0124937..044c921 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -140,11 +140,15 @@ extern void kvm_release_hpt(struct kvmppc_linear_info *li); extern int kvmppc_core_init_vm(struct kvm *kvm); extern void kvmppc_core_destroy_vm(struct kvm *kvm); extern int kvmppc_core_prepare_memory_region(struct kvm *kvm, + struct kvm_memory_slot *memslot, + struct kvm_memory_slot *old, struct kvm_userspace_memory_region *mem); extern void kvmppc_core_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem); extern int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info); +extern void kvmppc_unmap_memslot(struct kvm *kvm, + struct kvm_memory_slot *memslot); extern int kvmppc_bookehv_init(void); extern void kvmppc_bookehv_exit(void); diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index 3c635c0..87735a7 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -850,7 +850,8 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp, psize = hpte_page_size(hptep[0], ptel); if ((hptep[0] & HPTE_V_VALID) && hpte_rpn(ptel, psize) == gfn) { - hptep[0] |= HPTE_V_ABSENT; + if (kvm->arch.using_mmu_notifiers) + hptep[0] |= HPTE_V_ABSENT; kvmppc_invalidate_hpte(kvm, hptep, i); /* Harvest R and C */ rcbits = hptep[1] & (HPTE_R_R | HPTE_R_C); @@ -877,6 +878,28 @@ int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end) return 0; } +void kvmppc_unmap_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot) +{ + unsigned long *rmapp; + unsigned long gfn; + unsigned long n; + + rmapp = memslot->rmap; + gfn = memslot->base_gfn; + for (n = memslot->npages; n; --n) { + /* + * Testing the present bit without locking is OK because + * the memslot has been marked invalid already, and hence + * no new HPTEs referencing this page can be created, + * thus the present bit can't go from 0 to 1. + */ + if (*rmapp & KVMPPC_RMAP_PRESENT) + kvm_unmap_rmapp(kvm, rmapp, gfn); + ++rmapp; + ++gfn; + } +} + static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp, unsigned long gfn) { @@ -1039,7 +1062,7 @@ long kvmppc_hv_get_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) rmapp = memslot->rmap; map = memslot->dirty_bitmap; for (i = 0; i < memslot->npages; ++i) { - if (kvm_test_clear_dirty(kvm, rmapp)) + if (kvm_test_clear_dirty(kvm, rmapp) && map) __set_bit_le(i, map); ++rmapp; } diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 83e929e..aad20ca0 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1299,26 +1299,6 @@ static unsigned long slb_pgsize_encoding(unsigned long psize) return senc; } -int kvmppc_core_prepare_memory_region(struct kvm *kvm, - struct kvm_userspace_memory_region *mem) -{ - unsigned long npages; - unsigned long *phys; - - /* Allocate a slot_phys array */ - phys = kvm->arch.slot_phys[mem->slot]; - if (!kvm->arch.using_mmu_notifiers && !phys) { - npages = mem->memory_size >> PAGE_SHIFT; - phys = vzalloc(npages * sizeof(unsigned long)); - if (!phys) - return -ENOMEM; - kvm->arch.slot_phys[mem->slot] = phys; - kvm->arch.slot_npages[mem->slot] = npages; - } - - return 0; -} - static void unpin_slot(struct kvm *kvm, int slot_id) { unsigned long *physp; @@ -1343,6 +1323,47 @@ static void unpin_slot(struct kvm *kvm, int slot_id) } } +int kvmppc_core_prepare_memory_region(struct kvm *kvm, + struct kvm_memory_slot *memslot, + struct kvm_memory_slot *old, + struct kvm_userspace_memory_region *mem) +{ + unsigned long npages; + unsigned long *phys; + + /* Are we creating, deleting, or modifying the slot? */ + if (!memslot->npages) { + /* deleting */ + kvmppc_unmap_memslot(kvm, old); + if (!kvm->arch.using_mmu_notifiers) + unpin_slot(kvm, mem->slot); + return 0; + } + + if (old->npages) { + /* modifying guest_phys or flags */ + if (old->base_gfn != memslot->base_gfn) + kvmppc_unmap_memslot(kvm, old); + if (memslot->dirty_bitmap && + old->dirty_bitmap != memslot->dirty_bitmap) + kvmppc_hv_get_dirty_log(kvm, old); + return 0; + } + + /* Creating - allocate a slot_phys array if needed */ + phys = kvm->arch.slot_phys[mem->slot]; + if (!kvm->arch.using_mmu_notifiers && !phys) { + npages = mem->memory_size >> PAGE_SHIFT; + phys = vzalloc(npages * sizeof(unsigned long)); + if (!phys) + return -ENOMEM; + kvm->arch.slot_phys[mem->slot] = phys; + kvm->arch.slot_npages[mem->slot] = npages; + } + + return 0; +} + void kvmppc_core_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem) { diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c index 5c70d19..f48d7e6 100644 --- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c +++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c @@ -81,7 +81,7 @@ static void remove_revmap_chain(struct kvm *kvm, long pte_index, ptel = rev->guest_rpte |= rcbits; gfn = hpte_rpn(ptel, hpte_page_size(hpte_v, ptel)); memslot = __gfn_to_memslot(kvm_memslots(kvm), gfn); - if (!memslot || (memslot->flags & KVM_MEMSLOT_INVALID)) + if (!memslot) return; rmap = real_vmalloc_addr(&memslot->rmap[gfn - memslot->base_gfn]); diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index a1baec3..c5e0062 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -1184,6 +1184,8 @@ int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info) #endif /* CONFIG_PPC64 */ int kvmppc_core_prepare_memory_region(struct kvm *kvm, + struct kvm_memory_slot *memslot, + struct kvm_memory_slot *old, struct kvm_userspace_memory_region *mem) { return 0; diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index d25a097..f037c64 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -1254,6 +1254,8 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) } int kvmppc_core_prepare_memory_region(struct kvm *kvm, + struct kvm_memory_slot *memslot, + struct kvm_memory_slot *old, struct kvm_userspace_memory_region *mem) { return 0; diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 87f4dc8..e84ba15 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -315,7 +315,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, int user_alloc) { - return kvmppc_core_prepare_memory_region(kvm, mem); + return kvmppc_core_prepare_memory_region(kvm, memslot, &old, mem); } void kvm_arch_commit_memory_region(struct kvm *kvm,