diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2017-07-12 17:56:55 +1000 |
---|---|---|
committer | David Gibson <david@gibson.dropbear.id.au> | 2017-07-17 15:07:05 +1000 |
commit | b55d295e3ec98e46f5b39d50e4a3a9725b4289b3 (patch) | |
tree | a9fadf3a3abae4c1e018f3b3aaca9ca9ceea59fc /target/ppc | |
parent | 2772cf6be90e39919d0557ba3c57a77313ca9edf (diff) |
pseries: Allow HPT resizing with KVM
So far, qemu implements the PAPR Hash Page Table (HPT) resizing extension
with TCG. The same implementation will work with KVM PR, but we don't
currently allow that. For KVM HV we can only implement resizing with the
assistance of the host kernel, which needs a new capability and ioctl()s.
This patch adds support for testing the new KVM capability and implementing
the resize in terms of KVM facilities when necessary. If we're running on
a kernel which doesn't have the new capability flag at all, we fall back to
testing for PR vs. HV KVM using the same hack that we already use in a
number of places for older kernels.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Diffstat (limited to 'target/ppc')
-rw-r--r-- | target/ppc/kvm.c | 67 | ||||
-rw-r--r-- | target/ppc/kvm_ppc.h | 21 |
2 files changed, 86 insertions, 2 deletions
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 8bafd1e932..85713795de 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -89,6 +89,7 @@ static int cap_fixup_hcalls; static int cap_htm; /* Hardware transactional memory support */ static int cap_mmu_radix; static int cap_mmu_hash_v3; +static int cap_resize_hpt; static uint32_t debug_inst_opcode; @@ -145,6 +146,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM); cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX); cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3); + cap_resize_hpt = kvm_vm_check_extension(s, KVM_CAP_SPAPR_RESIZE_HPT); if (!cap_interrupt_level) { fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " @@ -2714,11 +2716,72 @@ int kvmppc_enable_hwrng(void) void kvmppc_check_papr_resize_hpt(Error **errp) { if (!kvm_enabled()) { - return; + return; /* No KVM, we're good */ + } + + if (cap_resize_hpt) { + return; /* Kernel has explicit support, we're good */ } - /* TODO: Check for resize-capable KVM implementations */ + /* Otherwise fallback on looking for PR KVM */ + if (kvmppc_is_pr(kvm_state)) { + return; + } error_setg(errp, "Hash page table resizing not available with this KVM version"); } + +int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, target_ulong flags, int shift) +{ + CPUState *cs = CPU(cpu); + struct kvm_ppc_resize_hpt rhpt = { + .flags = flags, + .shift = shift, + }; + + if (!cap_resize_hpt) { + return -ENOSYS; + } + + return kvm_vm_ioctl(cs->kvm_state, KVM_PPC_RESIZE_HPT_PREPARE, &rhpt); +} + +int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift) +{ + CPUState *cs = CPU(cpu); + struct kvm_ppc_resize_hpt rhpt = { + .flags = flags, + .shift = shift, + }; + + if (!cap_resize_hpt) { + return -ENOSYS; + } + + return kvm_vm_ioctl(cs->kvm_state, KVM_PPC_RESIZE_HPT_COMMIT, &rhpt); +} + +static void kvmppc_pivot_hpt_cpu(CPUState *cs, run_on_cpu_data arg) +{ + target_ulong sdr1 = arg.target_ptr; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* This is just for the benefit of PR KVM */ + cpu_synchronize_state(cs); + env->spr[SPR_SDR1] = sdr1; + if (kvmppc_put_books_sregs(cpu) < 0) { + error_report("Unable to update SDR1 in KVM"); + exit(1); + } +} + +void kvmppc_update_sdr1(target_ulong sdr1) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + run_on_cpu(cs, kvmppc_pivot_hpt_cpu, RUN_ON_CPU_TARGET_PTR(sdr1)); + } +} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 9be706c5c9..6bc6fb3e2d 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -64,6 +64,9 @@ int kvmppc_enable_hwrng(void); int kvmppc_put_books_sregs(PowerPCCPU *cpu); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); void kvmppc_check_papr_resize_hpt(Error **errp); +int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, target_ulong flags, int shift); +int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift); +void kvmppc_update_sdr1(target_ulong sdr1); bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path); @@ -302,6 +305,24 @@ static inline void kvmppc_check_papr_resize_hpt(Error **errp) { return; } + +static inline int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, + target_ulong flags, int shift) +{ + return -ENOSYS; +} + +static inline int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, + target_ulong flags, int shift) +{ + return -ENOSYS; +} + +static inline void kvmppc_update_sdr1(target_ulong sdr1) +{ + abort(); +} + #endif #ifndef CONFIG_KVM |