mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
x86:
* Fixes for the new scalable MMU * Fixes for migration of nested hypervisors on AMD * Fix for clang integrated assembler * Fix for left shift by 64 (UBSAN) * Small cleanups * Straggler SEV-ES patch ARM: * VM init cleanups * PSCI relay cleanups * Kill CONFIG_KVM_ARM_PMU * Fixup __init annotations * Fixup reg_to_encoding() * Fix spurious PMCR_EL0 access * selftests cleanups -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAl/4YOMUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroMg0Qf/eGDOoLL18KhA9MpoPXusR5kU+82G AcbFj9siNW46EF7mL+sw/xAx+gZaqSpIEmn/f6BzgiaUBdFTv9CKX3B54e43e59G HAKD0NpqwvDIi1b0T6bcgC92mY3Qx/IDCc7/9JYjBs/iORfqyWW6xVtkF/Gfymxt eK+MnfMqqNZODgR/cZnCH1E48fuwOvRMxLqilLi3OOMSUfs2cQOSLTNfYQYqjeaJ dsQ4YeyPJO5JHtfHFr6VPIo/jDhowniac9CNvOomWWVIx2zPYVSl9d8ub6ESEPNF GM7UeBCOMmZG/a3qFEZPAUJ7znW4yYE85Z6pjxlrGhd1I54MJi4dd+RApw== =5Nfj -----END PGP SIGNATURE----- Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm Pull kvm fixes from Paolo Bonzini: "x86: - Fixes for the new scalable MMU - Fixes for migration of nested hypervisors on AMD - Fix for clang integrated assembler - Fix for left shift by 64 (UBSAN) - Small cleanups - Straggler SEV-ES patch ARM: - VM init cleanups - PSCI relay cleanups - Kill CONFIG_KVM_ARM_PMU - Fixup __init annotations - Fixup reg_to_encoding() - Fix spurious PMCR_EL0 access Misc: - selftests cleanups" * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (38 commits) KVM: x86: __kvm_vcpu_halt can be static KVM: SVM: Add support for booting APs in an SEV-ES guest KVM: nSVM: cancel KVM_REQ_GET_NESTED_STATE_PAGES on nested vmexit KVM: nSVM: mark vmcb as dirty when forcingly leaving the guest mode KVM: nSVM: correctly restore nested_run_pending on migration KVM: x86/mmu: Clarify TDP MMU page list invariants KVM: x86/mmu: Ensure TDP MMU roots are freed after yield kvm: check tlbs_dirty directly KVM: x86: change in pv_eoi_get_pending() to make code more readable MAINTAINERS: Really update email address for Sean Christopherson KVM: x86: fix shift out of bounds reported by UBSAN KVM: selftests: Implement perf_test_util more conventionally KVM: selftests: Use vm_create_with_vcpus in create_vm KVM: selftests: Factor out guest mode code KVM/SVM: Remove leftover __svm_vcpu_run prototype from svm.c KVM: SVM: Add register operand to vmsave call in sev_es_vcpu_load KVM: x86/mmu: Optimize not-present/MMIO SPTE check in get_mmio_spte() KVM: x86/mmu: Use raw level to index into MMIO walks' sptes array KVM: x86/mmu: Get root level from walkers when retrieving MMIO SPTE KVM: x86/mmu: Use -1 to flag an undefined spte in get_mmio_spte() ...
This commit is contained in:
commit
2a190b22aa
@ -392,9 +392,14 @@ This ioctl is obsolete and has been removed.
|
|||||||
|
|
||||||
Errors:
|
Errors:
|
||||||
|
|
||||||
===== =============================
|
======= ==============================================================
|
||||||
EINTR an unmasked signal is pending
|
EINTR an unmasked signal is pending
|
||||||
===== =============================
|
ENOEXEC the vcpu hasn't been initialized or the guest tried to execute
|
||||||
|
instructions from device memory (arm64)
|
||||||
|
ENOSYS data abort outside memslots with no syndrome info and
|
||||||
|
KVM_CAP_ARM_NISV_TO_USER not enabled (arm64)
|
||||||
|
EPERM SVE feature set but not finalized (arm64)
|
||||||
|
======= ==============================================================
|
||||||
|
|
||||||
This ioctl is used to run a guest virtual cpu. While there are no
|
This ioctl is used to run a guest virtual cpu. While there are no
|
||||||
explicit parameters, there is an implicit parameter block that can be
|
explicit parameters, there is an implicit parameter block that can be
|
||||||
|
@ -9776,7 +9776,7 @@ F: tools/testing/selftests/kvm/s390x/
|
|||||||
|
|
||||||
KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86)
|
KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86)
|
||||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
R: Sean Christopherson <sean.j.christopherson@intel.com>
|
R: Sean Christopherson <seanjc@google.com>
|
||||||
R: Vitaly Kuznetsov <vkuznets@redhat.com>
|
R: Vitaly Kuznetsov <vkuznets@redhat.com>
|
||||||
R: Wanpeng Li <wanpengli@tencent.com>
|
R: Wanpeng Li <wanpengli@tencent.com>
|
||||||
R: Jim Mattson <jmattson@google.com>
|
R: Jim Mattson <jmattson@google.com>
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/jump_label.h>
|
#include <linux/jump_label.h>
|
||||||
#include <linux/kvm_types.h>
|
#include <linux/kvm_types.h>
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
|
#include <linux/psci.h>
|
||||||
#include <asm/arch_gicv3.h>
|
#include <asm/arch_gicv3.h>
|
||||||
#include <asm/barrier.h>
|
#include <asm/barrier.h>
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
@ -240,6 +241,28 @@ struct kvm_host_data {
|
|||||||
struct kvm_pmu_events pmu_events;
|
struct kvm_pmu_events pmu_events;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct kvm_host_psci_config {
|
||||||
|
/* PSCI version used by host. */
|
||||||
|
u32 version;
|
||||||
|
|
||||||
|
/* Function IDs used by host if version is v0.1. */
|
||||||
|
struct psci_0_1_function_ids function_ids_0_1;
|
||||||
|
|
||||||
|
bool psci_0_1_cpu_suspend_implemented;
|
||||||
|
bool psci_0_1_cpu_on_implemented;
|
||||||
|
bool psci_0_1_cpu_off_implemented;
|
||||||
|
bool psci_0_1_migrate_implemented;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct kvm_host_psci_config kvm_nvhe_sym(kvm_host_psci_config);
|
||||||
|
#define kvm_host_psci_config CHOOSE_NVHE_SYM(kvm_host_psci_config)
|
||||||
|
|
||||||
|
extern s64 kvm_nvhe_sym(hyp_physvirt_offset);
|
||||||
|
#define hyp_physvirt_offset CHOOSE_NVHE_SYM(hyp_physvirt_offset)
|
||||||
|
|
||||||
|
extern u64 kvm_nvhe_sym(hyp_cpu_logical_map)[NR_CPUS];
|
||||||
|
#define hyp_cpu_logical_map CHOOSE_NVHE_SYM(hyp_cpu_logical_map)
|
||||||
|
|
||||||
struct vcpu_reset_state {
|
struct vcpu_reset_state {
|
||||||
unsigned long pc;
|
unsigned long pc;
|
||||||
unsigned long r0;
|
unsigned long r0;
|
||||||
|
@ -2568,7 +2568,7 @@ static void verify_hyp_capabilities(void)
|
|||||||
int parange, ipa_max;
|
int parange, ipa_max;
|
||||||
unsigned int safe_vmid_bits, vmid_bits;
|
unsigned int safe_vmid_bits, vmid_bits;
|
||||||
|
|
||||||
if (!IS_ENABLED(CONFIG_KVM) || !IS_ENABLED(CONFIG_KVM_ARM_HOST))
|
if (!IS_ENABLED(CONFIG_KVM))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
safe_mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
safe_mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
|
||||||
|
@ -434,7 +434,7 @@ static void __init hyp_mode_check(void)
|
|||||||
"CPU: CPUs started in inconsistent modes");
|
"CPU: CPUs started in inconsistent modes");
|
||||||
else
|
else
|
||||||
pr_info("CPU: All CPU(s) started at EL1\n");
|
pr_info("CPU: All CPU(s) started at EL1\n");
|
||||||
if (IS_ENABLED(CONFIG_KVM))
|
if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
|
||||||
kvm_compute_layout();
|
kvm_compute_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,14 +49,6 @@ if KVM
|
|||||||
|
|
||||||
source "virt/kvm/Kconfig"
|
source "virt/kvm/Kconfig"
|
||||||
|
|
||||||
config KVM_ARM_PMU
|
|
||||||
bool "Virtual Performance Monitoring Unit (PMU) support"
|
|
||||||
depends on HW_PERF_EVENTS
|
|
||||||
default y
|
|
||||||
help
|
|
||||||
Adds support for a virtual Performance Monitoring Unit (PMU) in
|
|
||||||
virtual machines.
|
|
||||||
|
|
||||||
endif # KVM
|
endif # KVM
|
||||||
|
|
||||||
endif # VIRTUALIZATION
|
endif # VIRTUALIZATION
|
||||||
|
@ -24,4 +24,4 @@ kvm-y := $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o \
|
|||||||
vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
|
vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
|
||||||
vgic/vgic-its.o vgic/vgic-debug.o
|
vgic/vgic-its.o vgic/vgic-debug.o
|
||||||
|
|
||||||
kvm-$(CONFIG_KVM_ARM_PMU) += pmu-emul.o
|
kvm-$(CONFIG_HW_PERF_EVENTS) += pmu-emul.o
|
||||||
|
@ -1129,9 +1129,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
|
|||||||
if (!irqchip_in_kernel(vcpu->kvm))
|
if (!irqchip_in_kernel(vcpu->kvm))
|
||||||
goto no_vgic;
|
goto no_vgic;
|
||||||
|
|
||||||
if (!vgic_initialized(vcpu->kvm))
|
/*
|
||||||
return -ENODEV;
|
* At this stage, we have the guarantee that the vgic is both
|
||||||
|
* available and initialized.
|
||||||
|
*/
|
||||||
if (!timer_irqs_are_valid(vcpu)) {
|
if (!timer_irqs_are_valid(vcpu)) {
|
||||||
kvm_debug("incorrectly configured timer irqs\n");
|
kvm_debug("incorrectly configured timer irqs\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -65,10 +65,6 @@ static bool vgic_present;
|
|||||||
static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
|
static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
|
||||||
DEFINE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
|
DEFINE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
|
||||||
|
|
||||||
extern u64 kvm_nvhe_sym(__cpu_logical_map)[NR_CPUS];
|
|
||||||
extern u32 kvm_nvhe_sym(kvm_host_psci_version);
|
|
||||||
extern struct psci_0_1_function_ids kvm_nvhe_sym(kvm_host_psci_0_1_function_ids);
|
|
||||||
|
|
||||||
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
|
int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
|
return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
|
||||||
@ -584,11 +580,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
|
|||||||
* Map the VGIC hardware resources before running a vcpu the
|
* Map the VGIC hardware resources before running a vcpu the
|
||||||
* first time on this VM.
|
* first time on this VM.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!vgic_ready(kvm))) {
|
ret = kvm_vgic_map_resources(kvm);
|
||||||
ret = kvm_vgic_map_resources(kvm);
|
if (ret)
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Tell the rest of the code that there are userspace irqchip
|
* Tell the rest of the code that there are userspace irqchip
|
||||||
@ -1574,12 +1568,12 @@ static struct notifier_block hyp_init_cpu_pm_nb = {
|
|||||||
.notifier_call = hyp_init_cpu_pm_notifier,
|
.notifier_call = hyp_init_cpu_pm_notifier,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __init hyp_cpu_pm_init(void)
|
static void hyp_cpu_pm_init(void)
|
||||||
{
|
{
|
||||||
if (!is_protected_kvm_enabled())
|
if (!is_protected_kvm_enabled())
|
||||||
cpu_pm_register_notifier(&hyp_init_cpu_pm_nb);
|
cpu_pm_register_notifier(&hyp_init_cpu_pm_nb);
|
||||||
}
|
}
|
||||||
static void __init hyp_cpu_pm_exit(void)
|
static void hyp_cpu_pm_exit(void)
|
||||||
{
|
{
|
||||||
if (!is_protected_kvm_enabled())
|
if (!is_protected_kvm_enabled())
|
||||||
cpu_pm_unregister_notifier(&hyp_init_cpu_pm_nb);
|
cpu_pm_unregister_notifier(&hyp_init_cpu_pm_nb);
|
||||||
@ -1604,9 +1598,12 @@ static void init_cpu_logical_map(void)
|
|||||||
* allow any other CPUs from the `possible` set to boot.
|
* allow any other CPUs from the `possible` set to boot.
|
||||||
*/
|
*/
|
||||||
for_each_online_cpu(cpu)
|
for_each_online_cpu(cpu)
|
||||||
kvm_nvhe_sym(__cpu_logical_map)[cpu] = cpu_logical_map(cpu);
|
hyp_cpu_logical_map[cpu] = cpu_logical_map(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define init_psci_0_1_impl_state(config, what) \
|
||||||
|
config.psci_0_1_ ## what ## _implemented = psci_ops.what
|
||||||
|
|
||||||
static bool init_psci_relay(void)
|
static bool init_psci_relay(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1618,8 +1615,15 @@ static bool init_psci_relay(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_nvhe_sym(kvm_host_psci_version) = psci_ops.get_version();
|
kvm_host_psci_config.version = psci_ops.get_version();
|
||||||
kvm_nvhe_sym(kvm_host_psci_0_1_function_ids) = get_psci_0_1_function_ids();
|
|
||||||
|
if (kvm_host_psci_config.version == PSCI_VERSION(0, 1)) {
|
||||||
|
kvm_host_psci_config.function_ids_0_1 = get_psci_0_1_function_ids();
|
||||||
|
init_psci_0_1_impl_state(kvm_host_psci_config, cpu_suspend);
|
||||||
|
init_psci_0_1_impl_state(kvm_host_psci_config, cpu_on);
|
||||||
|
init_psci_0_1_impl_state(kvm_host_psci_config, cpu_off);
|
||||||
|
init_psci_0_1_impl_state(kvm_host_psci_config, migrate);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,4 +59,13 @@ static inline void __adjust_pc(struct kvm_vcpu *vcpu)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip an instruction while host sysregs are live.
|
||||||
|
* Assumes host is always 64-bit.
|
||||||
|
*/
|
||||||
|
static inline void kvm_skip_host_instr(void)
|
||||||
|
{
|
||||||
|
write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -157,11 +157,6 @@ static void default_host_smc_handler(struct kvm_cpu_context *host_ctxt)
|
|||||||
__kvm_hyp_host_forward_smc(host_ctxt);
|
__kvm_hyp_host_forward_smc(host_ctxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void skip_host_instruction(void)
|
|
||||||
{
|
|
||||||
write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
|
static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
|
||||||
{
|
{
|
||||||
bool handled;
|
bool handled;
|
||||||
@ -170,11 +165,8 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
|
|||||||
if (!handled)
|
if (!handled)
|
||||||
default_host_smc_handler(host_ctxt);
|
default_host_smc_handler(host_ctxt);
|
||||||
|
|
||||||
/*
|
/* SMC was trapped, move ELR past the current PC. */
|
||||||
* Unlike HVC, the return address of an SMC is the instruction's PC.
|
kvm_skip_host_instr();
|
||||||
* Move the return address past the instruction.
|
|
||||||
*/
|
|
||||||
skip_host_instruction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_trap(struct kvm_cpu_context *host_ctxt)
|
void handle_trap(struct kvm_cpu_context *host_ctxt)
|
||||||
|
@ -14,14 +14,14 @@
|
|||||||
* Other CPUs should not be allowed to boot because their features were
|
* Other CPUs should not be allowed to boot because their features were
|
||||||
* not checked against the finalized system capabilities.
|
* not checked against the finalized system capabilities.
|
||||||
*/
|
*/
|
||||||
u64 __ro_after_init __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
|
u64 __ro_after_init hyp_cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
|
||||||
|
|
||||||
u64 cpu_logical_map(unsigned int cpu)
|
u64 cpu_logical_map(unsigned int cpu)
|
||||||
{
|
{
|
||||||
if (cpu >= ARRAY_SIZE(__cpu_logical_map))
|
if (cpu >= ARRAY_SIZE(hyp_cpu_logical_map))
|
||||||
hyp_panic();
|
hyp_panic();
|
||||||
|
|
||||||
return __cpu_logical_map[cpu];
|
return hyp_cpu_logical_map[cpu];
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long __hyp_per_cpu_offset(unsigned int cpu)
|
unsigned long __hyp_per_cpu_offset(unsigned int cpu)
|
||||||
|
@ -7,11 +7,8 @@
|
|||||||
#include <asm/kvm_asm.h>
|
#include <asm/kvm_asm.h>
|
||||||
#include <asm/kvm_hyp.h>
|
#include <asm/kvm_hyp.h>
|
||||||
#include <asm/kvm_mmu.h>
|
#include <asm/kvm_mmu.h>
|
||||||
#include <kvm/arm_hypercalls.h>
|
|
||||||
#include <linux/arm-smccc.h>
|
#include <linux/arm-smccc.h>
|
||||||
#include <linux/kvm_host.h>
|
#include <linux/kvm_host.h>
|
||||||
#include <linux/psci.h>
|
|
||||||
#include <kvm/arm_psci.h>
|
|
||||||
#include <uapi/linux/psci.h>
|
#include <uapi/linux/psci.h>
|
||||||
|
|
||||||
#include <nvhe/trap_handler.h>
|
#include <nvhe/trap_handler.h>
|
||||||
@ -22,9 +19,8 @@ void kvm_hyp_cpu_resume(unsigned long r0);
|
|||||||
void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt);
|
void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt);
|
||||||
|
|
||||||
/* Config options set by the host. */
|
/* Config options set by the host. */
|
||||||
__ro_after_init u32 kvm_host_psci_version;
|
struct kvm_host_psci_config __ro_after_init kvm_host_psci_config;
|
||||||
__ro_after_init struct psci_0_1_function_ids kvm_host_psci_0_1_function_ids;
|
s64 __ro_after_init hyp_physvirt_offset;
|
||||||
__ro_after_init s64 hyp_physvirt_offset;
|
|
||||||
|
|
||||||
#define __hyp_pa(x) ((phys_addr_t)((x)) + hyp_physvirt_offset)
|
#define __hyp_pa(x) ((phys_addr_t)((x)) + hyp_physvirt_offset)
|
||||||
|
|
||||||
@ -47,19 +43,16 @@ struct psci_boot_args {
|
|||||||
static DEFINE_PER_CPU(struct psci_boot_args, cpu_on_args) = PSCI_BOOT_ARGS_INIT;
|
static DEFINE_PER_CPU(struct psci_boot_args, cpu_on_args) = PSCI_BOOT_ARGS_INIT;
|
||||||
static DEFINE_PER_CPU(struct psci_boot_args, suspend_args) = PSCI_BOOT_ARGS_INIT;
|
static DEFINE_PER_CPU(struct psci_boot_args, suspend_args) = PSCI_BOOT_ARGS_INIT;
|
||||||
|
|
||||||
static u64 get_psci_func_id(struct kvm_cpu_context *host_ctxt)
|
#define is_psci_0_1(what, func_id) \
|
||||||
{
|
(kvm_host_psci_config.psci_0_1_ ## what ## _implemented && \
|
||||||
DECLARE_REG(u64, func_id, host_ctxt, 0);
|
(func_id) == kvm_host_psci_config.function_ids_0_1.what)
|
||||||
|
|
||||||
return func_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_psci_0_1_call(u64 func_id)
|
static bool is_psci_0_1_call(u64 func_id)
|
||||||
{
|
{
|
||||||
return (func_id == kvm_host_psci_0_1_function_ids.cpu_suspend) ||
|
return (is_psci_0_1(cpu_suspend, func_id) ||
|
||||||
(func_id == kvm_host_psci_0_1_function_ids.cpu_on) ||
|
is_psci_0_1(cpu_on, func_id) ||
|
||||||
(func_id == kvm_host_psci_0_1_function_ids.cpu_off) ||
|
is_psci_0_1(cpu_off, func_id) ||
|
||||||
(func_id == kvm_host_psci_0_1_function_ids.migrate);
|
is_psci_0_1(migrate, func_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_psci_0_2_call(u64 func_id)
|
static bool is_psci_0_2_call(u64 func_id)
|
||||||
@ -69,16 +62,6 @@ static bool is_psci_0_2_call(u64 func_id)
|
|||||||
(PSCI_0_2_FN64(0) <= func_id && func_id <= PSCI_0_2_FN64(31));
|
(PSCI_0_2_FN64(0) <= func_id && func_id <= PSCI_0_2_FN64(31));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_psci_call(u64 func_id)
|
|
||||||
{
|
|
||||||
switch (kvm_host_psci_version) {
|
|
||||||
case PSCI_VERSION(0, 1):
|
|
||||||
return is_psci_0_1_call(func_id);
|
|
||||||
default:
|
|
||||||
return is_psci_0_2_call(func_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long psci_call(unsigned long fn, unsigned long arg0,
|
static unsigned long psci_call(unsigned long fn, unsigned long arg0,
|
||||||
unsigned long arg1, unsigned long arg2)
|
unsigned long arg1, unsigned long arg2)
|
||||||
{
|
{
|
||||||
@ -248,15 +231,14 @@ asmlinkage void __noreturn kvm_host_psci_cpu_entry(bool is_cpu_on)
|
|||||||
|
|
||||||
static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context *host_ctxt)
|
static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context *host_ctxt)
|
||||||
{
|
{
|
||||||
if ((func_id == kvm_host_psci_0_1_function_ids.cpu_off) ||
|
if (is_psci_0_1(cpu_off, func_id) || is_psci_0_1(migrate, func_id))
|
||||||
(func_id == kvm_host_psci_0_1_function_ids.migrate))
|
|
||||||
return psci_forward(host_ctxt);
|
return psci_forward(host_ctxt);
|
||||||
else if (func_id == kvm_host_psci_0_1_function_ids.cpu_on)
|
if (is_psci_0_1(cpu_on, func_id))
|
||||||
return psci_cpu_on(func_id, host_ctxt);
|
return psci_cpu_on(func_id, host_ctxt);
|
||||||
else if (func_id == kvm_host_psci_0_1_function_ids.cpu_suspend)
|
if (is_psci_0_1(cpu_suspend, func_id))
|
||||||
return psci_cpu_suspend(func_id, host_ctxt);
|
return psci_cpu_suspend(func_id, host_ctxt);
|
||||||
else
|
|
||||||
return PSCI_RET_NOT_SUPPORTED;
|
return PSCI_RET_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long psci_0_2_handler(u64 func_id, struct kvm_cpu_context *host_ctxt)
|
static unsigned long psci_0_2_handler(u64 func_id, struct kvm_cpu_context *host_ctxt)
|
||||||
@ -298,20 +280,23 @@ static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context *host_
|
|||||||
|
|
||||||
bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt)
|
bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt)
|
||||||
{
|
{
|
||||||
u64 func_id = get_psci_func_id(host_ctxt);
|
DECLARE_REG(u64, func_id, host_ctxt, 0);
|
||||||
unsigned long ret;
|
unsigned long ret;
|
||||||
|
|
||||||
if (!is_psci_call(func_id))
|
switch (kvm_host_psci_config.version) {
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (kvm_host_psci_version) {
|
|
||||||
case PSCI_VERSION(0, 1):
|
case PSCI_VERSION(0, 1):
|
||||||
|
if (!is_psci_0_1_call(func_id))
|
||||||
|
return false;
|
||||||
ret = psci_0_1_handler(func_id, host_ctxt);
|
ret = psci_0_1_handler(func_id, host_ctxt);
|
||||||
break;
|
break;
|
||||||
case PSCI_VERSION(0, 2):
|
case PSCI_VERSION(0, 2):
|
||||||
|
if (!is_psci_0_2_call(func_id))
|
||||||
|
return false;
|
||||||
ret = psci_0_2_handler(func_id, host_ctxt);
|
ret = psci_0_2_handler(func_id, host_ctxt);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (!is_psci_0_2_call(func_id))
|
||||||
|
return false;
|
||||||
ret = psci_1_0_handler(func_id, host_ctxt);
|
ret = psci_1_0_handler(func_id, host_ctxt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -850,8 +850,6 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_pmu_vcpu_reset(vcpu);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,6 +594,10 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
|
|||||||
{
|
{
|
||||||
u64 pmcr, val;
|
u64 pmcr, val;
|
||||||
|
|
||||||
|
/* No PMU available, PMCR_EL0 may UNDEF... */
|
||||||
|
if (!kvm_arm_support_pmu_v3())
|
||||||
|
return;
|
||||||
|
|
||||||
pmcr = read_sysreg(pmcr_el0);
|
pmcr = read_sysreg(pmcr_el0);
|
||||||
/*
|
/*
|
||||||
* Writable bits of PMCR_EL0 (ARMV8_PMU_PMCR_MASK) are reset to UNKNOWN
|
* Writable bits of PMCR_EL0 (ARMV8_PMU_PMCR_MASK) are reset to UNKNOWN
|
||||||
@ -919,7 +923,7 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
|||||||
|
|
||||||
#define reg_to_encoding(x) \
|
#define reg_to_encoding(x) \
|
||||||
sys_reg((u32)(x)->Op0, (u32)(x)->Op1, \
|
sys_reg((u32)(x)->Op0, (u32)(x)->Op1, \
|
||||||
(u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2);
|
(u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2)
|
||||||
|
|
||||||
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
|
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
|
||||||
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
|
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
|
||||||
|
@ -34,17 +34,16 @@ static u64 __early_kern_hyp_va(u64 addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store a hyp VA <-> PA offset into a hyp-owned variable.
|
* Store a hyp VA <-> PA offset into a EL2-owned variable.
|
||||||
*/
|
*/
|
||||||
static void init_hyp_physvirt_offset(void)
|
static void init_hyp_physvirt_offset(void)
|
||||||
{
|
{
|
||||||
extern s64 kvm_nvhe_sym(hyp_physvirt_offset);
|
|
||||||
u64 kern_va, hyp_va;
|
u64 kern_va, hyp_va;
|
||||||
|
|
||||||
/* Compute the offset from the hyp VA and PA of a random symbol. */
|
/* Compute the offset from the hyp VA and PA of a random symbol. */
|
||||||
kern_va = (u64)kvm_ksym_ref(__hyp_text_start);
|
kern_va = (u64)lm_alias(__hyp_text_start);
|
||||||
hyp_va = __early_kern_hyp_va(kern_va);
|
hyp_va = __early_kern_hyp_va(kern_va);
|
||||||
CHOOSE_NVHE_SYM(hyp_physvirt_offset) = (s64)__pa(kern_va) - (s64)hyp_va;
|
hyp_physvirt_offset = (s64)__pa(kern_va) - (s64)hyp_va;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -419,7 +419,8 @@ int vgic_lazy_init(struct kvm *kvm)
|
|||||||
* Map the MMIO regions depending on the VGIC model exposed to the guest
|
* Map the MMIO regions depending on the VGIC model exposed to the guest
|
||||||
* called on the first VCPU run.
|
* called on the first VCPU run.
|
||||||
* Also map the virtual CPU interface into the VM.
|
* Also map the virtual CPU interface into the VM.
|
||||||
* v2/v3 derivatives call vgic_init if not already done.
|
* v2 calls vgic_init() if not already done.
|
||||||
|
* v3 and derivatives return an error if the VGIC is not initialized.
|
||||||
* vgic_ready() returns true if this function has succeeded.
|
* vgic_ready() returns true if this function has succeeded.
|
||||||
* @kvm: kvm struct pointer
|
* @kvm: kvm struct pointer
|
||||||
*/
|
*/
|
||||||
@ -428,7 +429,13 @@ int kvm_vgic_map_resources(struct kvm *kvm)
|
|||||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (likely(vgic_ready(kvm)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
mutex_lock(&kvm->lock);
|
mutex_lock(&kvm->lock);
|
||||||
|
if (vgic_ready(kvm))
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (!irqchip_in_kernel(kvm))
|
if (!irqchip_in_kernel(kvm))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -439,6 +446,8 @@ int kvm_vgic_map_resources(struct kvm *kvm)
|
|||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
__kvm_vgic_destroy(kvm);
|
__kvm_vgic_destroy(kvm);
|
||||||
|
else
|
||||||
|
dist->ready = true;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&kvm->lock);
|
mutex_unlock(&kvm->lock);
|
||||||
|
@ -306,20 +306,15 @@ int vgic_v2_map_resources(struct kvm *kvm)
|
|||||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (vgic_ready(kvm))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
|
if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
|
||||||
IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) {
|
IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) {
|
||||||
kvm_err("Need to set vgic cpu and dist addresses first\n");
|
kvm_err("Need to set vgic cpu and dist addresses first\n");
|
||||||
ret = -ENXIO;
|
return -ENXIO;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) {
|
if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) {
|
||||||
kvm_err("VGIC CPU and dist frames overlap\n");
|
kvm_err("VGIC CPU and dist frames overlap\n");
|
||||||
ret = -EINVAL;
|
return -EINVAL;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -329,13 +324,13 @@ int vgic_v2_map_resources(struct kvm *kvm)
|
|||||||
ret = vgic_init(kvm);
|
ret = vgic_init(kvm);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kvm_err("Unable to initialize VGIC dynamic data structures\n");
|
kvm_err("Unable to initialize VGIC dynamic data structures\n");
|
||||||
goto out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2);
|
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kvm_err("Unable to register VGIC MMIO regions\n");
|
kvm_err("Unable to register VGIC MMIO regions\n");
|
||||||
goto out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
|
if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
|
||||||
@ -344,14 +339,11 @@ int vgic_v2_map_resources(struct kvm *kvm)
|
|||||||
KVM_VGIC_V2_CPU_SIZE, true);
|
KVM_VGIC_V2_CPU_SIZE, true);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kvm_err("Unable to remap VGIC CPU to VCPU\n");
|
kvm_err("Unable to remap VGIC CPU to VCPU\n");
|
||||||
goto out;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dist->ready = true;
|
return 0;
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
|
DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
|
||||||
|
@ -500,29 +500,23 @@ int vgic_v3_map_resources(struct kvm *kvm)
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
if (vgic_ready(kvm))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
kvm_for_each_vcpu(c, vcpu, kvm) {
|
kvm_for_each_vcpu(c, vcpu, kvm) {
|
||||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||||
|
|
||||||
if (IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) {
|
if (IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) {
|
||||||
kvm_debug("vcpu %d redistributor base not set\n", c);
|
kvm_debug("vcpu %d redistributor base not set\n", c);
|
||||||
ret = -ENXIO;
|
return -ENXIO;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) {
|
if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) {
|
||||||
kvm_err("Need to set vgic distributor addresses first\n");
|
kvm_err("Need to set vgic distributor addresses first\n");
|
||||||
ret = -ENXIO;
|
return -ENXIO;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vgic_v3_check_base(kvm)) {
|
if (!vgic_v3_check_base(kvm)) {
|
||||||
kvm_err("VGIC redist and dist frames overlap\n");
|
kvm_err("VGIC redist and dist frames overlap\n");
|
||||||
ret = -EINVAL;
|
return -EINVAL;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -530,22 +524,19 @@ int vgic_v3_map_resources(struct kvm *kvm)
|
|||||||
* the VGIC before we need to use it.
|
* the VGIC before we need to use it.
|
||||||
*/
|
*/
|
||||||
if (!vgic_initialized(kvm)) {
|
if (!vgic_initialized(kvm)) {
|
||||||
ret = -EBUSY;
|
return -EBUSY;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3);
|
ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kvm_err("Unable to register VGICv3 dist MMIO regions\n");
|
kvm_err("Unable to register VGICv3 dist MMIO regions\n");
|
||||||
goto out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kvm_vgic_global_state.has_gicv4_1)
|
if (kvm_vgic_global_state.has_gicv4_1)
|
||||||
vgic_v4_configure_vsgis(kvm);
|
vgic_v4_configure_vsgis(kvm);
|
||||||
dist->ready = true;
|
|
||||||
|
|
||||||
out:
|
return 0;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_STATIC_KEY_FALSE(vgic_v3_cpuif_trap);
|
DEFINE_STATIC_KEY_FALSE(vgic_v3_cpuif_trap);
|
||||||
|
@ -1010,9 +1010,21 @@ struct kvm_arch {
|
|||||||
*/
|
*/
|
||||||
bool tdp_mmu_enabled;
|
bool tdp_mmu_enabled;
|
||||||
|
|
||||||
/* List of struct tdp_mmu_pages being used as roots */
|
/*
|
||||||
|
* List of struct kvmp_mmu_pages being used as roots.
|
||||||
|
* All struct kvm_mmu_pages in the list should have
|
||||||
|
* tdp_mmu_page set.
|
||||||
|
* All struct kvm_mmu_pages in the list should have a positive
|
||||||
|
* root_count except when a thread holds the MMU lock and is removing
|
||||||
|
* an entry from the list.
|
||||||
|
*/
|
||||||
struct list_head tdp_mmu_roots;
|
struct list_head tdp_mmu_roots;
|
||||||
/* List of struct tdp_mmu_pages not being used as roots */
|
|
||||||
|
/*
|
||||||
|
* List of struct kvmp_mmu_pages not being used as roots.
|
||||||
|
* All struct kvm_mmu_pages in the list should have
|
||||||
|
* tdp_mmu_page set and a root_count of 0.
|
||||||
|
*/
|
||||||
struct list_head tdp_mmu_pages;
|
struct list_head tdp_mmu_pages;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1287,6 +1299,8 @@ struct kvm_x86_ops {
|
|||||||
void (*migrate_timers)(struct kvm_vcpu *vcpu);
|
void (*migrate_timers)(struct kvm_vcpu *vcpu);
|
||||||
void (*msr_filter_changed)(struct kvm_vcpu *vcpu);
|
void (*msr_filter_changed)(struct kvm_vcpu *vcpu);
|
||||||
int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err);
|
int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err);
|
||||||
|
|
||||||
|
void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_x86_nested_ops {
|
struct kvm_x86_nested_ops {
|
||||||
@ -1468,6 +1482,7 @@ int kvm_fast_pio(struct kvm_vcpu *vcpu, int size, unsigned short port, int in);
|
|||||||
int kvm_emulate_cpuid(struct kvm_vcpu *vcpu);
|
int kvm_emulate_cpuid(struct kvm_vcpu *vcpu);
|
||||||
int kvm_emulate_halt(struct kvm_vcpu *vcpu);
|
int kvm_emulate_halt(struct kvm_vcpu *vcpu);
|
||||||
int kvm_vcpu_halt(struct kvm_vcpu *vcpu);
|
int kvm_vcpu_halt(struct kvm_vcpu *vcpu);
|
||||||
|
int kvm_emulate_ap_reset_hold(struct kvm_vcpu *vcpu);
|
||||||
int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu);
|
int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu);
|
||||||
|
|
||||||
void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
|
void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
|
||||||
|
@ -674,7 +674,7 @@ static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu)
|
|||||||
(unsigned long long)vcpu->arch.pv_eoi.msr_val);
|
(unsigned long long)vcpu->arch.pv_eoi.msr_val);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return val & 0x1;
|
return val & KVM_PV_EOI_ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pv_eoi_set_pending(struct kvm_vcpu *vcpu)
|
static void pv_eoi_set_pending(struct kvm_vcpu *vcpu)
|
||||||
@ -2898,7 +2898,7 @@ void kvm_apic_accept_events(struct kvm_vcpu *vcpu)
|
|||||||
/* evaluate pending_events before reading the vector */
|
/* evaluate pending_events before reading the vector */
|
||||||
smp_rmb();
|
smp_rmb();
|
||||||
sipi_vector = apic->sipi_vector;
|
sipi_vector = apic->sipi_vector;
|
||||||
kvm_vcpu_deliver_sipi_vector(vcpu, sipi_vector);
|
kvm_x86_ops.vcpu_deliver_sipi_vector(vcpu, sipi_vector);
|
||||||
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ static inline u64 rsvd_bits(int s, int e)
|
|||||||
if (e < s)
|
if (e < s)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return ((1ULL << (e - s + 1)) - 1) << s;
|
return ((2ULL << (e - s)) - 1) << s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 access_mask);
|
void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 access_mask);
|
||||||
|
@ -3493,26 +3493,25 @@ static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct)
|
|||||||
* Return the level of the lowest level SPTE added to sptes.
|
* Return the level of the lowest level SPTE added to sptes.
|
||||||
* That SPTE may be non-present.
|
* That SPTE may be non-present.
|
||||||
*/
|
*/
|
||||||
static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
|
static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, int *root_level)
|
||||||
{
|
{
|
||||||
struct kvm_shadow_walk_iterator iterator;
|
struct kvm_shadow_walk_iterator iterator;
|
||||||
int leaf = vcpu->arch.mmu->root_level;
|
int leaf = -1;
|
||||||
u64 spte;
|
u64 spte;
|
||||||
|
|
||||||
|
|
||||||
walk_shadow_page_lockless_begin(vcpu);
|
walk_shadow_page_lockless_begin(vcpu);
|
||||||
|
|
||||||
for (shadow_walk_init(&iterator, vcpu, addr);
|
for (shadow_walk_init(&iterator, vcpu, addr),
|
||||||
|
*root_level = iterator.level;
|
||||||
shadow_walk_okay(&iterator);
|
shadow_walk_okay(&iterator);
|
||||||
__shadow_walk_next(&iterator, spte)) {
|
__shadow_walk_next(&iterator, spte)) {
|
||||||
leaf = iterator.level;
|
leaf = iterator.level;
|
||||||
spte = mmu_spte_get_lockless(iterator.sptep);
|
spte = mmu_spte_get_lockless(iterator.sptep);
|
||||||
|
|
||||||
sptes[leaf - 1] = spte;
|
sptes[leaf] = spte;
|
||||||
|
|
||||||
if (!is_shadow_present_pte(spte))
|
if (!is_shadow_present_pte(spte))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
walk_shadow_page_lockless_end(vcpu);
|
walk_shadow_page_lockless_end(vcpu);
|
||||||
@ -3520,14 +3519,12 @@ static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
|
|||||||
return leaf;
|
return leaf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return true if reserved bit is detected on spte. */
|
/* return true if reserved bit(s) are detected on a valid, non-MMIO SPTE. */
|
||||||
static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
|
static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
|
||||||
{
|
{
|
||||||
u64 sptes[PT64_ROOT_MAX_LEVEL];
|
u64 sptes[PT64_ROOT_MAX_LEVEL + 1];
|
||||||
struct rsvd_bits_validate *rsvd_check;
|
struct rsvd_bits_validate *rsvd_check;
|
||||||
int root = vcpu->arch.mmu->shadow_root_level;
|
int root, leaf, level;
|
||||||
int leaf;
|
|
||||||
int level;
|
|
||||||
bool reserved = false;
|
bool reserved = false;
|
||||||
|
|
||||||
if (!VALID_PAGE(vcpu->arch.mmu->root_hpa)) {
|
if (!VALID_PAGE(vcpu->arch.mmu->root_hpa)) {
|
||||||
@ -3536,35 +3533,45 @@ static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (is_tdp_mmu_root(vcpu->kvm, vcpu->arch.mmu->root_hpa))
|
if (is_tdp_mmu_root(vcpu->kvm, vcpu->arch.mmu->root_hpa))
|
||||||
leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes);
|
leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, &root);
|
||||||
else
|
else
|
||||||
leaf = get_walk(vcpu, addr, sptes);
|
leaf = get_walk(vcpu, addr, sptes, &root);
|
||||||
|
|
||||||
|
if (unlikely(leaf < 0)) {
|
||||||
|
*sptep = 0ull;
|
||||||
|
return reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sptep = sptes[leaf];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip reserved bits checks on the terminal leaf if it's not a valid
|
||||||
|
* SPTE. Note, this also (intentionally) skips MMIO SPTEs, which, by
|
||||||
|
* design, always have reserved bits set. The purpose of the checks is
|
||||||
|
* to detect reserved bits on non-MMIO SPTEs. i.e. buggy SPTEs.
|
||||||
|
*/
|
||||||
|
if (!is_shadow_present_pte(sptes[leaf]))
|
||||||
|
leaf++;
|
||||||
|
|
||||||
rsvd_check = &vcpu->arch.mmu->shadow_zero_check;
|
rsvd_check = &vcpu->arch.mmu->shadow_zero_check;
|
||||||
|
|
||||||
for (level = root; level >= leaf; level--) {
|
for (level = root; level >= leaf; level--)
|
||||||
if (!is_shadow_present_pte(sptes[level - 1]))
|
|
||||||
break;
|
|
||||||
/*
|
/*
|
||||||
* Use a bitwise-OR instead of a logical-OR to aggregate the
|
* Use a bitwise-OR instead of a logical-OR to aggregate the
|
||||||
* reserved bit and EPT's invalid memtype/XWR checks to avoid
|
* reserved bit and EPT's invalid memtype/XWR checks to avoid
|
||||||
* adding a Jcc in the loop.
|
* adding a Jcc in the loop.
|
||||||
*/
|
*/
|
||||||
reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level - 1]) |
|
reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level]) |
|
||||||
__is_rsvd_bits_set(rsvd_check, sptes[level - 1],
|
__is_rsvd_bits_set(rsvd_check, sptes[level], level);
|
||||||
level);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reserved) {
|
if (reserved) {
|
||||||
pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n",
|
pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n",
|
||||||
__func__, addr);
|
__func__, addr);
|
||||||
for (level = root; level >= leaf; level--)
|
for (level = root; level >= leaf; level--)
|
||||||
pr_err("------ spte 0x%llx level %d.\n",
|
pr_err("------ spte 0x%llx level %d.\n",
|
||||||
sptes[level - 1], level);
|
sptes[level], level);
|
||||||
}
|
}
|
||||||
|
|
||||||
*sptep = sptes[leaf - 1];
|
|
||||||
|
|
||||||
return reserved;
|
return reserved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,48 @@ void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm)
|
|||||||
WARN_ON(!list_empty(&kvm->arch.tdp_mmu_roots));
|
WARN_ON(!list_empty(&kvm->arch.tdp_mmu_roots));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define for_each_tdp_mmu_root(_kvm, _root) \
|
static void tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root)
|
||||||
|
{
|
||||||
|
if (kvm_mmu_put_root(kvm, root))
|
||||||
|
kvm_tdp_mmu_free_root(kvm, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool tdp_mmu_next_root_valid(struct kvm *kvm,
|
||||||
|
struct kvm_mmu_page *root)
|
||||||
|
{
|
||||||
|
lockdep_assert_held(&kvm->mmu_lock);
|
||||||
|
|
||||||
|
if (list_entry_is_head(root, &kvm->arch.tdp_mmu_roots, link))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
kvm_mmu_get_root(kvm, root);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct kvm_mmu_page *tdp_mmu_next_root(struct kvm *kvm,
|
||||||
|
struct kvm_mmu_page *root)
|
||||||
|
{
|
||||||
|
struct kvm_mmu_page *next_root;
|
||||||
|
|
||||||
|
next_root = list_next_entry(root, link);
|
||||||
|
tdp_mmu_put_root(kvm, root);
|
||||||
|
return next_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: this iterator gets and puts references to the roots it iterates over.
|
||||||
|
* This makes it safe to release the MMU lock and yield within the loop, but
|
||||||
|
* if exiting the loop early, the caller must drop the reference to the most
|
||||||
|
* recent root. (Unless keeping a live reference is desirable.)
|
||||||
|
*/
|
||||||
|
#define for_each_tdp_mmu_root_yield_safe(_kvm, _root) \
|
||||||
|
for (_root = list_first_entry(&_kvm->arch.tdp_mmu_roots, \
|
||||||
|
typeof(*_root), link); \
|
||||||
|
tdp_mmu_next_root_valid(_kvm, _root); \
|
||||||
|
_root = tdp_mmu_next_root(_kvm, _root))
|
||||||
|
|
||||||
|
#define for_each_tdp_mmu_root(_kvm, _root) \
|
||||||
list_for_each_entry(_root, &_kvm->arch.tdp_mmu_roots, link)
|
list_for_each_entry(_root, &_kvm->arch.tdp_mmu_roots, link)
|
||||||
|
|
||||||
bool is_tdp_mmu_root(struct kvm *kvm, hpa_t hpa)
|
bool is_tdp_mmu_root(struct kvm *kvm, hpa_t hpa)
|
||||||
@ -447,18 +488,9 @@ bool kvm_tdp_mmu_zap_gfn_range(struct kvm *kvm, gfn_t start, gfn_t end)
|
|||||||
struct kvm_mmu_page *root;
|
struct kvm_mmu_page *root;
|
||||||
bool flush = false;
|
bool flush = false;
|
||||||
|
|
||||||
for_each_tdp_mmu_root(kvm, root) {
|
for_each_tdp_mmu_root_yield_safe(kvm, root)
|
||||||
/*
|
|
||||||
* Take a reference on the root so that it cannot be freed if
|
|
||||||
* this thread releases the MMU lock and yields in this loop.
|
|
||||||
*/
|
|
||||||
kvm_mmu_get_root(kvm, root);
|
|
||||||
|
|
||||||
flush |= zap_gfn_range(kvm, root, start, end, true);
|
flush |= zap_gfn_range(kvm, root, start, end, true);
|
||||||
|
|
||||||
kvm_mmu_put_root(kvm, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
return flush;
|
return flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,13 +651,7 @@ static int kvm_tdp_mmu_handle_hva_range(struct kvm *kvm, unsigned long start,
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
int as_id;
|
int as_id;
|
||||||
|
|
||||||
for_each_tdp_mmu_root(kvm, root) {
|
for_each_tdp_mmu_root_yield_safe(kvm, root) {
|
||||||
/*
|
|
||||||
* Take a reference on the root so that it cannot be freed if
|
|
||||||
* this thread releases the MMU lock and yields in this loop.
|
|
||||||
*/
|
|
||||||
kvm_mmu_get_root(kvm, root);
|
|
||||||
|
|
||||||
as_id = kvm_mmu_page_as_id(root);
|
as_id = kvm_mmu_page_as_id(root);
|
||||||
slots = __kvm_memslots(kvm, as_id);
|
slots = __kvm_memslots(kvm, as_id);
|
||||||
kvm_for_each_memslot(memslot, slots) {
|
kvm_for_each_memslot(memslot, slots) {
|
||||||
@ -647,8 +673,6 @@ static int kvm_tdp_mmu_handle_hva_range(struct kvm *kvm, unsigned long start,
|
|||||||
ret |= handler(kvm, memslot, root, gfn_start,
|
ret |= handler(kvm, memslot, root, gfn_start,
|
||||||
gfn_end, data);
|
gfn_end, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_mmu_put_root(kvm, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -838,21 +862,13 @@ bool kvm_tdp_mmu_wrprot_slot(struct kvm *kvm, struct kvm_memory_slot *slot,
|
|||||||
int root_as_id;
|
int root_as_id;
|
||||||
bool spte_set = false;
|
bool spte_set = false;
|
||||||
|
|
||||||
for_each_tdp_mmu_root(kvm, root) {
|
for_each_tdp_mmu_root_yield_safe(kvm, root) {
|
||||||
root_as_id = kvm_mmu_page_as_id(root);
|
root_as_id = kvm_mmu_page_as_id(root);
|
||||||
if (root_as_id != slot->as_id)
|
if (root_as_id != slot->as_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
|
||||||
* Take a reference on the root so that it cannot be freed if
|
|
||||||
* this thread releases the MMU lock and yields in this loop.
|
|
||||||
*/
|
|
||||||
kvm_mmu_get_root(kvm, root);
|
|
||||||
|
|
||||||
spte_set |= wrprot_gfn_range(kvm, root, slot->base_gfn,
|
spte_set |= wrprot_gfn_range(kvm, root, slot->base_gfn,
|
||||||
slot->base_gfn + slot->npages, min_level);
|
slot->base_gfn + slot->npages, min_level);
|
||||||
|
|
||||||
kvm_mmu_put_root(kvm, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return spte_set;
|
return spte_set;
|
||||||
@ -906,21 +922,13 @@ bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
|
|||||||
int root_as_id;
|
int root_as_id;
|
||||||
bool spte_set = false;
|
bool spte_set = false;
|
||||||
|
|
||||||
for_each_tdp_mmu_root(kvm, root) {
|
for_each_tdp_mmu_root_yield_safe(kvm, root) {
|
||||||
root_as_id = kvm_mmu_page_as_id(root);
|
root_as_id = kvm_mmu_page_as_id(root);
|
||||||
if (root_as_id != slot->as_id)
|
if (root_as_id != slot->as_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
|
||||||
* Take a reference on the root so that it cannot be freed if
|
|
||||||
* this thread releases the MMU lock and yields in this loop.
|
|
||||||
*/
|
|
||||||
kvm_mmu_get_root(kvm, root);
|
|
||||||
|
|
||||||
spte_set |= clear_dirty_gfn_range(kvm, root, slot->base_gfn,
|
spte_set |= clear_dirty_gfn_range(kvm, root, slot->base_gfn,
|
||||||
slot->base_gfn + slot->npages);
|
slot->base_gfn + slot->npages);
|
||||||
|
|
||||||
kvm_mmu_put_root(kvm, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return spte_set;
|
return spte_set;
|
||||||
@ -1029,21 +1037,13 @@ bool kvm_tdp_mmu_slot_set_dirty(struct kvm *kvm, struct kvm_memory_slot *slot)
|
|||||||
int root_as_id;
|
int root_as_id;
|
||||||
bool spte_set = false;
|
bool spte_set = false;
|
||||||
|
|
||||||
for_each_tdp_mmu_root(kvm, root) {
|
for_each_tdp_mmu_root_yield_safe(kvm, root) {
|
||||||
root_as_id = kvm_mmu_page_as_id(root);
|
root_as_id = kvm_mmu_page_as_id(root);
|
||||||
if (root_as_id != slot->as_id)
|
if (root_as_id != slot->as_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
|
||||||
* Take a reference on the root so that it cannot be freed if
|
|
||||||
* this thread releases the MMU lock and yields in this loop.
|
|
||||||
*/
|
|
||||||
kvm_mmu_get_root(kvm, root);
|
|
||||||
|
|
||||||
spte_set |= set_dirty_gfn_range(kvm, root, slot->base_gfn,
|
spte_set |= set_dirty_gfn_range(kvm, root, slot->base_gfn,
|
||||||
slot->base_gfn + slot->npages);
|
slot->base_gfn + slot->npages);
|
||||||
|
|
||||||
kvm_mmu_put_root(kvm, root);
|
|
||||||
}
|
}
|
||||||
return spte_set;
|
return spte_set;
|
||||||
}
|
}
|
||||||
@ -1089,21 +1089,13 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
|||||||
struct kvm_mmu_page *root;
|
struct kvm_mmu_page *root;
|
||||||
int root_as_id;
|
int root_as_id;
|
||||||
|
|
||||||
for_each_tdp_mmu_root(kvm, root) {
|
for_each_tdp_mmu_root_yield_safe(kvm, root) {
|
||||||
root_as_id = kvm_mmu_page_as_id(root);
|
root_as_id = kvm_mmu_page_as_id(root);
|
||||||
if (root_as_id != slot->as_id)
|
if (root_as_id != slot->as_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
|
||||||
* Take a reference on the root so that it cannot be freed if
|
|
||||||
* this thread releases the MMU lock and yields in this loop.
|
|
||||||
*/
|
|
||||||
kvm_mmu_get_root(kvm, root);
|
|
||||||
|
|
||||||
zap_collapsible_spte_range(kvm, root, slot->base_gfn,
|
zap_collapsible_spte_range(kvm, root, slot->base_gfn,
|
||||||
slot->base_gfn + slot->npages);
|
slot->base_gfn + slot->npages);
|
||||||
|
|
||||||
kvm_mmu_put_root(kvm, root);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1160,16 +1152,19 @@ bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
|
|||||||
* Return the level of the lowest level SPTE added to sptes.
|
* Return the level of the lowest level SPTE added to sptes.
|
||||||
* That SPTE may be non-present.
|
* That SPTE may be non-present.
|
||||||
*/
|
*/
|
||||||
int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
|
int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes,
|
||||||
|
int *root_level)
|
||||||
{
|
{
|
||||||
struct tdp_iter iter;
|
struct tdp_iter iter;
|
||||||
struct kvm_mmu *mmu = vcpu->arch.mmu;
|
struct kvm_mmu *mmu = vcpu->arch.mmu;
|
||||||
int leaf = vcpu->arch.mmu->shadow_root_level;
|
|
||||||
gfn_t gfn = addr >> PAGE_SHIFT;
|
gfn_t gfn = addr >> PAGE_SHIFT;
|
||||||
|
int leaf = -1;
|
||||||
|
|
||||||
|
*root_level = vcpu->arch.mmu->shadow_root_level;
|
||||||
|
|
||||||
tdp_mmu_for_each_pte(iter, mmu, gfn, gfn + 1) {
|
tdp_mmu_for_each_pte(iter, mmu, gfn, gfn + 1) {
|
||||||
leaf = iter.level;
|
leaf = iter.level;
|
||||||
sptes[leaf - 1] = iter.old_spte;
|
sptes[leaf] = iter.old_spte;
|
||||||
}
|
}
|
||||||
|
|
||||||
return leaf;
|
return leaf;
|
||||||
|
@ -44,5 +44,7 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
|
|||||||
bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
|
bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
|
||||||
struct kvm_memory_slot *slot, gfn_t gfn);
|
struct kvm_memory_slot *slot, gfn_t gfn);
|
||||||
|
|
||||||
int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes);
|
int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes,
|
||||||
|
int *root_level);
|
||||||
|
|
||||||
#endif /* __KVM_X86_MMU_TDP_MMU_H */
|
#endif /* __KVM_X86_MMU_TDP_MMU_H */
|
||||||
|
@ -199,6 +199,7 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
|
|||||||
static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu)
|
static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct vcpu_svm *svm = to_svm(vcpu);
|
struct vcpu_svm *svm = to_svm(vcpu);
|
||||||
|
|
||||||
if (!nested_svm_vmrun_msrpm(svm)) {
|
if (!nested_svm_vmrun_msrpm(svm)) {
|
||||||
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
|
||||||
vcpu->run->internal.suberror =
|
vcpu->run->internal.suberror =
|
||||||
@ -595,6 +596,8 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
|
|||||||
svm->nested.vmcb12_gpa = 0;
|
svm->nested.vmcb12_gpa = 0;
|
||||||
WARN_ON_ONCE(svm->nested.nested_run_pending);
|
WARN_ON_ONCE(svm->nested.nested_run_pending);
|
||||||
|
|
||||||
|
kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, &svm->vcpu);
|
||||||
|
|
||||||
/* in case we halted in L2 */
|
/* in case we halted in L2 */
|
||||||
svm->vcpu.arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
svm->vcpu.arch.mp_state = KVM_MP_STATE_RUNNABLE;
|
||||||
|
|
||||||
@ -754,6 +757,7 @@ void svm_leave_nested(struct vcpu_svm *svm)
|
|||||||
leave_guest_mode(&svm->vcpu);
|
leave_guest_mode(&svm->vcpu);
|
||||||
copy_vmcb_control_area(&vmcb->control, &hsave->control);
|
copy_vmcb_control_area(&vmcb->control, &hsave->control);
|
||||||
nested_svm_uninit_mmu_context(&svm->vcpu);
|
nested_svm_uninit_mmu_context(&svm->vcpu);
|
||||||
|
vmcb_mark_all_dirty(svm->vmcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, &svm->vcpu);
|
kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, &svm->vcpu);
|
||||||
@ -1194,6 +1198,10 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu,
|
|||||||
* in the registers, the save area of the nested state instead
|
* in the registers, the save area of the nested state instead
|
||||||
* contains saved L1 state.
|
* contains saved L1 state.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
svm->nested.nested_run_pending =
|
||||||
|
!!(kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING);
|
||||||
|
|
||||||
copy_vmcb_control_area(&hsave->control, &svm->vmcb->control);
|
copy_vmcb_control_area(&hsave->control, &svm->vmcb->control);
|
||||||
hsave->save = *save;
|
hsave->save = *save;
|
||||||
|
|
||||||
|
@ -1563,6 +1563,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
|
|||||||
goto vmgexit_err;
|
goto vmgexit_err;
|
||||||
break;
|
break;
|
||||||
case SVM_VMGEXIT_NMI_COMPLETE:
|
case SVM_VMGEXIT_NMI_COMPLETE:
|
||||||
|
case SVM_VMGEXIT_AP_HLT_LOOP:
|
||||||
case SVM_VMGEXIT_AP_JUMP_TABLE:
|
case SVM_VMGEXIT_AP_JUMP_TABLE:
|
||||||
case SVM_VMGEXIT_UNSUPPORTED_EVENT:
|
case SVM_VMGEXIT_UNSUPPORTED_EVENT:
|
||||||
break;
|
break;
|
||||||
@ -1888,6 +1889,9 @@ int sev_handle_vmgexit(struct vcpu_svm *svm)
|
|||||||
case SVM_VMGEXIT_NMI_COMPLETE:
|
case SVM_VMGEXIT_NMI_COMPLETE:
|
||||||
ret = svm_invoke_exit_handler(svm, SVM_EXIT_IRET);
|
ret = svm_invoke_exit_handler(svm, SVM_EXIT_IRET);
|
||||||
break;
|
break;
|
||||||
|
case SVM_VMGEXIT_AP_HLT_LOOP:
|
||||||
|
ret = kvm_emulate_ap_reset_hold(&svm->vcpu);
|
||||||
|
break;
|
||||||
case SVM_VMGEXIT_AP_JUMP_TABLE: {
|
case SVM_VMGEXIT_AP_JUMP_TABLE: {
|
||||||
struct kvm_sev_info *sev = &to_kvm_svm(svm->vcpu.kvm)->sev_info;
|
struct kvm_sev_info *sev = &to_kvm_svm(svm->vcpu.kvm)->sev_info;
|
||||||
|
|
||||||
@ -2001,7 +2005,7 @@ void sev_es_vcpu_load(struct vcpu_svm *svm, int cpu)
|
|||||||
* of which one step is to perform a VMLOAD. Since hardware does not
|
* of which one step is to perform a VMLOAD. Since hardware does not
|
||||||
* perform a VMSAVE on VMRUN, the host savearea must be updated.
|
* perform a VMSAVE on VMRUN, the host savearea must be updated.
|
||||||
*/
|
*/
|
||||||
asm volatile(__ex("vmsave") : : "a" (__sme_page_pa(sd->save_area)) : "memory");
|
asm volatile(__ex("vmsave %0") : : "a" (__sme_page_pa(sd->save_area)) : "memory");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Certain MSRs are restored on VMEXIT, only save ones that aren't
|
* Certain MSRs are restored on VMEXIT, only save ones that aren't
|
||||||
@ -2040,3 +2044,21 @@ void sev_es_vcpu_put(struct vcpu_svm *svm)
|
|||||||
wrmsrl(host_save_user_msrs[i].index, svm->host_user_msrs[i]);
|
wrmsrl(host_save_user_msrs[i].index, svm->host_user_msrs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
|
||||||
|
{
|
||||||
|
struct vcpu_svm *svm = to_svm(vcpu);
|
||||||
|
|
||||||
|
/* First SIPI: Use the values as initially set by the VMM */
|
||||||
|
if (!svm->received_first_sipi) {
|
||||||
|
svm->received_first_sipi = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subsequent SIPI: Return from an AP Reset Hold VMGEXIT, where
|
||||||
|
* the guest will set the CS and RIP. Set SW_EXIT_INFO_2 to a
|
||||||
|
* non-zero value.
|
||||||
|
*/
|
||||||
|
ghcb_set_sw_exit_info_2(svm->ghcb, 1);
|
||||||
|
}
|
||||||
|
@ -3677,8 +3677,6 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
|
|||||||
return EXIT_FASTPATH_NONE;
|
return EXIT_FASTPATH_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __svm_vcpu_run(unsigned long vmcb_pa, unsigned long *regs);
|
|
||||||
|
|
||||||
static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu,
|
static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu,
|
||||||
struct vcpu_svm *svm)
|
struct vcpu_svm *svm)
|
||||||
{
|
{
|
||||||
@ -4384,6 +4382,14 @@ static bool svm_apic_init_signal_blocked(struct kvm_vcpu *vcpu)
|
|||||||
(vmcb_is_intercept(&svm->vmcb->control, INTERCEPT_INIT));
|
(vmcb_is_intercept(&svm->vmcb->control, INTERCEPT_INIT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void svm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
|
||||||
|
{
|
||||||
|
if (!sev_es_guest(vcpu->kvm))
|
||||||
|
return kvm_vcpu_deliver_sipi_vector(vcpu, vector);
|
||||||
|
|
||||||
|
sev_vcpu_deliver_sipi_vector(vcpu, vector);
|
||||||
|
}
|
||||||
|
|
||||||
static void svm_vm_destroy(struct kvm *kvm)
|
static void svm_vm_destroy(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
avic_vm_destroy(kvm);
|
avic_vm_destroy(kvm);
|
||||||
@ -4526,6 +4532,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
|
|||||||
|
|
||||||
.msr_filter_changed = svm_msr_filter_changed,
|
.msr_filter_changed = svm_msr_filter_changed,
|
||||||
.complete_emulated_msr = svm_complete_emulated_msr,
|
.complete_emulated_msr = svm_complete_emulated_msr,
|
||||||
|
|
||||||
|
.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct kvm_x86_init_ops svm_init_ops __initdata = {
|
static struct kvm_x86_init_ops svm_init_ops __initdata = {
|
||||||
|
@ -185,6 +185,7 @@ struct vcpu_svm {
|
|||||||
struct vmcb_save_area *vmsa;
|
struct vmcb_save_area *vmsa;
|
||||||
struct ghcb *ghcb;
|
struct ghcb *ghcb;
|
||||||
struct kvm_host_map ghcb_map;
|
struct kvm_host_map ghcb_map;
|
||||||
|
bool received_first_sipi;
|
||||||
|
|
||||||
/* SEV-ES scratch area support */
|
/* SEV-ES scratch area support */
|
||||||
void *ghcb_sa;
|
void *ghcb_sa;
|
||||||
@ -591,6 +592,7 @@ void sev_es_init_vmcb(struct vcpu_svm *svm);
|
|||||||
void sev_es_create_vcpu(struct vcpu_svm *svm);
|
void sev_es_create_vcpu(struct vcpu_svm *svm);
|
||||||
void sev_es_vcpu_load(struct vcpu_svm *svm, int cpu);
|
void sev_es_vcpu_load(struct vcpu_svm *svm, int cpu);
|
||||||
void sev_es_vcpu_put(struct vcpu_svm *svm);
|
void sev_es_vcpu_put(struct vcpu_svm *svm);
|
||||||
|
void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector);
|
||||||
|
|
||||||
/* vmenter.S */
|
/* vmenter.S */
|
||||||
|
|
||||||
|
@ -4442,6 +4442,8 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
|
|||||||
/* trying to cancel vmlaunch/vmresume is a bug */
|
/* trying to cancel vmlaunch/vmresume is a bug */
|
||||||
WARN_ON_ONCE(vmx->nested.nested_run_pending);
|
WARN_ON_ONCE(vmx->nested.nested_run_pending);
|
||||||
|
|
||||||
|
kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu);
|
||||||
|
|
||||||
/* Service the TLB flush request for L2 before switching to L1. */
|
/* Service the TLB flush request for L2 before switching to L1. */
|
||||||
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
|
if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
|
||||||
kvm_vcpu_flush_tlb_current(vcpu);
|
kvm_vcpu_flush_tlb_current(vcpu);
|
||||||
|
@ -7707,6 +7707,8 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = {
|
|||||||
.msr_filter_changed = vmx_msr_filter_changed,
|
.msr_filter_changed = vmx_msr_filter_changed,
|
||||||
.complete_emulated_msr = kvm_complete_insn_gp,
|
.complete_emulated_msr = kvm_complete_insn_gp,
|
||||||
.cpu_dirty_log_size = vmx_cpu_dirty_log_size,
|
.cpu_dirty_log_size = vmx_cpu_dirty_log_size,
|
||||||
|
|
||||||
|
.vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector,
|
||||||
};
|
};
|
||||||
|
|
||||||
static __init int hardware_setup(void)
|
static __init int hardware_setup(void)
|
||||||
|
@ -7976,17 +7976,22 @@ void kvm_arch_exit(void)
|
|||||||
kmem_cache_destroy(x86_fpu_cache);
|
kmem_cache_destroy(x86_fpu_cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_vcpu_halt(struct kvm_vcpu *vcpu)
|
static int __kvm_vcpu_halt(struct kvm_vcpu *vcpu, int state, int reason)
|
||||||
{
|
{
|
||||||
++vcpu->stat.halt_exits;
|
++vcpu->stat.halt_exits;
|
||||||
if (lapic_in_kernel(vcpu)) {
|
if (lapic_in_kernel(vcpu)) {
|
||||||
vcpu->arch.mp_state = KVM_MP_STATE_HALTED;
|
vcpu->arch.mp_state = state;
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
vcpu->run->exit_reason = KVM_EXIT_HLT;
|
vcpu->run->exit_reason = reason;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_vcpu_halt(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
return __kvm_vcpu_halt(vcpu, KVM_MP_STATE_HALTED, KVM_EXIT_HLT);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_vcpu_halt);
|
EXPORT_SYMBOL_GPL(kvm_vcpu_halt);
|
||||||
|
|
||||||
int kvm_emulate_halt(struct kvm_vcpu *vcpu)
|
int kvm_emulate_halt(struct kvm_vcpu *vcpu)
|
||||||
@ -8000,6 +8005,14 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(kvm_emulate_halt);
|
EXPORT_SYMBOL_GPL(kvm_emulate_halt);
|
||||||
|
|
||||||
|
int kvm_emulate_ap_reset_hold(struct kvm_vcpu *vcpu)
|
||||||
|
{
|
||||||
|
int ret = kvm_skip_emulated_instruction(vcpu);
|
||||||
|
|
||||||
|
return __kvm_vcpu_halt(vcpu, KVM_MP_STATE_AP_RESET_HOLD, KVM_EXIT_AP_RESET_HOLD) && ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kvm_emulate_ap_reset_hold);
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
|
static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
|
||||||
unsigned long clock_type)
|
unsigned long clock_type)
|
||||||
@ -8789,7 +8802,9 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
|
|||||||
|
|
||||||
if (kvm_request_pending(vcpu)) {
|
if (kvm_request_pending(vcpu)) {
|
||||||
if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) {
|
if (kvm_check_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu)) {
|
||||||
if (unlikely(!kvm_x86_ops.nested_ops->get_nested_state_pages(vcpu))) {
|
if (WARN_ON_ONCE(!is_guest_mode(vcpu)))
|
||||||
|
;
|
||||||
|
else if (unlikely(!kvm_x86_ops.nested_ops->get_nested_state_pages(vcpu))) {
|
||||||
r = 0;
|
r = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -9094,6 +9109,7 @@ static inline int vcpu_block(struct kvm *kvm, struct kvm_vcpu *vcpu)
|
|||||||
kvm_apic_accept_events(vcpu);
|
kvm_apic_accept_events(vcpu);
|
||||||
switch(vcpu->arch.mp_state) {
|
switch(vcpu->arch.mp_state) {
|
||||||
case KVM_MP_STATE_HALTED:
|
case KVM_MP_STATE_HALTED:
|
||||||
|
case KVM_MP_STATE_AP_RESET_HOLD:
|
||||||
vcpu->arch.pv.pv_unhalted = false;
|
vcpu->arch.pv.pv_unhalted = false;
|
||||||
vcpu->arch.mp_state =
|
vcpu->arch.mp_state =
|
||||||
KVM_MP_STATE_RUNNABLE;
|
KVM_MP_STATE_RUNNABLE;
|
||||||
@ -9520,8 +9536,9 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
|
|||||||
kvm_load_guest_fpu(vcpu);
|
kvm_load_guest_fpu(vcpu);
|
||||||
|
|
||||||
kvm_apic_accept_events(vcpu);
|
kvm_apic_accept_events(vcpu);
|
||||||
if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED &&
|
if ((vcpu->arch.mp_state == KVM_MP_STATE_HALTED ||
|
||||||
vcpu->arch.pv.pv_unhalted)
|
vcpu->arch.mp_state == KVM_MP_STATE_AP_RESET_HOLD) &&
|
||||||
|
vcpu->arch.pv.pv_unhalted)
|
||||||
mp_state->mp_state = KVM_MP_STATE_RUNNABLE;
|
mp_state->mp_state = KVM_MP_STATE_RUNNABLE;
|
||||||
else
|
else
|
||||||
mp_state->mp_state = vcpu->arch.mp_state;
|
mp_state->mp_state = vcpu->arch.mp_state;
|
||||||
@ -10152,6 +10169,7 @@ void kvm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
|
|||||||
kvm_set_segment(vcpu, &cs, VCPU_SREG_CS);
|
kvm_set_segment(vcpu, &cs, VCPU_SREG_CS);
|
||||||
kvm_rip_write(vcpu, 0);
|
kvm_rip_write(vcpu, 0);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kvm_vcpu_deliver_sipi_vector);
|
||||||
|
|
||||||
int kvm_arch_hardware_enable(void)
|
int kvm_arch_hardware_enable(void)
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#define ARMV8_PMU_CYCLE_IDX (ARMV8_PMU_MAX_COUNTERS - 1)
|
#define ARMV8_PMU_CYCLE_IDX (ARMV8_PMU_MAX_COUNTERS - 1)
|
||||||
#define ARMV8_PMU_MAX_COUNTER_PAIRS ((ARMV8_PMU_MAX_COUNTERS + 1) >> 1)
|
#define ARMV8_PMU_MAX_COUNTER_PAIRS ((ARMV8_PMU_MAX_COUNTERS + 1) >> 1)
|
||||||
|
|
||||||
#ifdef CONFIG_KVM_ARM_PMU
|
#ifdef CONFIG_HW_PERF_EVENTS
|
||||||
|
|
||||||
struct kvm_pmc {
|
struct kvm_pmc {
|
||||||
u8 idx; /* index into the pmu->pmc array */
|
u8 idx; /* index into the pmu->pmc array */
|
||||||
|
@ -251,6 +251,7 @@ struct kvm_hyperv_exit {
|
|||||||
#define KVM_EXIT_X86_RDMSR 29
|
#define KVM_EXIT_X86_RDMSR 29
|
||||||
#define KVM_EXIT_X86_WRMSR 30
|
#define KVM_EXIT_X86_WRMSR 30
|
||||||
#define KVM_EXIT_DIRTY_RING_FULL 31
|
#define KVM_EXIT_DIRTY_RING_FULL 31
|
||||||
|
#define KVM_EXIT_AP_RESET_HOLD 32
|
||||||
|
|
||||||
/* For KVM_EXIT_INTERNAL_ERROR */
|
/* For KVM_EXIT_INTERNAL_ERROR */
|
||||||
/* Emulate instruction failed. */
|
/* Emulate instruction failed. */
|
||||||
@ -573,6 +574,7 @@ struct kvm_vapic_addr {
|
|||||||
#define KVM_MP_STATE_CHECK_STOP 6
|
#define KVM_MP_STATE_CHECK_STOP 6
|
||||||
#define KVM_MP_STATE_OPERATING 7
|
#define KVM_MP_STATE_OPERATING 7
|
||||||
#define KVM_MP_STATE_LOAD 8
|
#define KVM_MP_STATE_LOAD 8
|
||||||
|
#define KVM_MP_STATE_AP_RESET_HOLD 9
|
||||||
|
|
||||||
struct kvm_mp_state {
|
struct kvm_mp_state {
|
||||||
__u32 mp_state;
|
__u32 mp_state;
|
||||||
|
@ -33,7 +33,7 @@ ifeq ($(ARCH),s390)
|
|||||||
UNAME_M := s390x
|
UNAME_M := s390x
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c
|
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
|
||||||
LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
|
LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
|
||||||
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
|
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
|
||||||
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
|
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
|
||||||
|
@ -7,23 +7,20 @@
|
|||||||
* Copyright (C) 2019, Google, Inc.
|
* Copyright (C) 2019, Google, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE /* for program_invocation_name */
|
#define _GNU_SOURCE /* for pipe2 */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <asm/unistd.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <linux/bitmap.h>
|
|
||||||
#include <linux/bitops.h>
|
|
||||||
#include <linux/userfaultfd.h>
|
#include <linux/userfaultfd.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
#include "perf_test_util.h"
|
#include "kvm_util.h"
|
||||||
#include "processor.h"
|
|
||||||
#include "test_util.h"
|
#include "test_util.h"
|
||||||
|
#include "perf_test_util.h"
|
||||||
|
#include "guest_modes.h"
|
||||||
|
|
||||||
#ifdef __NR_userfaultfd
|
#ifdef __NR_userfaultfd
|
||||||
|
|
||||||
@ -39,12 +36,14 @@
|
|||||||
#define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__)
|
#define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int nr_vcpus = 1;
|
||||||
|
static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
|
||||||
static char *guest_data_prototype;
|
static char *guest_data_prototype;
|
||||||
|
|
||||||
static void *vcpu_worker(void *data)
|
static void *vcpu_worker(void *data)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct vcpu_args *vcpu_args = (struct vcpu_args *)data;
|
struct perf_test_vcpu_args *vcpu_args = (struct perf_test_vcpu_args *)data;
|
||||||
int vcpu_id = vcpu_args->vcpu_id;
|
int vcpu_id = vcpu_args->vcpu_id;
|
||||||
struct kvm_vm *vm = perf_test_args.vm;
|
struct kvm_vm *vm = perf_test_args.vm;
|
||||||
struct kvm_run *run;
|
struct kvm_run *run;
|
||||||
@ -248,9 +247,14 @@ static int setup_demand_paging(struct kvm_vm *vm,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
struct test_params {
|
||||||
useconds_t uffd_delay)
|
bool use_uffd;
|
||||||
|
useconds_t uffd_delay;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void run_test(enum vm_guest_mode mode, void *arg)
|
||||||
{
|
{
|
||||||
|
struct test_params *p = arg;
|
||||||
pthread_t *vcpu_threads;
|
pthread_t *vcpu_threads;
|
||||||
pthread_t *uffd_handler_threads = NULL;
|
pthread_t *uffd_handler_threads = NULL;
|
||||||
struct uffd_handler_args *uffd_args = NULL;
|
struct uffd_handler_args *uffd_args = NULL;
|
||||||
@ -261,7 +265,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
|||||||
int vcpu_id;
|
int vcpu_id;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size);
|
vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size);
|
||||||
|
|
||||||
perf_test_args.wr_fract = 1;
|
perf_test_args.wr_fract = 1;
|
||||||
|
|
||||||
@ -273,9 +277,9 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
|||||||
vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
|
vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
|
||||||
TEST_ASSERT(vcpu_threads, "Memory allocation failed");
|
TEST_ASSERT(vcpu_threads, "Memory allocation failed");
|
||||||
|
|
||||||
add_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
|
perf_test_setup_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
|
||||||
|
|
||||||
if (use_uffd) {
|
if (p->use_uffd) {
|
||||||
uffd_handler_threads =
|
uffd_handler_threads =
|
||||||
malloc(nr_vcpus * sizeof(*uffd_handler_threads));
|
malloc(nr_vcpus * sizeof(*uffd_handler_threads));
|
||||||
TEST_ASSERT(uffd_handler_threads, "Memory allocation failed");
|
TEST_ASSERT(uffd_handler_threads, "Memory allocation failed");
|
||||||
@ -308,7 +312,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
|||||||
r = setup_demand_paging(vm,
|
r = setup_demand_paging(vm,
|
||||||
&uffd_handler_threads[vcpu_id],
|
&uffd_handler_threads[vcpu_id],
|
||||||
pipefds[vcpu_id * 2],
|
pipefds[vcpu_id * 2],
|
||||||
uffd_delay, &uffd_args[vcpu_id],
|
p->uffd_delay, &uffd_args[vcpu_id],
|
||||||
vcpu_hva, guest_percpu_mem_size);
|
vcpu_hva, guest_percpu_mem_size);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
exit(-r);
|
exit(-r);
|
||||||
@ -339,7 +343,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
|||||||
|
|
||||||
pr_info("All vCPU threads joined\n");
|
pr_info("All vCPU threads joined\n");
|
||||||
|
|
||||||
if (use_uffd) {
|
if (p->use_uffd) {
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
/* Tell the user fault fd handler threads to quit */
|
/* Tell the user fault fd handler threads to quit */
|
||||||
@ -357,43 +361,23 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
|
|||||||
perf_test_args.vcpu_args[0].pages * nr_vcpus /
|
perf_test_args.vcpu_args[0].pages * nr_vcpus /
|
||||||
((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
|
((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
|
||||||
|
|
||||||
ucall_uninit(vm);
|
perf_test_destroy_vm(vm);
|
||||||
kvm_vm_free(vm);
|
|
||||||
|
|
||||||
free(guest_data_prototype);
|
free(guest_data_prototype);
|
||||||
free(vcpu_threads);
|
free(vcpu_threads);
|
||||||
if (use_uffd) {
|
if (p->use_uffd) {
|
||||||
free(uffd_handler_threads);
|
free(uffd_handler_threads);
|
||||||
free(uffd_args);
|
free(uffd_args);
|
||||||
free(pipefds);
|
free(pipefds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct guest_mode {
|
|
||||||
bool supported;
|
|
||||||
bool enabled;
|
|
||||||
};
|
|
||||||
static struct guest_mode guest_modes[NUM_VM_MODES];
|
|
||||||
|
|
||||||
#define guest_mode_init(mode, supported, enabled) ({ \
|
|
||||||
guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
|
|
||||||
})
|
|
||||||
|
|
||||||
static void help(char *name)
|
static void help(char *name)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
puts("");
|
puts("");
|
||||||
printf("usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec]\n"
|
printf("usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec]\n"
|
||||||
" [-b memory] [-v vcpus]\n", name);
|
" [-b memory] [-v vcpus]\n", name);
|
||||||
printf(" -m: specify the guest mode ID to test\n"
|
guest_modes_help();
|
||||||
" (default: test all supported modes)\n"
|
|
||||||
" This option may be used multiple times.\n"
|
|
||||||
" Guest mode IDs:\n");
|
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
|
||||||
printf(" %d: %s%s\n", i, vm_guest_mode_string(i),
|
|
||||||
guest_modes[i].supported ? " (supported)" : "");
|
|
||||||
}
|
|
||||||
printf(" -u: use User Fault FD to handle vCPU page\n"
|
printf(" -u: use User Fault FD to handle vCPU page\n"
|
||||||
" faults.\n");
|
" faults.\n");
|
||||||
printf(" -d: add a delay in usec to the User Fault\n"
|
printf(" -d: add a delay in usec to the User Fault\n"
|
||||||
@ -410,53 +394,22 @@ static void help(char *name)
|
|||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
|
int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
|
||||||
bool mode_selected = false;
|
struct test_params p = {};
|
||||||
unsigned int mode;
|
int opt;
|
||||||
int opt, i;
|
|
||||||
bool use_uffd = false;
|
|
||||||
useconds_t uffd_delay = 0;
|
|
||||||
|
|
||||||
#ifdef __x86_64__
|
guest_modes_append_default();
|
||||||
guest_mode_init(VM_MODE_PXXV48_4K, true, true);
|
|
||||||
#endif
|
|
||||||
#ifdef __aarch64__
|
|
||||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
|
||||||
guest_mode_init(VM_MODE_P40V48_64K, true, true);
|
|
||||||
{
|
|
||||||
unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
|
|
||||||
|
|
||||||
if (limit >= 52)
|
|
||||||
guest_mode_init(VM_MODE_P52V48_64K, true, true);
|
|
||||||
if (limit >= 48) {
|
|
||||||
guest_mode_init(VM_MODE_P48V48_4K, true, true);
|
|
||||||
guest_mode_init(VM_MODE_P48V48_64K, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef __s390x__
|
|
||||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "hm:ud:b:v:")) != -1) {
|
while ((opt = getopt(argc, argv, "hm:ud:b:v:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'm':
|
case 'm':
|
||||||
if (!mode_selected) {
|
guest_modes_cmdline(optarg);
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i)
|
|
||||||
guest_modes[i].enabled = false;
|
|
||||||
mode_selected = true;
|
|
||||||
}
|
|
||||||
mode = strtoul(optarg, NULL, 10);
|
|
||||||
TEST_ASSERT(mode < NUM_VM_MODES,
|
|
||||||
"Guest mode ID %d too big", mode);
|
|
||||||
guest_modes[mode].enabled = true;
|
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
use_uffd = true;
|
p.use_uffd = true;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
uffd_delay = strtoul(optarg, NULL, 0);
|
p.uffd_delay = strtoul(optarg, NULL, 0);
|
||||||
TEST_ASSERT(uffd_delay >= 0,
|
TEST_ASSERT(p.uffd_delay >= 0, "A negative UFFD delay is not supported.");
|
||||||
"A negative UFFD delay is not supported.");
|
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
guest_percpu_mem_size = parse_size(optarg);
|
guest_percpu_mem_size = parse_size(optarg);
|
||||||
@ -473,14 +426,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
for_each_guest_mode(run_test, &p);
|
||||||
if (!guest_modes[i].enabled)
|
|
||||||
continue;
|
|
||||||
TEST_ASSERT(guest_modes[i].supported,
|
|
||||||
"Guest mode ID %d (%s) not supported.",
|
|
||||||
i, vm_guest_mode_string(i));
|
|
||||||
run_test(i, use_uffd, uffd_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -8,29 +8,28 @@
|
|||||||
* Copyright (C) 2020, Google, Inc.
|
* Copyright (C) 2020, Google, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE /* for program_invocation_name */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <linux/bitmap.h>
|
#include <linux/bitmap.h>
|
||||||
#include <linux/bitops.h>
|
|
||||||
|
|
||||||
#include "kvm_util.h"
|
#include "kvm_util.h"
|
||||||
#include "perf_test_util.h"
|
|
||||||
#include "processor.h"
|
|
||||||
#include "test_util.h"
|
#include "test_util.h"
|
||||||
|
#include "perf_test_util.h"
|
||||||
|
#include "guest_modes.h"
|
||||||
|
|
||||||
/* How many host loops to run by default (one KVM_GET_DIRTY_LOG for each loop)*/
|
/* How many host loops to run by default (one KVM_GET_DIRTY_LOG for each loop)*/
|
||||||
#define TEST_HOST_LOOP_N 2UL
|
#define TEST_HOST_LOOP_N 2UL
|
||||||
|
|
||||||
|
static int nr_vcpus = 1;
|
||||||
|
static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
|
||||||
|
|
||||||
/* Host variables */
|
/* Host variables */
|
||||||
static u64 dirty_log_manual_caps;
|
static u64 dirty_log_manual_caps;
|
||||||
static bool host_quit;
|
static bool host_quit;
|
||||||
static uint64_t iteration;
|
static uint64_t iteration;
|
||||||
static uint64_t vcpu_last_completed_iteration[MAX_VCPUS];
|
static uint64_t vcpu_last_completed_iteration[KVM_MAX_VCPUS];
|
||||||
|
|
||||||
static void *vcpu_worker(void *data)
|
static void *vcpu_worker(void *data)
|
||||||
{
|
{
|
||||||
@ -42,7 +41,7 @@ static void *vcpu_worker(void *data)
|
|||||||
struct timespec ts_diff;
|
struct timespec ts_diff;
|
||||||
struct timespec total = (struct timespec){0};
|
struct timespec total = (struct timespec){0};
|
||||||
struct timespec avg;
|
struct timespec avg;
|
||||||
struct vcpu_args *vcpu_args = (struct vcpu_args *)data;
|
struct perf_test_vcpu_args *vcpu_args = (struct perf_test_vcpu_args *)data;
|
||||||
int vcpu_id = vcpu_args->vcpu_id;
|
int vcpu_id = vcpu_args->vcpu_id;
|
||||||
|
|
||||||
vcpu_args_set(vm, vcpu_id, 1, vcpu_id);
|
vcpu_args_set(vm, vcpu_id, 1, vcpu_id);
|
||||||
@ -89,9 +88,15 @@ static void *vcpu_worker(void *data)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
struct test_params {
|
||||||
uint64_t phys_offset, int wr_fract)
|
unsigned long iterations;
|
||||||
|
uint64_t phys_offset;
|
||||||
|
int wr_fract;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void run_test(enum vm_guest_mode mode, void *arg)
|
||||||
{
|
{
|
||||||
|
struct test_params *p = arg;
|
||||||
pthread_t *vcpu_threads;
|
pthread_t *vcpu_threads;
|
||||||
struct kvm_vm *vm;
|
struct kvm_vm *vm;
|
||||||
unsigned long *bmap;
|
unsigned long *bmap;
|
||||||
@ -106,9 +111,9 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
struct kvm_enable_cap cap = {};
|
struct kvm_enable_cap cap = {};
|
||||||
struct timespec clear_dirty_log_total = (struct timespec){0};
|
struct timespec clear_dirty_log_total = (struct timespec){0};
|
||||||
|
|
||||||
vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size);
|
vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size);
|
||||||
|
|
||||||
perf_test_args.wr_fract = wr_fract;
|
perf_test_args.wr_fract = p->wr_fract;
|
||||||
|
|
||||||
guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm_get_page_shift(vm);
|
guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm_get_page_shift(vm);
|
||||||
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
|
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
|
||||||
@ -124,7 +129,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
|
vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
|
||||||
TEST_ASSERT(vcpu_threads, "Memory allocation failed");
|
TEST_ASSERT(vcpu_threads, "Memory allocation failed");
|
||||||
|
|
||||||
add_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
|
perf_test_setup_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
|
||||||
|
|
||||||
sync_global_to_guest(vm, perf_test_args);
|
sync_global_to_guest(vm, perf_test_args);
|
||||||
|
|
||||||
@ -150,13 +155,13 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
|
|
||||||
/* Enable dirty logging */
|
/* Enable dirty logging */
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX,
|
vm_mem_region_set_flags(vm, PERF_TEST_MEM_SLOT_INDEX,
|
||||||
KVM_MEM_LOG_DIRTY_PAGES);
|
KVM_MEM_LOG_DIRTY_PAGES);
|
||||||
ts_diff = timespec_diff_now(start);
|
ts_diff = timespec_diff_now(start);
|
||||||
pr_info("Enabling dirty logging time: %ld.%.9lds\n\n",
|
pr_info("Enabling dirty logging time: %ld.%.9lds\n\n",
|
||||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||||
|
|
||||||
while (iteration < iterations) {
|
while (iteration < p->iterations) {
|
||||||
/*
|
/*
|
||||||
* Incrementing the iteration number will start the vCPUs
|
* Incrementing the iteration number will start the vCPUs
|
||||||
* dirtying memory again.
|
* dirtying memory again.
|
||||||
@ -177,7 +182,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
iteration, ts_diff.tv_sec, ts_diff.tv_nsec);
|
iteration, ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap);
|
kvm_vm_get_dirty_log(vm, PERF_TEST_MEM_SLOT_INDEX, bmap);
|
||||||
|
|
||||||
ts_diff = timespec_diff_now(start);
|
ts_diff = timespec_diff_now(start);
|
||||||
get_dirty_log_total = timespec_add(get_dirty_log_total,
|
get_dirty_log_total = timespec_add(get_dirty_log_total,
|
||||||
@ -187,7 +192,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
|
|
||||||
if (dirty_log_manual_caps) {
|
if (dirty_log_manual_caps) {
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0,
|
kvm_vm_clear_dirty_log(vm, PERF_TEST_MEM_SLOT_INDEX, bmap, 0,
|
||||||
host_num_pages);
|
host_num_pages);
|
||||||
|
|
||||||
ts_diff = timespec_diff_now(start);
|
ts_diff = timespec_diff_now(start);
|
||||||
@ -205,43 +210,30 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
|
|
||||||
/* Disable dirty logging */
|
/* Disable dirty logging */
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX, 0);
|
vm_mem_region_set_flags(vm, PERF_TEST_MEM_SLOT_INDEX, 0);
|
||||||
ts_diff = timespec_diff_now(start);
|
ts_diff = timespec_diff_now(start);
|
||||||
pr_info("Disabling dirty logging time: %ld.%.9lds\n",
|
pr_info("Disabling dirty logging time: %ld.%.9lds\n",
|
||||||
ts_diff.tv_sec, ts_diff.tv_nsec);
|
ts_diff.tv_sec, ts_diff.tv_nsec);
|
||||||
|
|
||||||
avg = timespec_div(get_dirty_log_total, iterations);
|
avg = timespec_div(get_dirty_log_total, p->iterations);
|
||||||
pr_info("Get dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
|
pr_info("Get dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
|
||||||
iterations, get_dirty_log_total.tv_sec,
|
p->iterations, get_dirty_log_total.tv_sec,
|
||||||
get_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
|
get_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
|
||||||
|
|
||||||
if (dirty_log_manual_caps) {
|
if (dirty_log_manual_caps) {
|
||||||
avg = timespec_div(clear_dirty_log_total, iterations);
|
avg = timespec_div(clear_dirty_log_total, p->iterations);
|
||||||
pr_info("Clear dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
|
pr_info("Clear dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
|
||||||
iterations, clear_dirty_log_total.tv_sec,
|
p->iterations, clear_dirty_log_total.tv_sec,
|
||||||
clear_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
|
clear_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(bmap);
|
free(bmap);
|
||||||
free(vcpu_threads);
|
free(vcpu_threads);
|
||||||
ucall_uninit(vm);
|
perf_test_destroy_vm(vm);
|
||||||
kvm_vm_free(vm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct guest_mode {
|
|
||||||
bool supported;
|
|
||||||
bool enabled;
|
|
||||||
};
|
|
||||||
static struct guest_mode guest_modes[NUM_VM_MODES];
|
|
||||||
|
|
||||||
#define guest_mode_init(mode, supported, enabled) ({ \
|
|
||||||
guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
|
|
||||||
})
|
|
||||||
|
|
||||||
static void help(char *name)
|
static void help(char *name)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
puts("");
|
puts("");
|
||||||
printf("usage: %s [-h] [-i iterations] [-p offset] "
|
printf("usage: %s [-h] [-i iterations] [-p offset] "
|
||||||
"[-m mode] [-b vcpu bytes] [-v vcpus]\n", name);
|
"[-m mode] [-b vcpu bytes] [-v vcpus]\n", name);
|
||||||
@ -250,14 +242,7 @@ static void help(char *name)
|
|||||||
TEST_HOST_LOOP_N);
|
TEST_HOST_LOOP_N);
|
||||||
printf(" -p: specify guest physical test memory offset\n"
|
printf(" -p: specify guest physical test memory offset\n"
|
||||||
" Warning: a low offset can conflict with the loaded test code.\n");
|
" Warning: a low offset can conflict with the loaded test code.\n");
|
||||||
printf(" -m: specify the guest mode ID to test "
|
guest_modes_help();
|
||||||
"(default: test all supported modes)\n"
|
|
||||||
" This option may be used multiple times.\n"
|
|
||||||
" Guest mode IDs:\n");
|
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
|
||||||
printf(" %d: %s%s\n", i, vm_guest_mode_string(i),
|
|
||||||
guest_modes[i].supported ? " (supported)" : "");
|
|
||||||
}
|
|
||||||
printf(" -b: specify the size of the memory region which should be\n"
|
printf(" -b: specify the size of the memory region which should be\n"
|
||||||
" dirtied by each vCPU. e.g. 10M or 3G.\n"
|
" dirtied by each vCPU. e.g. 10M or 3G.\n"
|
||||||
" (default: 1G)\n");
|
" (default: 1G)\n");
|
||||||
@ -272,74 +257,43 @@ static void help(char *name)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
unsigned long iterations = TEST_HOST_LOOP_N;
|
int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
|
||||||
bool mode_selected = false;
|
struct test_params p = {
|
||||||
uint64_t phys_offset = 0;
|
.iterations = TEST_HOST_LOOP_N,
|
||||||
unsigned int mode;
|
.wr_fract = 1,
|
||||||
int opt, i;
|
};
|
||||||
int wr_fract = 1;
|
int opt;
|
||||||
|
|
||||||
dirty_log_manual_caps =
|
dirty_log_manual_caps =
|
||||||
kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
|
||||||
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
|
||||||
KVM_DIRTY_LOG_INITIALLY_SET);
|
KVM_DIRTY_LOG_INITIALLY_SET);
|
||||||
|
|
||||||
#ifdef __x86_64__
|
guest_modes_append_default();
|
||||||
guest_mode_init(VM_MODE_PXXV48_4K, true, true);
|
|
||||||
#endif
|
|
||||||
#ifdef __aarch64__
|
|
||||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
|
||||||
guest_mode_init(VM_MODE_P40V48_64K, true, true);
|
|
||||||
|
|
||||||
{
|
|
||||||
unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
|
|
||||||
|
|
||||||
if (limit >= 52)
|
|
||||||
guest_mode_init(VM_MODE_P52V48_64K, true, true);
|
|
||||||
if (limit >= 48) {
|
|
||||||
guest_mode_init(VM_MODE_P48V48_4K, true, true);
|
|
||||||
guest_mode_init(VM_MODE_P48V48_64K, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef __s390x__
|
|
||||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "hi:p:m:b:f:v:")) != -1) {
|
while ((opt = getopt(argc, argv, "hi:p:m:b:f:v:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'i':
|
case 'i':
|
||||||
iterations = strtol(optarg, NULL, 10);
|
p.iterations = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
phys_offset = strtoull(optarg, NULL, 0);
|
p.phys_offset = strtoull(optarg, NULL, 0);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
if (!mode_selected) {
|
guest_modes_cmdline(optarg);
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i)
|
|
||||||
guest_modes[i].enabled = false;
|
|
||||||
mode_selected = true;
|
|
||||||
}
|
|
||||||
mode = strtoul(optarg, NULL, 10);
|
|
||||||
TEST_ASSERT(mode < NUM_VM_MODES,
|
|
||||||
"Guest mode ID %d too big", mode);
|
|
||||||
guest_modes[mode].enabled = true;
|
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
guest_percpu_mem_size = parse_size(optarg);
|
guest_percpu_mem_size = parse_size(optarg);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
wr_fract = atoi(optarg);
|
p.wr_fract = atoi(optarg);
|
||||||
TEST_ASSERT(wr_fract >= 1,
|
TEST_ASSERT(p.wr_fract >= 1,
|
||||||
"Write fraction cannot be less than one");
|
"Write fraction cannot be less than one");
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
nr_vcpus = atoi(optarg);
|
nr_vcpus = atoi(optarg);
|
||||||
TEST_ASSERT(nr_vcpus > 0,
|
TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
|
||||||
"Must have a positive number of vCPUs");
|
"Invalid number of vcpus, must be between 1 and %d", max_vcpus);
|
||||||
TEST_ASSERT(nr_vcpus <= MAX_VCPUS,
|
|
||||||
"This test does not currently support\n"
|
|
||||||
"more than %d vCPUs.", MAX_VCPUS);
|
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
default:
|
default:
|
||||||
@ -348,18 +302,11 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT(iterations >= 2, "The test should have at least two iterations");
|
TEST_ASSERT(p.iterations >= 2, "The test should have at least two iterations");
|
||||||
|
|
||||||
pr_info("Test iterations: %"PRIu64"\n", iterations);
|
pr_info("Test iterations: %"PRIu64"\n", p.iterations);
|
||||||
|
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
for_each_guest_mode(run_test, &p);
|
||||||
if (!guest_modes[i].enabled)
|
|
||||||
continue;
|
|
||||||
TEST_ASSERT(guest_modes[i].supported,
|
|
||||||
"Guest mode ID %d (%s) not supported.",
|
|
||||||
i, vm_guest_mode_string(i));
|
|
||||||
run_test(i, iterations, phys_offset, wr_fract);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -20,8 +18,9 @@
|
|||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <asm/barrier.h>
|
#include <asm/barrier.h>
|
||||||
|
|
||||||
#include "test_util.h"
|
|
||||||
#include "kvm_util.h"
|
#include "kvm_util.h"
|
||||||
|
#include "test_util.h"
|
||||||
|
#include "guest_modes.h"
|
||||||
#include "processor.h"
|
#include "processor.h"
|
||||||
|
|
||||||
#define VCPU_ID 1
|
#define VCPU_ID 1
|
||||||
@ -673,9 +672,15 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid,
|
|||||||
#define DIRTY_MEM_BITS 30 /* 1G */
|
#define DIRTY_MEM_BITS 30 /* 1G */
|
||||||
#define PAGE_SHIFT_4K 12
|
#define PAGE_SHIFT_4K 12
|
||||||
|
|
||||||
static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
struct test_params {
|
||||||
unsigned long interval, uint64_t phys_offset)
|
unsigned long iterations;
|
||||||
|
unsigned long interval;
|
||||||
|
uint64_t phys_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void run_test(enum vm_guest_mode mode, void *arg)
|
||||||
{
|
{
|
||||||
|
struct test_params *p = arg;
|
||||||
struct kvm_vm *vm;
|
struct kvm_vm *vm;
|
||||||
unsigned long *bmap;
|
unsigned long *bmap;
|
||||||
|
|
||||||
@ -709,12 +714,12 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
host_page_size = getpagesize();
|
host_page_size = getpagesize();
|
||||||
host_num_pages = vm_num_host_pages(mode, guest_num_pages);
|
host_num_pages = vm_num_host_pages(mode, guest_num_pages);
|
||||||
|
|
||||||
if (!phys_offset) {
|
if (!p->phys_offset) {
|
||||||
guest_test_phys_mem = (vm_get_max_gfn(vm) -
|
guest_test_phys_mem = (vm_get_max_gfn(vm) -
|
||||||
guest_num_pages) * guest_page_size;
|
guest_num_pages) * guest_page_size;
|
||||||
guest_test_phys_mem &= ~(host_page_size - 1);
|
guest_test_phys_mem &= ~(host_page_size - 1);
|
||||||
} else {
|
} else {
|
||||||
guest_test_phys_mem = phys_offset;
|
guest_test_phys_mem = p->phys_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __s390x__
|
#ifdef __s390x__
|
||||||
@ -758,9 +763,9 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
|
|
||||||
pthread_create(&vcpu_thread, NULL, vcpu_worker, vm);
|
pthread_create(&vcpu_thread, NULL, vcpu_worker, vm);
|
||||||
|
|
||||||
while (iteration < iterations) {
|
while (iteration < p->iterations) {
|
||||||
/* Give the vcpu thread some time to dirty some pages */
|
/* Give the vcpu thread some time to dirty some pages */
|
||||||
usleep(interval * 1000);
|
usleep(p->interval * 1000);
|
||||||
log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX,
|
log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX,
|
||||||
bmap, host_num_pages);
|
bmap, host_num_pages);
|
||||||
vm_dirty_log_verify(mode, bmap);
|
vm_dirty_log_verify(mode, bmap);
|
||||||
@ -783,20 +788,8 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
|
|||||||
kvm_vm_free(vm);
|
kvm_vm_free(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct guest_mode {
|
|
||||||
bool supported;
|
|
||||||
bool enabled;
|
|
||||||
};
|
|
||||||
static struct guest_mode guest_modes[NUM_VM_MODES];
|
|
||||||
|
|
||||||
#define guest_mode_init(mode, supported, enabled) ({ \
|
|
||||||
guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
|
|
||||||
})
|
|
||||||
|
|
||||||
static void help(char *name)
|
static void help(char *name)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
|
|
||||||
puts("");
|
puts("");
|
||||||
printf("usage: %s [-h] [-i iterations] [-I interval] "
|
printf("usage: %s [-h] [-i iterations] [-I interval] "
|
||||||
"[-p offset] [-m mode]\n", name);
|
"[-p offset] [-m mode]\n", name);
|
||||||
@ -813,51 +806,23 @@ static void help(char *name)
|
|||||||
printf(" -M: specify the host logging mode "
|
printf(" -M: specify the host logging mode "
|
||||||
"(default: run all log modes). Supported modes: \n\t");
|
"(default: run all log modes). Supported modes: \n\t");
|
||||||
log_modes_dump();
|
log_modes_dump();
|
||||||
printf(" -m: specify the guest mode ID to test "
|
guest_modes_help();
|
||||||
"(default: test all supported modes)\n"
|
|
||||||
" This option may be used multiple times.\n"
|
|
||||||
" Guest mode IDs:\n");
|
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
|
||||||
printf(" %d: %s%s\n", i, vm_guest_mode_string(i),
|
|
||||||
guest_modes[i].supported ? " (supported)" : "");
|
|
||||||
}
|
|
||||||
puts("");
|
puts("");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
unsigned long iterations = TEST_HOST_LOOP_N;
|
struct test_params p = {
|
||||||
unsigned long interval = TEST_HOST_LOOP_INTERVAL;
|
.iterations = TEST_HOST_LOOP_N,
|
||||||
bool mode_selected = false;
|
.interval = TEST_HOST_LOOP_INTERVAL,
|
||||||
uint64_t phys_offset = 0;
|
};
|
||||||
unsigned int mode;
|
int opt, i;
|
||||||
int opt, i, j;
|
|
||||||
|
|
||||||
sem_init(&dirty_ring_vcpu_stop, 0, 0);
|
sem_init(&dirty_ring_vcpu_stop, 0, 0);
|
||||||
sem_init(&dirty_ring_vcpu_cont, 0, 0);
|
sem_init(&dirty_ring_vcpu_cont, 0, 0);
|
||||||
|
|
||||||
#ifdef __x86_64__
|
guest_modes_append_default();
|
||||||
guest_mode_init(VM_MODE_PXXV48_4K, true, true);
|
|
||||||
#endif
|
|
||||||
#ifdef __aarch64__
|
|
||||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
|
||||||
guest_mode_init(VM_MODE_P40V48_64K, true, true);
|
|
||||||
|
|
||||||
{
|
|
||||||
unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
|
|
||||||
|
|
||||||
if (limit >= 52)
|
|
||||||
guest_mode_init(VM_MODE_P52V48_64K, true, true);
|
|
||||||
if (limit >= 48) {
|
|
||||||
guest_mode_init(VM_MODE_P48V48_4K, true, true);
|
|
||||||
guest_mode_init(VM_MODE_P48V48_64K, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef __s390x__
|
|
||||||
guest_mode_init(VM_MODE_P40V48_4K, true, true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "c:hi:I:p:m:M:")) != -1) {
|
while ((opt = getopt(argc, argv, "c:hi:I:p:m:M:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
@ -865,24 +830,16 @@ int main(int argc, char *argv[])
|
|||||||
test_dirty_ring_count = strtol(optarg, NULL, 10);
|
test_dirty_ring_count = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
iterations = strtol(optarg, NULL, 10);
|
p.iterations = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
interval = strtol(optarg, NULL, 10);
|
p.interval = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
phys_offset = strtoull(optarg, NULL, 0);
|
p.phys_offset = strtoull(optarg, NULL, 0);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
if (!mode_selected) {
|
guest_modes_cmdline(optarg);
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i)
|
|
||||||
guest_modes[i].enabled = false;
|
|
||||||
mode_selected = true;
|
|
||||||
}
|
|
||||||
mode = strtoul(optarg, NULL, 10);
|
|
||||||
TEST_ASSERT(mode < NUM_VM_MODES,
|
|
||||||
"Guest mode ID %d too big", mode);
|
|
||||||
guest_modes[mode].enabled = true;
|
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
if (!strcmp(optarg, "all")) {
|
if (!strcmp(optarg, "all")) {
|
||||||
@ -911,32 +868,24 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT(iterations > 2, "Iterations must be greater than two");
|
TEST_ASSERT(p.iterations > 2, "Iterations must be greater than two");
|
||||||
TEST_ASSERT(interval > 0, "Interval must be greater than zero");
|
TEST_ASSERT(p.interval > 0, "Interval must be greater than zero");
|
||||||
|
|
||||||
pr_info("Test iterations: %"PRIu64", interval: %"PRIu64" (ms)\n",
|
pr_info("Test iterations: %"PRIu64", interval: %"PRIu64" (ms)\n",
|
||||||
iterations, interval);
|
p.iterations, p.interval);
|
||||||
|
|
||||||
srandom(time(0));
|
srandom(time(0));
|
||||||
|
|
||||||
for (i = 0; i < NUM_VM_MODES; ++i) {
|
if (host_log_mode_option == LOG_MODE_ALL) {
|
||||||
if (!guest_modes[i].enabled)
|
/* Run each log mode */
|
||||||
continue;
|
for (i = 0; i < LOG_MODE_NUM; i++) {
|
||||||
TEST_ASSERT(guest_modes[i].supported,
|
pr_info("Testing Log Mode '%s'\n", log_modes[i].name);
|
||||||
"Guest mode ID %d (%s) not supported.",
|
host_log_mode = i;
|
||||||
i, vm_guest_mode_string(i));
|
for_each_guest_mode(run_test, &p);
|
||||||
if (host_log_mode_option == LOG_MODE_ALL) {
|
|
||||||
/* Run each log mode */
|
|
||||||
for (j = 0; j < LOG_MODE_NUM; j++) {
|
|
||||||
pr_info("Testing Log Mode '%s'\n",
|
|
||||||
log_modes[j].name);
|
|
||||||
host_log_mode = j;
|
|
||||||
run_test(i, iterations, interval, phys_offset);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
host_log_mode = host_log_mode_option;
|
|
||||||
run_test(i, iterations, interval, phys_offset);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
host_log_mode = host_log_mode_option;
|
||||||
|
for_each_guest_mode(run_test, &p);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
21
tools/testing/selftests/kvm/include/guest_modes.h
Normal file
21
tools/testing/selftests/kvm/include/guest_modes.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020, Red Hat, Inc.
|
||||||
|
*/
|
||||||
|
#include "kvm_util.h"
|
||||||
|
|
||||||
|
struct guest_mode {
|
||||||
|
bool supported;
|
||||||
|
bool enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct guest_mode guest_modes[NUM_VM_MODES];
|
||||||
|
|
||||||
|
#define guest_mode_append(mode, supported, enabled) ({ \
|
||||||
|
guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
|
||||||
|
})
|
||||||
|
|
||||||
|
void guest_modes_append_default(void);
|
||||||
|
void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg);
|
||||||
|
void guest_modes_help(void);
|
||||||
|
void guest_modes_cmdline(const char *arg);
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "sparsebit.h"
|
#include "sparsebit.h"
|
||||||
|
|
||||||
|
#define KVM_MAX_VCPUS 512
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callers of kvm_util only have an incomplete/opaque description of the
|
* Callers of kvm_util only have an incomplete/opaque description of the
|
||||||
@ -70,6 +71,14 @@ enum vm_guest_mode {
|
|||||||
#define vm_guest_mode_string(m) vm_guest_mode_string[m]
|
#define vm_guest_mode_string(m) vm_guest_mode_string[m]
|
||||||
extern const char * const vm_guest_mode_string[];
|
extern const char * const vm_guest_mode_string[];
|
||||||
|
|
||||||
|
struct vm_guest_mode_params {
|
||||||
|
unsigned int pa_bits;
|
||||||
|
unsigned int va_bits;
|
||||||
|
unsigned int page_size;
|
||||||
|
unsigned int page_shift;
|
||||||
|
};
|
||||||
|
extern const struct vm_guest_mode_params vm_guest_mode_params[];
|
||||||
|
|
||||||
enum vm_mem_backing_src_type {
|
enum vm_mem_backing_src_type {
|
||||||
VM_MEM_SRC_ANONYMOUS,
|
VM_MEM_SRC_ANONYMOUS,
|
||||||
VM_MEM_SRC_ANONYMOUS_THP,
|
VM_MEM_SRC_ANONYMOUS_THP,
|
||||||
|
@ -9,38 +9,15 @@
|
|||||||
#define SELFTEST_KVM_PERF_TEST_UTIL_H
|
#define SELFTEST_KVM_PERF_TEST_UTIL_H
|
||||||
|
|
||||||
#include "kvm_util.h"
|
#include "kvm_util.h"
|
||||||
#include "processor.h"
|
|
||||||
|
|
||||||
#define MAX_VCPUS 512
|
|
||||||
|
|
||||||
#define PAGE_SHIFT_4K 12
|
|
||||||
#define PTES_PER_4K_PT 512
|
|
||||||
|
|
||||||
#define TEST_MEM_SLOT_INDEX 1
|
|
||||||
|
|
||||||
/* Default guest test virtual memory offset */
|
/* Default guest test virtual memory offset */
|
||||||
#define DEFAULT_GUEST_TEST_MEM 0xc0000000
|
#define DEFAULT_GUEST_TEST_MEM 0xc0000000
|
||||||
|
|
||||||
#define DEFAULT_PER_VCPU_MEM_SIZE (1 << 30) /* 1G */
|
#define DEFAULT_PER_VCPU_MEM_SIZE (1 << 30) /* 1G */
|
||||||
|
|
||||||
/*
|
#define PERF_TEST_MEM_SLOT_INDEX 1
|
||||||
* Guest physical memory offset of the testing memory slot.
|
|
||||||
* This will be set to the topmost valid physical address minus
|
|
||||||
* the test memory size.
|
|
||||||
*/
|
|
||||||
static uint64_t guest_test_phys_mem;
|
|
||||||
|
|
||||||
/*
|
struct perf_test_vcpu_args {
|
||||||
* Guest virtual memory offset of the testing memory slot.
|
|
||||||
* Must not conflict with identity mapped test code.
|
|
||||||
*/
|
|
||||||
static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
|
|
||||||
static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
|
|
||||||
|
|
||||||
/* Number of VCPUs for the test */
|
|
||||||
static int nr_vcpus = 1;
|
|
||||||
|
|
||||||
struct vcpu_args {
|
|
||||||
uint64_t gva;
|
uint64_t gva;
|
||||||
uint64_t pages;
|
uint64_t pages;
|
||||||
|
|
||||||
@ -54,141 +31,21 @@ struct perf_test_args {
|
|||||||
uint64_t guest_page_size;
|
uint64_t guest_page_size;
|
||||||
int wr_fract;
|
int wr_fract;
|
||||||
|
|
||||||
struct vcpu_args vcpu_args[MAX_VCPUS];
|
struct perf_test_vcpu_args vcpu_args[KVM_MAX_VCPUS];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct perf_test_args perf_test_args;
|
extern struct perf_test_args perf_test_args;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Continuously write to the first 8 bytes of each page in the
|
* Guest physical memory offset of the testing memory slot.
|
||||||
* specified region.
|
* This will be set to the topmost valid physical address minus
|
||||||
|
* the test memory size.
|
||||||
*/
|
*/
|
||||||
static void guest_code(uint32_t vcpu_id)
|
extern uint64_t guest_test_phys_mem;
|
||||||
{
|
|
||||||
struct vcpu_args *vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
|
|
||||||
uint64_t gva;
|
|
||||||
uint64_t pages;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Make sure vCPU args data structure is not corrupt. */
|
struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
|
||||||
GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id);
|
uint64_t vcpu_memory_bytes);
|
||||||
|
void perf_test_destroy_vm(struct kvm_vm *vm);
|
||||||
gva = vcpu_args->gva;
|
void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes);
|
||||||
pages = vcpu_args->pages;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
for (i = 0; i < pages; i++) {
|
|
||||||
uint64_t addr = gva + (i * perf_test_args.guest_page_size);
|
|
||||||
|
|
||||||
if (i % perf_test_args.wr_fract == 0)
|
|
||||||
*(uint64_t *)addr = 0x0123456789ABCDEF;
|
|
||||||
else
|
|
||||||
READ_ONCE(*(uint64_t *)addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
GUEST_SYNC(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kvm_vm *create_vm(enum vm_guest_mode mode, int vcpus,
|
|
||||||
uint64_t vcpu_memory_bytes)
|
|
||||||
{
|
|
||||||
struct kvm_vm *vm;
|
|
||||||
uint64_t pages = DEFAULT_GUEST_PHY_PAGES;
|
|
||||||
uint64_t guest_num_pages;
|
|
||||||
|
|
||||||
/* Account for a few pages per-vCPU for stacks */
|
|
||||||
pages += DEFAULT_STACK_PGS * vcpus;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reserve twice the ammount of memory needed to map the test region and
|
|
||||||
* the page table / stacks region, at 4k, for page tables. Do the
|
|
||||||
* calculation with 4K page size: the smallest of all archs. (e.g., 64K
|
|
||||||
* page size guest will need even less memory for page tables).
|
|
||||||
*/
|
|
||||||
pages += (2 * pages) / PTES_PER_4K_PT;
|
|
||||||
pages += ((2 * vcpus * vcpu_memory_bytes) >> PAGE_SHIFT_4K) /
|
|
||||||
PTES_PER_4K_PT;
|
|
||||||
pages = vm_adjust_num_guest_pages(mode, pages);
|
|
||||||
|
|
||||||
pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
|
|
||||||
|
|
||||||
vm = vm_create(mode, pages, O_RDWR);
|
|
||||||
kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
|
|
||||||
#ifdef __x86_64__
|
|
||||||
vm_create_irqchip(vm);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
perf_test_args.vm = vm;
|
|
||||||
perf_test_args.guest_page_size = vm_get_page_size(vm);
|
|
||||||
perf_test_args.host_page_size = getpagesize();
|
|
||||||
|
|
||||||
TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0,
|
|
||||||
"Guest memory size is not guest page size aligned.");
|
|
||||||
|
|
||||||
guest_num_pages = (vcpus * vcpu_memory_bytes) /
|
|
||||||
perf_test_args.guest_page_size;
|
|
||||||
guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there should be more memory in the guest test region than there
|
|
||||||
* can be pages in the guest, it will definitely cause problems.
|
|
||||||
*/
|
|
||||||
TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm),
|
|
||||||
"Requested more guest memory than address space allows.\n"
|
|
||||||
" guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n",
|
|
||||||
guest_num_pages, vm_get_max_gfn(vm), vcpus,
|
|
||||||
vcpu_memory_bytes);
|
|
||||||
|
|
||||||
TEST_ASSERT(vcpu_memory_bytes % perf_test_args.host_page_size == 0,
|
|
||||||
"Guest memory size is not host page size aligned.");
|
|
||||||
|
|
||||||
guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) *
|
|
||||||
perf_test_args.guest_page_size;
|
|
||||||
guest_test_phys_mem &= ~(perf_test_args.host_page_size - 1);
|
|
||||||
|
|
||||||
#ifdef __s390x__
|
|
||||||
/* Align to 1M (segment size) */
|
|
||||||
guest_test_phys_mem &= ~((1 << 20) - 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem);
|
|
||||||
|
|
||||||
/* Add an extra memory slot for testing */
|
|
||||||
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
|
|
||||||
guest_test_phys_mem,
|
|
||||||
TEST_MEM_SLOT_INDEX,
|
|
||||||
guest_num_pages, 0);
|
|
||||||
|
|
||||||
/* Do mapping for the demand paging memory slot */
|
|
||||||
virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0);
|
|
||||||
|
|
||||||
ucall_init(vm, NULL);
|
|
||||||
|
|
||||||
return vm;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void add_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes)
|
|
||||||
{
|
|
||||||
vm_paddr_t vcpu_gpa;
|
|
||||||
struct vcpu_args *vcpu_args;
|
|
||||||
int vcpu_id;
|
|
||||||
|
|
||||||
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
|
|
||||||
vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
|
|
||||||
|
|
||||||
vm_vcpu_add_default(vm, vcpu_id, guest_code);
|
|
||||||
|
|
||||||
vcpu_args->vcpu_id = vcpu_id;
|
|
||||||
vcpu_args->gva = guest_test_virt_mem +
|
|
||||||
(vcpu_id * vcpu_memory_bytes);
|
|
||||||
vcpu_args->pages = vcpu_memory_bytes /
|
|
||||||
perf_test_args.guest_page_size;
|
|
||||||
|
|
||||||
vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes);
|
|
||||||
pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n",
|
|
||||||
vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */
|
#endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */
|
||||||
|
70
tools/testing/selftests/kvm/lib/guest_modes.c
Normal file
70
tools/testing/selftests/kvm/lib/guest_modes.c
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020, Red Hat, Inc.
|
||||||
|
*/
|
||||||
|
#include "guest_modes.h"
|
||||||
|
|
||||||
|
struct guest_mode guest_modes[NUM_VM_MODES];
|
||||||
|
|
||||||
|
void guest_modes_append_default(void)
|
||||||
|
{
|
||||||
|
guest_mode_append(VM_MODE_DEFAULT, true, true);
|
||||||
|
|
||||||
|
#ifdef __aarch64__
|
||||||
|
guest_mode_append(VM_MODE_P40V48_64K, true, true);
|
||||||
|
{
|
||||||
|
unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
|
||||||
|
if (limit >= 52)
|
||||||
|
guest_mode_append(VM_MODE_P52V48_64K, true, true);
|
||||||
|
if (limit >= 48) {
|
||||||
|
guest_mode_append(VM_MODE_P48V48_4K, true, true);
|
||||||
|
guest_mode_append(VM_MODE_P48V48_64K, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_VM_MODES; ++i) {
|
||||||
|
if (!guest_modes[i].enabled)
|
||||||
|
continue;
|
||||||
|
TEST_ASSERT(guest_modes[i].supported,
|
||||||
|
"Guest mode ID %d (%s) not supported.",
|
||||||
|
i, vm_guest_mode_string(i));
|
||||||
|
func(i, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void guest_modes_help(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf(" -m: specify the guest mode ID to test\n"
|
||||||
|
" (default: test all supported modes)\n"
|
||||||
|
" This option may be used multiple times.\n"
|
||||||
|
" Guest mode IDs:\n");
|
||||||
|
for (i = 0; i < NUM_VM_MODES; ++i) {
|
||||||
|
printf(" %d: %s%s\n", i, vm_guest_mode_string(i),
|
||||||
|
guest_modes[i].supported ? " (supported)" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void guest_modes_cmdline(const char *arg)
|
||||||
|
{
|
||||||
|
static bool mode_selected;
|
||||||
|
unsigned int mode;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!mode_selected) {
|
||||||
|
for (i = 0; i < NUM_VM_MODES; ++i)
|
||||||
|
guest_modes[i].enabled = false;
|
||||||
|
mode_selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = strtoul(optarg, NULL, 10);
|
||||||
|
TEST_ASSERT(mode < NUM_VM_MODES, "Guest mode ID %d too big", mode);
|
||||||
|
guest_modes[mode].enabled = true;
|
||||||
|
}
|
@ -153,14 +153,7 @@ const char * const vm_guest_mode_string[] = {
|
|||||||
_Static_assert(sizeof(vm_guest_mode_string)/sizeof(char *) == NUM_VM_MODES,
|
_Static_assert(sizeof(vm_guest_mode_string)/sizeof(char *) == NUM_VM_MODES,
|
||||||
"Missing new mode strings?");
|
"Missing new mode strings?");
|
||||||
|
|
||||||
struct vm_guest_mode_params {
|
const struct vm_guest_mode_params vm_guest_mode_params[] = {
|
||||||
unsigned int pa_bits;
|
|
||||||
unsigned int va_bits;
|
|
||||||
unsigned int page_size;
|
|
||||||
unsigned int page_shift;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct vm_guest_mode_params vm_guest_mode_params[] = {
|
|
||||||
{ 52, 48, 0x1000, 12 },
|
{ 52, 48, 0x1000, 12 },
|
||||||
{ 52, 48, 0x10000, 16 },
|
{ 52, 48, 0x10000, 16 },
|
||||||
{ 48, 48, 0x1000, 12 },
|
{ 48, 48, 0x1000, 12 },
|
||||||
|
134
tools/testing/selftests/kvm/lib/perf_test_util.c
Normal file
134
tools/testing/selftests/kvm/lib/perf_test_util.c
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020, Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kvm_util.h"
|
||||||
|
#include "perf_test_util.h"
|
||||||
|
#include "processor.h"
|
||||||
|
|
||||||
|
struct perf_test_args perf_test_args;
|
||||||
|
|
||||||
|
uint64_t guest_test_phys_mem;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Guest virtual memory offset of the testing memory slot.
|
||||||
|
* Must not conflict with identity mapped test code.
|
||||||
|
*/
|
||||||
|
static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Continuously write to the first 8 bytes of each page in the
|
||||||
|
* specified region.
|
||||||
|
*/
|
||||||
|
static void guest_code(uint32_t vcpu_id)
|
||||||
|
{
|
||||||
|
struct perf_test_vcpu_args *vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
|
||||||
|
uint64_t gva;
|
||||||
|
uint64_t pages;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Make sure vCPU args data structure is not corrupt. */
|
||||||
|
GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id);
|
||||||
|
|
||||||
|
gva = vcpu_args->gva;
|
||||||
|
pages = vcpu_args->pages;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
for (i = 0; i < pages; i++) {
|
||||||
|
uint64_t addr = gva + (i * perf_test_args.guest_page_size);
|
||||||
|
|
||||||
|
if (i % perf_test_args.wr_fract == 0)
|
||||||
|
*(uint64_t *)addr = 0x0123456789ABCDEF;
|
||||||
|
else
|
||||||
|
READ_ONCE(*(uint64_t *)addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUEST_SYNC(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
|
||||||
|
uint64_t vcpu_memory_bytes)
|
||||||
|
{
|
||||||
|
struct kvm_vm *vm;
|
||||||
|
uint64_t guest_num_pages;
|
||||||
|
|
||||||
|
pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
|
||||||
|
|
||||||
|
perf_test_args.host_page_size = getpagesize();
|
||||||
|
perf_test_args.guest_page_size = vm_guest_mode_params[mode].page_size;
|
||||||
|
|
||||||
|
guest_num_pages = vm_adjust_num_guest_pages(mode,
|
||||||
|
(vcpus * vcpu_memory_bytes) / perf_test_args.guest_page_size);
|
||||||
|
|
||||||
|
TEST_ASSERT(vcpu_memory_bytes % perf_test_args.host_page_size == 0,
|
||||||
|
"Guest memory size is not host page size aligned.");
|
||||||
|
TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0,
|
||||||
|
"Guest memory size is not guest page size aligned.");
|
||||||
|
|
||||||
|
vm = vm_create_with_vcpus(mode, vcpus,
|
||||||
|
(vcpus * vcpu_memory_bytes) / perf_test_args.guest_page_size,
|
||||||
|
0, guest_code, NULL);
|
||||||
|
|
||||||
|
perf_test_args.vm = vm;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there should be more memory in the guest test region than there
|
||||||
|
* can be pages in the guest, it will definitely cause problems.
|
||||||
|
*/
|
||||||
|
TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm),
|
||||||
|
"Requested more guest memory than address space allows.\n"
|
||||||
|
" guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n",
|
||||||
|
guest_num_pages, vm_get_max_gfn(vm), vcpus,
|
||||||
|
vcpu_memory_bytes);
|
||||||
|
|
||||||
|
guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) *
|
||||||
|
perf_test_args.guest_page_size;
|
||||||
|
guest_test_phys_mem &= ~(perf_test_args.host_page_size - 1);
|
||||||
|
#ifdef __s390x__
|
||||||
|
/* Align to 1M (segment size) */
|
||||||
|
guest_test_phys_mem &= ~((1 << 20) - 1);
|
||||||
|
#endif
|
||||||
|
pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem);
|
||||||
|
|
||||||
|
/* Add an extra memory slot for testing */
|
||||||
|
vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
|
||||||
|
guest_test_phys_mem,
|
||||||
|
PERF_TEST_MEM_SLOT_INDEX,
|
||||||
|
guest_num_pages, 0);
|
||||||
|
|
||||||
|
/* Do mapping for the demand paging memory slot */
|
||||||
|
virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0);
|
||||||
|
|
||||||
|
ucall_init(vm, NULL);
|
||||||
|
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_test_destroy_vm(struct kvm_vm *vm)
|
||||||
|
{
|
||||||
|
ucall_uninit(vm);
|
||||||
|
kvm_vm_free(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes)
|
||||||
|
{
|
||||||
|
vm_paddr_t vcpu_gpa;
|
||||||
|
struct perf_test_vcpu_args *vcpu_args;
|
||||||
|
int vcpu_id;
|
||||||
|
|
||||||
|
for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
|
||||||
|
vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
|
||||||
|
|
||||||
|
vcpu_args->vcpu_id = vcpu_id;
|
||||||
|
vcpu_args->gva = guest_test_virt_mem +
|
||||||
|
(vcpu_id * vcpu_memory_bytes);
|
||||||
|
vcpu_args->pages = vcpu_memory_bytes /
|
||||||
|
perf_test_args.guest_page_size;
|
||||||
|
|
||||||
|
vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes);
|
||||||
|
pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n",
|
||||||
|
vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes);
|
||||||
|
}
|
||||||
|
}
|
@ -485,9 +485,8 @@ static int kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
|
|||||||
kvm->mmu_notifier_count++;
|
kvm->mmu_notifier_count++;
|
||||||
need_tlb_flush = kvm_unmap_hva_range(kvm, range->start, range->end,
|
need_tlb_flush = kvm_unmap_hva_range(kvm, range->start, range->end,
|
||||||
range->flags);
|
range->flags);
|
||||||
need_tlb_flush |= kvm->tlbs_dirty;
|
|
||||||
/* we've to flush the tlb before the pages can be freed */
|
/* we've to flush the tlb before the pages can be freed */
|
||||||
if (need_tlb_flush)
|
if (need_tlb_flush || kvm->tlbs_dirty)
|
||||||
kvm_flush_remote_tlbs(kvm);
|
kvm_flush_remote_tlbs(kvm);
|
||||||
|
|
||||||
spin_unlock(&kvm->mmu_lock);
|
spin_unlock(&kvm->mmu_lock);
|
||||||
|
Loading…
Reference in New Issue
Block a user