KVM: PPC: Book3S HV: Enable radix guest support
authorPaul Mackerras <paulus@ozlabs.org>
Mon, 30 Jan 2017 10:21:53 +0000 (21:21 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 31 Jan 2017 08:11:52 +0000 (19:11 +1100)
This adds a few last pieces of the support for radix guests:

* Implement the backends for the KVM_PPC_CONFIGURE_V3_MMU and
  KVM_PPC_GET_RMMU_INFO ioctls for radix guests

* On POWER9, allow secondary threads to be on/off-lined while guests
  are running.

* Set up LPCR and the partition table entry for radix guests.

* Don't allocate the rmap array in the kvm_memory_slot structure
  on radix.

* Don't try to initialize the HPT for radix guests, since they don't
  have an HPT.

* Take out the code that prevents the HV KVM module from
  initializing on radix hosts.

At this stage, we only support radix guests if the host is running
in radix mode, and only support HPT guests if the host is running in
HPT mode.  Thus a guest cannot switch from one mode to the other,
which enables some simplifications.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/kvm_book3s.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 57dc407cec4aafbfd325a2725432fe4c4dac5a93..2bf35017ffc0e40c0771378eb93160012c77957e 100644 (file)
@@ -189,6 +189,7 @@ extern int kvmppc_book3s_radix_page_fault(struct kvm_run *run,
                        unsigned long ea, unsigned long dsisr);
 extern int kvmppc_mmu_radix_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
                        struct kvmppc_pte *gpte, bool data, bool iswrite);
+extern int kvmppc_init_vm_radix(struct kvm *kvm);
 extern void kvmppc_free_radix(struct kvm *kvm);
 extern int kvmppc_radix_init(void);
 extern void kvmppc_radix_exit(void);
@@ -200,6 +201,7 @@ extern int kvm_test_age_radix(struct kvm *kvm, struct kvm_memory_slot *memslot,
                        unsigned long gfn);
 extern long kvmppc_hv_get_dirty_log_radix(struct kvm *kvm,
                        struct kvm_memory_slot *memslot, unsigned long *map);
+extern int kvmhv_get_rmmu_info(struct kvm *kvm, struct kvm_ppc_rmmu_info *info);
 
 /* XXX remove this export when load_last_inst() is generic */
 extern int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr, bool data);
