Commit 18c3640c authored by Paul Mackerras's avatar Paul Mackerras

KVM: PPC: Book3S HV: Add infrastructure for running HPT guests on radix host

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: default avatarPaul Mackerras <paulus@ozlabs.org>
parent e641a317
...@@ -168,6 +168,7 @@ extern int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order); ...@@ -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 void kvmppc_set_hpt(struct kvm *kvm, struct kvm_hpt_info *info);
extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order); extern long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order);
extern void kvmppc_free_hpt(struct kvm_hpt_info *info); 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, extern long kvmppc_prepare_vrma(struct kvm *kvm,
struct kvm_userspace_memory_region *mem); struct kvm_userspace_memory_region *mem);
extern void kvmppc_map_vrma(struct kvm_vcpu *vcpu, 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, ...@@ -177,6 +178,8 @@ extern long kvm_spapr_tce_attach_iommu_group(struct kvm *kvm, int tablefd,
struct iommu_group *grp); struct iommu_group *grp);
extern void kvm_spapr_tce_release_iommu_group(struct kvm *kvm, extern void kvm_spapr_tce_release_iommu_group(struct kvm *kvm,
struct iommu_group *grp); 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, extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
struct kvm_create_spapr_tce_64 *args); struct kvm_create_spapr_tce_64 *args);
......
...@@ -73,8 +73,6 @@ struct kvm_resize_hpt { ...@@ -73,8 +73,6 @@ struct kvm_resize_hpt {
struct kvm_hpt_info 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) int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order)
{ {
unsigned long hpt = 0; unsigned long hpt = 0;
...@@ -136,9 +134,6 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order) ...@@ -136,9 +134,6 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order)
long err = -EBUSY; long err = -EBUSY;
struct kvm_hpt_info info; struct kvm_hpt_info info;
if (kvm_is_radix(kvm))
return -EINVAL;
mutex_lock(&kvm->lock); mutex_lock(&kvm->lock);
if (kvm->arch.mmu_ready) { if (kvm->arch.mmu_ready) {
kvm->arch.mmu_ready = 0; kvm->arch.mmu_ready = 0;
...@@ -149,6 +144,12 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order) ...@@ -149,6 +144,12 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order)
goto out; goto out;
} }
} }
if (kvm_is_radix(kvm)) {
err = kvmppc_switch_mmu_to_hpt(kvm);
if (err)
goto out;
}
if (kvm->arch.hpt.order == order) { if (kvm->arch.hpt.order == order) {
/* We already have a suitable HPT */ /* We already have a suitable HPT */
...@@ -182,6 +183,7 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order) ...@@ -182,6 +183,7 @@ long kvmppc_alloc_reset_hpt(struct kvm *kvm, int order)
void kvmppc_free_hpt(struct kvm_hpt_info *info) void kvmppc_free_hpt(struct kvm_hpt_info *info)
{ {
vfree(info->rev); vfree(info->rev);
info->rev = NULL;
if (info->cma) if (info->cma)
kvm_free_hpt_cma(virt_to_page(info->virt), kvm_free_hpt_cma(virt_to_page(info->virt),
1 << (info->order - PAGE_SHIFT)); 1 << (info->order - PAGE_SHIFT));
...@@ -349,6 +351,9 @@ static int kvmppc_mmu_book3s_64_hv_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, ...@@ -349,6 +351,9 @@ static int kvmppc_mmu_book3s_64_hv_xlate(struct kvm_vcpu *vcpu, gva_t eaddr,
int index; int index;
int virtmode = vcpu->arch.shregs.msr & (data ? MSR_DR : MSR_IR); 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 */ /* Get SLB entry */
if (virtmode) { if (virtmode) {
slbe = kvmppc_mmu_book3s_hv_find_slbe(vcpu, eaddr); 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, ...@@ -710,7 +715,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
goto out_put; goto out_put;
} }
static void kvmppc_rmap_reset(struct kvm *kvm) void kvmppc_rmap_reset(struct kvm *kvm)
{ {
struct kvm_memslots *slots; struct kvm_memslots *slots;
struct kvm_memory_slot *memslot; struct kvm_memory_slot *memslot;
...@@ -2089,10 +2094,7 @@ void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu) ...@@ -2089,10 +2094,7 @@ void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu)
vcpu->arch.slb_nr = 32; /* POWER7/POWER8 */ vcpu->arch.slb_nr = 32; /* POWER7/POWER8 */
if (kvm_is_radix(vcpu->kvm)) mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate;
mmu->xlate = kvmppc_mmu_radix_xlate;
else
mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate;
mmu->reset_msr = kvmppc_mmu_book3s_64_hv_reset_msr; mmu->reset_msr = kvmppc_mmu_book3s_64_hv_reset_msr;
vcpu->arch.hflags |= BOOK3S_HFLAG_SLB; vcpu->arch.hflags |= BOOK3S_HFLAG_SLB;
......
...@@ -662,6 +662,7 @@ void kvmppc_free_radix(struct kvm *kvm) ...@@ -662,6 +662,7 @@ void kvmppc_free_radix(struct kvm *kvm)
pgd_clear(pgd); pgd_clear(pgd);
} }
pgd_free(kvm->mm, kvm->arch.pgtable); pgd_free(kvm->mm, kvm->arch.pgtable);
kvm->arch.pgtable = NULL;
} }
static void pte_ctor(void *addr) static void pte_ctor(void *addr)
......
...@@ -2442,6 +2442,9 @@ static void prepare_threads(struct kvmppc_vcore *vc) ...@@ -2442,6 +2442,9 @@ static void prepare_threads(struct kvmppc_vcore *vc)
for_each_runnable_thread(i, vcpu, vc) { for_each_runnable_thread(i, vcpu, vc) {
if (signal_pending(vcpu->arch.run_task)) if (signal_pending(vcpu->arch.run_task))
vcpu->arch.ret = -EINTR; 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 || else if (vcpu->arch.vpa.update_pending ||
vcpu->arch.slb_shadow.update_pending || vcpu->arch.slb_shadow.update_pending ||
vcpu->arch.dtl.update_pending) vcpu->arch.dtl.update_pending)
...@@ -3338,13 +3341,6 @@ static int kvm_vm_ioctl_get_smmu_info_hv(struct kvm *kvm, ...@@ -3338,13 +3341,6 @@ static int kvm_vm_ioctl_get_smmu_info_hv(struct kvm *kvm,
{ {
struct kvm_ppc_one_seg_page_size *sps; 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, POWER8 and POWER9 all support 32 storage keys for data.
* POWER7 doesn't support keys for instruction accesses, * POWER7 doesn't support keys for instruction accesses,
...@@ -3447,15 +3443,6 @@ static void kvmppc_core_free_memslot_hv(struct kvm_memory_slot *free, ...@@ -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, static int kvmppc_core_create_memslot_hv(struct kvm_memory_slot *slot,
unsigned long npages) 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)); slot->arch.rmap = vzalloc(npages * sizeof(*slot->arch.rmap));
if (!slot->arch.rmap) if (!slot->arch.rmap)
return -ENOMEM; return -ENOMEM;
...@@ -3628,6 +3615,34 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu) ...@@ -3628,6 +3615,34 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)
goto out_srcu; 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 #ifdef CONFIG_KVM_XICS
/* /*
* Allocate a per-core structure for managing state about which cores are * 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) ...@@ -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()) { if (radix_enabled()) {
kvm->arch.radix = 1; kvm->arch.radix = 1;
...@@ -3795,7 +3810,7 @@ static int kvmppc_core_init_vm_hv(struct kvm *kvm) ...@@ -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 * Work out how many sets the TLB has, for the use of
* the TLB invalidation loop in book3s_hv_rmhandlers.S. * 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 */ kvm->arch.tlb_sets = POWER9_TLB_SETS_RADIX; /* 128 */
else if (cpu_has_feature(CPU_FTR_ARCH_300)) else if (cpu_has_feature(CPU_FTR_ARCH_300))
kvm->arch.tlb_sets = POWER9_TLB_SETS_HASH; /* 256 */ 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) ...@@ -4185,6 +4200,7 @@ static int kvmhv_configure_mmu(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg)
{ {
unsigned long lpcr; unsigned long lpcr;
int radix; int radix;
int err;
/* If not on a POWER9, reject it */ /* If not on a POWER9, reject it */
if (!cpu_has_feature(CPU_FTR_ARCH_300)) 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) ...@@ -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)) if (cfg->flags & ~(KVM_PPC_MMUV3_RADIX | KVM_PPC_MMUV3_GTSE))
return -EINVAL; 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 */ /* GR (guest radix) bit in process_table field must match */
radix = !!(cfg->flags & KVM_PPC_MMUV3_RADIX);
if (!!(cfg->process_table & PATB_GR) != radix) if (!!(cfg->process_table & PATB_GR) != radix)
return -EINVAL; return -EINVAL;
...@@ -4207,15 +4219,40 @@ static int kvmhv_configure_mmu(struct kvm *kvm, struct kvm_ppc_mmuv3_cfg *cfg) ...@@ -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) if ((cfg->process_table & PRTS_MASK) > 24)
return -EINVAL; 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); 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; kvm->arch.process_table = cfg->process_table;
kvmppc_setup_partition_table(kvm); kvmppc_setup_partition_table(kvm);
lpcr = (cfg->flags & KVM_PPC_MMUV3_GTSE) ? LPCR_GTSE : 0; lpcr = (cfg->flags & KVM_PPC_MMUV3_GTSE) ? LPCR_GTSE : 0;
kvmppc_update_lpcr(kvm, lpcr, LPCR_GTSE); 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 = { static struct kvmppc_ops kvm_ops_hv = {
......
...@@ -590,8 +590,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) ...@@ -590,8 +590,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
r = !!(hv_enabled && radix_enabled()); r = !!(hv_enabled && radix_enabled());
break; break;
case KVM_CAP_PPC_MMU_HASH_V3: case KVM_CAP_PPC_MMU_HASH_V3:
r = !!(hv_enabled && !radix_enabled() && r = !!(hv_enabled && cpu_has_feature(CPU_FTR_ARCH_300));
cpu_has_feature(CPU_FTR_ARCH_300));
break; break;
#endif #endif
case KVM_CAP_SYNC_MMU: case KVM_CAP_SYNC_MMU:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment