* 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:
Linus Torvalds 2021-01-08 15:06:02 -08:00
commit 2a190b22aa
45 changed files with 666 additions and 654 deletions

View File

@ -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

View File

@ -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>

View File

@ -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;

View File

@ -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);

View File

@ -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();
} }

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
} }

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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) \

View File

@ -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;
} }
/* /*

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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);

View File

@ -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;
} }

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);
}

View File

@ -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 = {

View File

@ -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 */

View File

@ -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);

View File

@ -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)

View File

@ -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)
{ {

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;

View 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);

View File

@ -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,

View File

@ -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 */

View 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;
}

View File

@ -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 },

View 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);
}
}

View File

@ -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);