index c9e587a2849dac3e58a975cd15d05900a15ffffd..9df3d940acec226595fb2f2c4efbdd0fdd015037 100644 (file)
@@ -155,7 +155,6 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
 
 void kvmppc_free_hpt(struct kvm *kvm)
 {
-       kvmppc_free_lpid(kvm->arch.lpid);
        vfree(kvm->arch.revmap);
        if (kvm->arch.hpt_cma_alloc)
                kvm_release_hpt(virt_to_page(kvm->arch.hpt_virt),
index 125cc7ce1525014e468fb2adf2e03a3a82bd0cf1..4344651f408ca24a4149eb042268ec502363c7a4 100644 (file)
@@ -610,6 +610,51 @@ long kvmppc_hv_get_dirty_log_radix(struct kvm *kvm,
        return 0;
 }
 
+static void add_rmmu_ap_encoding(struct kvm_ppc_rmmu_info *info,
+                                int psize, int *indexp)
+{
+       if (!mmu_psize_defs[psize].shift)
+               return;
+       info->ap_encodings[*indexp] = mmu_psize_defs[psize].shift |
+               (mmu_psize_defs[psize].ap << 29);
+       ++(*indexp);
+}
+
+int kvmhv_get_rmmu_info(struct kvm *kvm, struct kvm_ppc_rmmu_info *info)
+{
+       int i;
+
+       if (!radix_enabled())
+               return -EINVAL;
+       memset(info, 0, sizeof(*info));
+
+       /* 4k page size */
+       info->geometries[0].page_shift = 12;
+       info->geometries[0].level_bits[0] = 9;
+       for (i = 1; i < 4; ++i)
+               info->geometries[0].level_bits[i] = p9_supported_radix_bits[i];
+       /* 64k page size */
+       info->geometries[1].page_shift = 16;
+       for (i = 0; i < 4; ++i)
+               info->geometries[1].level_bits[i] = p9_supported_radix_bits[i];
+
+       i = 0;
+       add_rmmu_ap_encoding(info, MMU_PAGE_4K, &i);
+       add_rmmu_ap_encoding(info, MMU_PAGE_64K, &i);
+       add_rmmu_ap_encoding(info, MMU_PAGE_2M, &i);
+       add_rmmu_ap_encoding(info, MMU_PAGE_1G, &i);
+
+       return 0;
+}
+
+int kvmppc_init_vm_radix(struct kvm *kvm)
+{
+       kvm->arch.pgtable = pgd_alloc(kvm->mm);
+       if (!kvm->arch.pgtable)
+               return -ENOMEM;
+       return 0;
+}
+
 void kvmppc_free_radix(struct kvm *kvm)
 {
        unsigned long ig, iu, im;
index 50c230e83f9b6e91cd171a01bae0a9771da5ecf6..e4a79679342ee4c6b1db1dba2a4dee19b02cddc0 100644 (file)
@@ -1135,7 +1135,7 @@ static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 new_lpcr,
        /*
         * Userspace can only modify DPFD (default prefetch depth),
         * ILE (interrupt little-endian) and TC (translation control).
-        * On POWER8 userspace can also modify AIL (alt. interrupt loc.)
+        * On POWER8 and POWER9 userspace can also modify AIL (alt. interrupt loc.).
         */
        mask = LPCR_DPFD | LPCR_ILE | LPCR_TC;
        if (cpu_has_feature(CPU_FTR_ARCH_207S))
@@ -2922,7 +2922,7 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu)
        smp_mb();
 
        /* On the first time here, set up HTAB and VRMA */
-       if (!vcpu->kvm->arch.hpte_setup_done) {
+       if (!kvm_is_radix(vcpu->kvm) && !vcpu->kvm->arch.hpte_setup_done) {
                r = kvmppc_hv_setup_htab_rma(vcpu);
                if (r)
                        goto out;
@@ -2984,6 +2984,13 @@ 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;
+
        info->flags = KVM_PPC_PAGE_SIZES_REAL;
        if (mmu_has_feature(MMU_FTR_1T_SEGMENT))
                info->flags |= KVM_PPC_1T_SEGMENTS;
@@ -3069,6 +3076,15 @@ 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;
@@ -3149,14 +3165,20 @@ static void kvmppc_setup_partition_table(struct kvm *kvm)
 {
        unsigned long dw0, dw1;
 
-       /* PS field - page size for VRMA */
-       dw0 = ((kvm->arch.vrma_slb_v & SLB_VSID_L) >> 1) |
-               ((kvm->arch.vrma_slb_v & SLB_VSID_LP) << 1);
-       /* HTABSIZE and HTABORG fields */
-       dw0 |= kvm->arch.sdr1;
+       if (!kvm_is_radix(kvm)) {
+               /* PS field - page size for VRMA */
+               dw0 = ((kvm->arch.vrma_slb_v & SLB_VSID_L) >> 1) |
+                       ((kvm->arch.vrma_slb_v & SLB_VSID_LP) << 1);
+               /* HTABSIZE and HTABORG fields */
+               dw0 |= kvm->arch.sdr1;
 
-       /* Second dword as set by userspace */
-       dw1 = kvm->arch.process_table;
+               /* Second dword as set by userspace */
+               dw1 = kvm->arch.process_table;
+       } else {
+               dw0 = PATB_HR | radix__get_tree_size() |
+                       __pa(kvm->arch.pgtable) | RADIX_PGD_INDEX_SIZE;
+               dw1 = PATB_GR | kvm->arch.process_table;
+       }
 
        mmu_partition_table_set_entry(kvm->arch.lpid, dw0, dw1);
 }
@@ -3326,6 +3348,7 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
 {
        unsigned long lpcr, lpid;
        char buf[32];
+       int ret;
 
        /* Allocate the guest's logical partition ID */
 
@@ -3373,13 +3396,30 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
                lpcr |= LPCR_HVICE;
        }
 
+       /*
+        * For now, if the host uses radix, the guest must be radix.
+        */
+       if (radix_enabled()) {
+               kvm->arch.radix = 1;
+               lpcr &= ~LPCR_VPM1;
+               lpcr |= LPCR_UPRT | LPCR_GTSE | LPCR_HR;
+               ret = kvmppc_init_vm_radix(kvm);
+               if (ret) {
+                       kvmppc_free_lpid(kvm->arch.lpid);
+                       return ret;
+               }
+               kvmppc_setup_partition_table(kvm);
+       }
+
        kvm->arch.lpcr = lpcr;
 
        /*
         * Work out how many sets the TLB has, for the use of
         * the TLB invalidation loop in book3s_hv_rmhandlers.S.
         */
-       if (cpu_has_feature(CPU_FTR_ARCH_300))
+       if (kvm_is_radix(kvm))
+               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 */
        else if (cpu_has_feature(CPU_FTR_ARCH_207S))
                kvm->arch.tlb_sets = POWER8_TLB_SETS;           /* 512 */
@@ -3389,8 +3429,11 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm)
        /*
         * Track that we now have a HV mode VM active. This blocks secondary
         * CPU threads from coming online.
+        * On POWER9, we only need to do this for HPT guests on a radix
+        * host, which is not yet supported.
         */
-       kvm_hv_vm_activated();
+       if (!cpu_has_feature(CPU_FTR_ARCH_300))
+               kvm_hv_vm_activated();
 
        /*
         * Create a debugfs directory for the VM
@@ -3416,10 +3459,13 @@ static void kvmppc_core_destroy_vm_hv(struct kvm *kvm)
 {
        debugfs_remove_recursive(kvm->arch.debugfs_dir);
 
-       kvm_hv_vm_deactivated();
+       if (!cpu_has_feature(CPU_FTR_ARCH_300))
+               kvm_hv_vm_deactivated();
 
        kvmppc_free_vcores(kvm);
 
+       kvmppc_free_lpid(kvm->arch.lpid);
+
        if (kvm_is_radix(kvm))
                kvmppc_free_radix(kvm);
        else
@@ -3452,11 +3498,6 @@ static int kvmppc_core_check_processor_compat_hv(void)
        if (!cpu_has_feature(CPU_FTR_HVMODE) ||
            !cpu_has_feature(CPU_FTR_ARCH_206))
                return -EIO;
-       /*
-        * Disable KVM for Power9 in radix mode.
-        */
-       if (cpu_has_feature(CPU_FTR_ARCH_300) && radix_enabled())
-               return -EIO;
 
        return 0;
 }
@@ -3727,6 +3768,7 @@ static void init_default_hcalls(void)
 static int kvmhv_configure_mmu(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg)
 {
        unsigned long lpcr;
+       int radix;
 
        /* If not on a POWER9, reject it */
        if (!cpu_has_feature(CPU_FTR_ARCH_300))
@@ -3736,12 +3778,13 @@ 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 do radix yet */
-       if (cfg->flags & KVM_PPC_MMUV3_RADIX)
+       /* 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 */
-       if (cfg->process_table & PATB_GR)
+       if (!!(cfg->process_table & PATB_GR) != radix)
                return -EINVAL;
 
        /* Process table size field must be reasonable, i.e. <= 24 */
@@ -3757,11 +3800,6 @@ static int kvmhv_configure_mmu(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg)
        return 0;
 }
 
-static int kvmhv_get_rmmu_info(struct kvm *kvm, struct kvm_ppc_rmmu_info *info)
-{
-       return -EINVAL;
-}
-
 static struct kvmppc_ops kvm_ops_hv = {
        .get_sregs = kvm_arch_vcpu_ioctl_get_sregs_hv,
        .set_sregs = kvm_arch_vcpu_ioctl_set_sregs_hv,
index 1476a480745e19af84f5e203fb04a1c1cd2ac86b..40a5b2d75ed19979aa0b52246d1c7951ecf0a367 100644 (file)
@@ -566,7 +566,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
                r = kvmppc_hwrng_present();
                break;
        case KVM_CAP_PPC_MMU_RADIX:
-               r = !!(0 && hv_enabled && radix_enabled());
+               r = !!(hv_enabled && radix_enabled());
                break;
        case KVM_CAP_PPC_MMU_HASH_V3:
                r = !!(hv_enabled && !radix_enabled() &&