KVM: PPC: Book3S HV: Add infrastructure for running HPT guests on radix host
authorPaul Mackerras <paulus@ozlabs.org>
Wed, 13 Sep 2017 06:00:10 +0000 (16:00 +1000)
committerPaul Mackerras <paulus@ozlabs.org>
Wed, 1 Nov 2017 04:36:28 +0000 (15:36 +1100)
This sets up the machinery for switching a guest between HPT (hashed
page table) and radix MMU modes, so that in future we can run a HPT
guest on a radix host on POWER9 machines.

* The KVM_PPC_CONFIGURE_V3_MMU ioctl can now specify either HPT or
  radix mode, on a radix host.

* The KVM_CAP_PPC_MMU_HASH_V3 capability now returns 1 on POWER9
  with HV KVM on a radix host.

* The KVM_PPC_GET_SMMU_INFO returns information about the HPT MMU on a
  radix host.

* The KVM_PPC_ALLOCATE_HTAB ioctl on a radix host will switch the
  guest to HPT mode and allocate a HPT.

* For simplicity, we now allocate the rmap array for each memslot,
  even on a radix host, since it will be needed if the guest switches
  to HPT mode.

* Since we cannot yet run a HPT guest on a radix host, the KVM_RUN
  ioctl will return an EINVAL error in that case.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/kvm/book3s_64_mmu_hv.c
arch/powerpc/kvm/book3s_64_mmu_radix.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/powerpc.c

index ba5fadd6f3c95b9a6163ac1506d2eaf48a850197..96753f3aac6dd7e753ba9b2f9ad5e4ebba7f50aa 100644 (file)
@@ -168,6 +168,7 @@ extern int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order);
 extern void kvmppc_set_hpt(struct kvm *kvm, struct kvm_hpt_info *info);
 extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order);
 extern void kvmppc_free_hpt(struct kvm_hpt_info *info);
+extern void kvmppc_rmap_reset(struct kvm *kvm);
 extern long kvmppc_prepare_vrma(struct kvm *kvm,
                                struct kvm_userspace_memory_region *mem);
 extern void kvmppc_map_vrma(struct kvm_vcpu *vcpu,
@@ -177,6 +178,8 @@ extern long kvm_spapr_tce_attach_iommu_group(struct kvm *kvm, int tablefd,
                struct iommu_group *grp);
 extern void kvm_spapr_tce_release_iommu_group(struct kvm *kvm,
                struct iommu_group *grp);
+extern int kvmppc_switch_mmu_to_hpt(struct kvm *kvm);
+extern int kvmppc_switch_mmu_to_radix(struct kvm *kvm);
 
 extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
                                struct kvm_create_spapr_tce_64 *args);
index 944f7a532879327648e81a4b3cd582e2852751ed..6aec8a22aeff3b1d70b71cfb544be9285966a14d 100644 (file)
@@ -73,8 +73,6 @@ struct kvm_resize_hpt {
        struct kvm_hpt_info hpt;
 };
 
-static void kvmppc_rmap_reset(struct kvm *kvm);
-
 int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order)
 {
        unsigned long hpt = 0;
@@ -136,9 +134,6 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order)
        long err = -EBUSY;
        struct kvm_hpt_info info;
 
-       if (kvm_is_radix(kvm))
-               return -EINVAL;
-
        mutex_lock(&kvm->lock);
        if (kvm->arch.mmu_ready) {
                kvm->arch.mmu_ready = 0;
@@ -149,6 +144,12 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order)
                        goto out;
                }
        }
+       if (kvm_is_radix(kvm)) {
+               err = kvmppc_switch_mmu_to_hpt(kvm);
+               if (err)
+                       goto out;
+       }
+
        if (kvm->arch.hpt.order == order) {
                /* We already have a suitable HPT */
 
@@ -182,6 +183,7 @@ out:
 void kvmppc_free_hpt(struct kvm_hpt_info *info)
 {
        vfree(info->rev);
+       info->rev = NULL;
        if (info->cma)
                kvm_free_hpt_cma(virt_to_page(info->virt),
                                 1 << (info->order - PAGE_SHIFT));
@@ -349,6 +351,9 @@ static int kvmppc_mmu_book3s_64_hv_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
        int index;
        int virtmode = vcpu->arch.shregs.msr & (data ? MSR_DR : MSR_IR);
 
+       if (kvm_is_radix(vcpu->kvm))
+               return kvmppc_mmu_radix_xlate(vcpu, eaddr, gpte, data, iswrite);
+
        /* Get SLB entry */
        if (virtmode) {
                slbe = kvmppc_mmu_book3s_hv_find_slbe(vcpu, eaddr);
@@ -710,7 +715,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
        goto out_put;
 }
 
-static void kvmppc_rmap_reset(struct kvm *kvm)
+void kvmppc_rmap_reset(struct kvm *kvm)
 {
        struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
@@ -2089,10 +2094,7 @@ void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu)
 
        vcpu->arch.slb_nr = 32;         /* POWER7/POWER8 */
 
-       if (kvm_is_radix(vcpu->kvm))
-               mmu->xlate = kvmppc_mmu_radix_xlate;
-       else
-               mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate;
+       mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate;
        mmu->reset_msr = kvmppc_mmu_book3s_64_hv_reset_msr;
 
        vcpu->arch.hflags |= BOOK3S_HFLAG_SLB;
index 6336b13ed233d9a09597c83396278a8ede6fd90e..58618f644c56bd12efc74f6479d37d7ba539d434 100644 (file)
@@ -662,6 +662,7 @@ void kvmppc_free_radix(struct kvm *kvm)
                pgd_clear(pgd);
        }
        pgd_free(kvm->mm, kvm->arch.pgtable);
+       kvm->arch.pgtable = NULL;
 }
 
 static void pte_ctor(void *addr)
index 96b92d4adb876d1984a6493ef9a5829188360ef6..040e102de4efe3bef386cee53657762a5384b500 100644 (file)
@@ -2442,6 +2442,9 @@ static void prepare_threads(struct kvmppc_vcore *vc)
        for_each_runnable_thread(i, vcpu, vc) {
                if (signal_pending(vcpu->arch.run_task))
                        vcpu->arch.ret = -EINTR;
+               else if (kvm_is_radix(vc->kvm) != radix_enabled())
+                       /* can't actually run HPT guest on radix host yet... */
+                       vcpu->arch.ret = -EINVAL;
                else if (vcpu->arch.vpa.update_pending ||
                         vcpu->arch.slb_shadow.update_pending ||
                         vcpu->arch.dtl.update_pending)
@@ -3338,13 +3341,6 @@ static int kvm_vm_ioctl_get_smmu_info_hv(struct kvm *kvm,
 {
        struct kvm_ppc_one_seg_page_size *sps;
 
-       /*
-        * Since we don't yet support HPT guests on a radix host,
-        * return an error if the host uses radix.
-        */
-       if (radix_enabled())
-               return -EINVAL;
-
        /*
         * POWER7, POWER8 and POWER9 all support 32 storage keys for data.
         * POWER7 doesn't support keys for instruction accesses,
@@ -3447,15 +3443,6 @@ static void kvmppc_core_free_memslot_hv(struct kvm_memory_slot *free,
 static int kvmppc_core_create_memslot_hv(struct kvm_memory_slot *slot,
                                         unsigned long npages)
 {
-       /*
-        * For now, if radix_enabled() then we only support radix guests,
-        * and in that case we don't need the rmap array.
-        */
-       if (radix_enabled()) {
-               slot->arch.rmap = NULL;
-               return 0;
-       }
-
        slot->arch.rmap = vzalloc(npages * sizeof(*slot->arch.rmap));
        if (!slot->arch.rmap)
                return -ENOMEM;
@@ -3628,6 +3615,34 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
        goto out_srcu;
 }
 
+/* Must be called with kvm->lock held and mmu_ready = 0 and no vcpus running */
+int kvmppc_switch_mmu_to_hpt(struct kvm *kvm)
+{
+       kvmppc_free_radix(kvm);
+       kvmppc_update_lpcr(kvm, LPCR_VPM1,
+                          LPCR_VPM1 | LPCR_UPRT | LPCR_GTSE | LPCR_HR);
+       kvmppc_rmap_reset(kvm);
+       kvm->arch.radix = 0;
+       kvm->arch.process_table = 0;
+       return 0;
+}
+
+/* Must be called with kvm->lock held and mmu_ready = 0 and no vcpus running */
+int kvmppc_switch_mmu_to_radix(struct kvm *kvm)
+{
+       int err;
+
+       err = kvmppc_init_vm_radix(kvm);
+       if (err)
+               return err;
+
+       kvmppc_free_hpt(&kvm->arch.hpt);
+       kvmppc_update_lpcr(kvm, LPCR_UPRT | LPCR_GTSE | LPCR_HR,
+                          LPCR_VPM1 | LPCR_UPRT | LPCR_GTSE | LPCR_HR);
+       kvm->arch.radix = 1;
+       return 0;
+}
+
 #ifdef CONFIG_KVM_XICS
 /*
  * Allocate a per-core structure for managing state about which cores are
@@ -3771,7 +3786,7 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
        }
 
        /*
-        * For now, if the host uses radix, the guest must be radix.
+        * If the host uses radix, the guest starts out as radix.
         */
        if (radix_enabled()) {
                kvm->arch.radix = 1;
@@ -3795,7 +3810,7 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
         * Work out how many sets the TLB has, for the use of
         * the TLB invalidation loop in book3s_hv_rmhandlers.S.
         */
-       if (kvm_is_radix(kvm))
+       if (radix_enabled())
                kvm->arch.tlb_sets = POWER9_TLB_SETS_RADIX;     /* 128 */
        else if (cpu_has_feature(CPU_FTR_ARCH_300))
                kvm->arch.tlb_sets = POWER9_TLB_SETS_HASH;      /* 256 */
@@ -4185,6 +4200,7 @@ static int kvmhv_configure_mmu(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg)
 {
        unsigned long lpcr;
        int radix;
+       int err;
 
        /* If not on a POWER9, reject it */
        if (!cpu_has_feature(CPU_FTR_ARCH_300))
@@ -4194,12 +4210,8 @@ static int kvmhv_configure_mmu(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg)
        if (cfg->flags & ~(KVM_PPC_MMUV3_RADIX | KVM_PPC_MMUV3_GTSE))
                return -EINVAL;
 
-       /* We can't change a guest to/from radix yet */
-       radix = !!(cfg->flags & KVM_PPC_MMUV3_RADIX);
-       if (radix != kvm_is_radix(kvm))
-               return -EINVAL;
-
        /* GR (guest radix) bit in process_table field must match */
+       radix = !!(cfg->flags & KVM_PPC_MMUV3_RADIX);
        if (!!(cfg->process_table & PATB_GR) != radix)
                return -EINVAL;
 
@@ -4207,15 +4219,40 @@ static int kvmhv_configure_mmu(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg)
        if ((cfg->process_table & PRTS_MASK) > 24)
                return -EINVAL;
 
+       /* We can change a guest to/from radix now, if the host is radix */
+       if (radix && !radix_enabled())
+               return -EINVAL;
+
        mutex_lock(&kvm->lock);
+       if (radix != kvm_is_radix(kvm)) {
+               if (kvm->arch.mmu_ready) {
+                       kvm->arch.mmu_ready = 0;
+                       /* order mmu_ready vs. vcpus_running */
+                       smp_mb();
+                       if (atomic_read(&kvm->arch.vcpus_running)) {
+                               kvm->arch.mmu_ready = 1;
+                               err = -EBUSY;
+                               goto out_unlock;
+                       }
+               }
+               if (radix)
+                       err = kvmppc_switch_mmu_to_radix(kvm);
+               else
+                       err = kvmppc_switch_mmu_to_hpt(kvm);
+               if (err)
+                       goto out_unlock;
+       }
+
        kvm->arch.process_table = cfg->process_table;
        kvmppc_setup_partition_table(kvm);
 
        lpcr = (cfg->flags & KVM_PPC_MMUV3_GTSE) ? LPCR_GTSE : 0;
        kvmppc_update_lpcr(kvm, lpcr, LPCR_GTSE);
-       mutex_unlock(&kvm->lock);
+       err = 0;
 
-       return 0;
+ out_unlock:
+       mutex_unlock(&kvm->lock);
+       return err;
 }
 
 static struct kvmppc_ops kvm_ops_hv = {
index a3746b98ec11dd6d244dbfbfd6894a8490fb4042..a0b7f094de78a3dd3366ddc2372f2f0938fb9434 100644 (file)
@@ -590,8 +590,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
                r = !!(hv_enabled && radix_enabled());
                break;
        case KVM_CAP_PPC_MMU_HASH_V3:
-               r = !!(hv_enabled && !radix_enabled() &&
-                      cpu_has_feature(CPU_FTR_ARCH_300));
+               r = !!(hv_enabled && cpu_has_feature(CPU_FTR_ARCH_300));
                break;
 #endif
        case KVM_CAP_SYNC_MMU: