From patchwork Mon Jul 30 09:50:24 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Mackerras X-Patchwork-Id: 173984 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 1F73D2C0096 for ; Mon, 30 Jul 2012 19:50:13 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752808Ab2G3JuM (ORCPT ); Mon, 30 Jul 2012 05:50:12 -0400 Received: from ozlabs.org ([203.10.76.45]:59978 "EHLO ozlabs.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752625Ab2G3JuL (ORCPT ); Mon, 30 Jul 2012 05:50:11 -0400 Received: by ozlabs.org (Postfix, from userid 1003) id ECC062C0093; Mon, 30 Jul 2012 19:50:09 +1000 (EST) Date: Mon, 30 Jul 2012 19:50:24 +1000 From: Paul Mackerras To: Alexander Graf , kvm-ppc@vger.kernel.org Cc: kvm@vger.kernel.org Subject: [PATCH] KVM: PPC: Book3S HV: Handle memory slot deletion and modification Message-ID: <20120730095024.GA31675@bloggs.ozlabs.ibm.com> MIME-Version: 1.0 Content-Disposition: inline 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 4df5da62cb49b71c9a70072341dcb0557768c520 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 30 Jul 2012 16:40:54 +1000 Subject: [PATCH] KVM: PPC: Book3S HV: Handle memory slot deletion and modification correctly 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 d03eb6f..69fee3b 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -830,7 +830,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); @@ -850,6 +851,28 @@ int kvm_unmap_hva(struct kvm *kvm, unsigned long hva) 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) { @@ -1012,7 +1035,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 64fdc3d..3fd63aa 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1317,26 +1317,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; @@ -1361,6 +1341,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 75e196d..72fbbff 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,