Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Jan 2021 23:06:02 +0000 (15:06 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Jan 2021 23:06:02 +0000 (15:06 -0800)
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()
  ...

45 files changed:
Documentation/virt/kvm/api.rst
MAINTAINERS
arch/arm64/include/asm/kvm_host.h
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/smp.c
arch/arm64/kvm/Kconfig
arch/arm64/kvm/Makefile
arch/arm64/kvm/arch_timer.c
arch/arm64/kvm/arm.c
arch/arm64/kvm/hyp/include/hyp/adjust_pc.h
arch/arm64/kvm/hyp/nvhe/hyp-main.c
arch/arm64/kvm/hyp/nvhe/hyp-smp.c
arch/arm64/kvm/hyp/nvhe/psci-relay.c
arch/arm64/kvm/pmu-emul.c
arch/arm64/kvm/sys_regs.c
arch/arm64/kvm/va_layout.c
arch/arm64/kvm/vgic/vgic-init.c
arch/arm64/kvm/vgic/vgic-v2.c
arch/arm64/kvm/vgic/vgic-v3.c
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/lapic.c
arch/x86/kvm/mmu.h
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/mmu/tdp_mmu.c
arch/x86/kvm/mmu/tdp_mmu.h
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/sev.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/svm/svm.h
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/x86.c
include/kvm/arm_pmu.h
include/uapi/linux/kvm.h
tools/testing/selftests/kvm/Makefile
tools/testing/selftests/kvm/demand_paging_test.c
tools/testing/selftests/kvm/dirty_log_perf_test.c
tools/testing/selftests/kvm/dirty_log_test.c
tools/testing/selftests/kvm/include/guest_modes.h [new file with mode: 0644]
tools/testing/selftests/kvm/include/kvm_util.h
tools/testing/selftests/kvm/include/perf_test_util.h
tools/testing/selftests/kvm/lib/guest_modes.c [new file with mode: 0644]
tools/testing/selftests/kvm/lib/kvm_util.c
tools/testing/selftests/kvm/lib/perf_test_util.c [new file with mode: 0644]
virt/kvm/kvm_main.c

index 70254ea..c136e25 100644 (file)
@@ -392,9 +392,14 @@ This ioctl is obsolete and has been removed.
 
 Errors:
 
-  =====      =============================
+  =======    ==============================================================
   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
 explicit parameters, there is an implicit parameter block that can be
index b15514a..c566006 100644 (file)
@@ -9776,7 +9776,7 @@ F:        tools/testing/selftests/kvm/s390x/
 
 KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86)
 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:     Wanpeng Li <wanpengli@tencent.com>
 R:     Jim Mattson <jmattson@google.com>
index 11beda8..8fcfab0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/jump_label.h>
 #include <linux/kvm_types.h>
 #include <linux/percpu.h>
+#include <linux/psci.h>
 #include <asm/arch_gicv3.h>
 #include <asm/barrier.h>
 #include <asm/cpufeature.h>
@@ -240,6 +241,28 @@ struct kvm_host_data {
        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 {
        unsigned long   pc;
        unsigned long   r0;
index 7ffb5f1..e99edde 100644 (file)
@@ -2568,7 +2568,7 @@ static void verify_hyp_capabilities(void)
        int parange, ipa_max;
        unsigned int safe_vmid_bits, vmid_bits;
 
-       if (!IS_ENABLED(CONFIG_KVM) || !IS_ENABLED(CONFIG_KVM_ARM_HOST))
+       if (!IS_ENABLED(CONFIG_KVM))
                return;
 
        safe_mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
index 376343d..ad00f99 100644 (file)
@@ -434,7 +434,7 @@ static void __init hyp_mode_check(void)
                           "CPU: CPUs started in inconsistent modes");
        else
                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();
 }
 
index 043756d..3964acf 100644 (file)
@@ -49,14 +49,6 @@ if KVM
 
 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 # VIRTUALIZATION
index 60fd181..13b0172 100644 (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-its.o vgic/vgic-debug.o
 
-kvm-$(CONFIG_KVM_ARM_PMU)  += pmu-emul.o
+kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o
index 32ba6fb..74e0699 100644 (file)
@@ -1129,9 +1129,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
        if (!irqchip_in_kernel(vcpu->kvm))
                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)) {
                kvm_debug("incorrectly configured timer irqs\n");
                return -EINVAL;
index 6e637d2..04c4485 100644 (file)
@@ -65,10 +65,6 @@ static bool vgic_present;
 static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
 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)
 {
        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
                 * first time on this VM.
                 */
-               if (unlikely(!vgic_ready(kvm))) {
-                       ret = kvm_vgic_map_resources(kvm);
-                       if (ret)
-                               return ret;
-               }
+               ret = kvm_vgic_map_resources(kvm);
+               if (ret)
+                       return ret;
        } else {
                /*
                 * 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,
 };
 
-static void __init hyp_cpu_pm_init(void)
+static void hyp_cpu_pm_init(void)
 {
        if (!is_protected_kvm_enabled())
                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())
                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.
         */
        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)
 {
        /*
@@ -1618,8 +1615,15 @@ static bool init_psci_relay(void)
                return false;
        }
 
-       kvm_nvhe_sym(kvm_host_psci_version) = psci_ops.get_version();
-       kvm_nvhe_sym(kvm_host_psci_0_1_function_ids) = get_psci_0_1_function_ids();
+       kvm_host_psci_config.version = psci_ops.get_version();
+
+       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;
 }
 
index b1f6092..6171635 100644 (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
index bde658d..a906f9e 100644 (file)
@@ -157,11 +157,6 @@ static void default_host_smc_handler(struct kvm_cpu_context *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)
 {
        bool handled;
@@ -170,11 +165,8 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
        if (!handled)
                default_host_smc_handler(host_ctxt);
 
-       /*
-        * Unlike HVC, the return address of an SMC is the instruction's PC.
-        * Move the return address past the instruction.
-        */
-       skip_host_instruction();
+       /* SMC was trapped, move ELR past the current PC. */
+       kvm_skip_host_instr();
 }
 
 void handle_trap(struct kvm_cpu_context *host_ctxt)
index cbab0c6..2997aa1 100644 (file)
  * Other CPUs should not be allowed to boot because their features were
  * 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)
 {
-       if (cpu >= ARRAY_SIZE(__cpu_logical_map))
+       if (cpu >= ARRAY_SIZE(hyp_cpu_logical_map))
                hyp_panic();
 
-       return __cpu_logical_map[cpu];
+       return hyp_cpu_logical_map[cpu];
 }
 
 unsigned long __hyp_per_cpu_offset(unsigned int cpu)
index 08dc9de..e394784 100644 (file)
@@ -7,11 +7,8 @@
 #include <asm/kvm_asm.h>
 #include <asm/kvm_hyp.h>
 #include <asm/kvm_mmu.h>
-#include <kvm/arm_hypercalls.h>
 #include <linux/arm-smccc.h>
 #include <linux/kvm_host.h>
-#include <linux/psci.h>
-#include <kvm/arm_psci.h>
 #include <uapi/linux/psci.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);
 
 /* Config options set by the host. */
-__ro_after_init u32 kvm_host_psci_version;
-__ro_after_init struct psci_0_1_function_ids kvm_host_psci_0_1_function_ids;
-__ro_after_init s64 hyp_physvirt_offset;
+struct kvm_host_psci_config __ro_after_init kvm_host_psci_config;
+s64 __ro_after_init 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, suspend_args) = PSCI_BOOT_ARGS_INIT;
 
-static u64 get_psci_func_id(struct kvm_cpu_context *host_ctxt)
-{
-       DECLARE_REG(u64, func_id, host_ctxt, 0);
-
-       return func_id;
-}
+#define        is_psci_0_1(what, func_id)                                      \
+       (kvm_host_psci_config.psci_0_1_ ## what ## _implemented &&      \
+        (func_id) == kvm_host_psci_config.function_ids_0_1.what)
 
 static bool is_psci_0_1_call(u64 func_id)
 {
-       return (func_id == kvm_host_psci_0_1_function_ids.cpu_suspend) ||
-              (func_id == kvm_host_psci_0_1_function_ids.cpu_on) ||
-              (func_id == kvm_host_psci_0_1_function_ids.cpu_off) ||
-              (func_id == kvm_host_psci_0_1_function_ids.migrate);
+       return (is_psci_0_1(cpu_suspend, func_id) ||
+               is_psci_0_1(cpu_on, func_id) ||
+               is_psci_0_1(cpu_off, func_id) ||
+               is_psci_0_1(migrate, 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));
 }
 
-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,
                               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)
 {
-       if ((func_id == kvm_host_psci_0_1_function_ids.cpu_off) ||
-           (func_id == kvm_host_psci_0_1_function_ids.migrate))
+       if (is_psci_0_1(cpu_off, func_id) || is_psci_0_1(migrate, func_id))
                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);
-       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);
-       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)
@@ -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)
 {
-       u64 func_id = get_psci_func_id(host_ctxt);
+       DECLARE_REG(u64, func_id, host_ctxt, 0);
        unsigned long ret;
 
-       if (!is_psci_call(func_id))
-               return false;
-
-       switch (kvm_host_psci_version) {
+       switch (kvm_host_psci_config.version) {
        case PSCI_VERSION(0, 1):
+               if (!is_psci_0_1_call(func_id))
+                       return false;
                ret = psci_0_1_handler(func_id, host_ctxt);
                break;
        case PSCI_VERSION(0, 2):
+               if (!is_psci_0_2_call(func_id))
+                       return false;
                ret = psci_0_2_handler(func_id, host_ctxt);
                break;
        default:
+               if (!is_psci_0_2_call(func_id))
+                       return false;
                ret = psci_1_0_handler(func_id, host_ctxt);
                break;
        }
index 398f6df..4ad66a5 100644 (file)
@@ -850,8 +850,6 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
                   return -EINVAL;
        }
 
-       kvm_pmu_vcpu_reset(vcpu);
-
        return 0;
 }
 
index 3313ded..42ccc27 100644 (file)
@@ -594,6 +594,10 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
        u64 pmcr, val;
 
+       /* No PMU available, PMCR_EL0 may UNDEF... */
+       if (!kvm_arm_support_pmu_v3())
+               return;
+
        pmcr = read_sysreg(pmcr_el0);
        /*
         * 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)                                             \
        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 */
 #define DBG_BCR_BVR_WCR_WVR_EL1(n)                                     \
index d8cc51b..70fcd6a 100644 (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)
 {
-       extern s64 kvm_nvhe_sym(hyp_physvirt_offset);
        u64 kern_va, hyp_va;
 
        /* 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);
-       CHOOSE_NVHE_SYM(hyp_physvirt_offset) = (s64)__pa(kern_va) - (s64)hyp_va;
+       hyp_physvirt_offset = (s64)__pa(kern_va) - (s64)hyp_va;
 }
 
 /*
index 32e32d6..052917d 100644 (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
  * called on the first VCPU run.
  * 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.
  * @kvm: kvm struct pointer
  */
@@ -428,7 +429,13 @@ int kvm_vgic_map_resources(struct kvm *kvm)
        struct vgic_dist *dist = &kvm->arch.vgic;
        int ret = 0;
 
+       if (likely(vgic_ready(kvm)))
+               return 0;
+
        mutex_lock(&kvm->lock);
+       if (vgic_ready(kvm))
+               goto out;
+
        if (!irqchip_in_kernel(kvm))
                goto out;
 
@@ -439,6 +446,8 @@ int kvm_vgic_map_resources(struct kvm *kvm)
 
        if (ret)
                __kvm_vgic_destroy(kvm);
+       else
+               dist->ready = true;
 
 out:
        mutex_unlock(&kvm->lock);
index ebf53a4..11934c2 100644 (file)
@@ -306,20 +306,15 @@ int vgic_v2_map_resources(struct kvm *kvm)
        struct vgic_dist *dist = &kvm->arch.vgic;
        int ret = 0;
 
-       if (vgic_ready(kvm))
-               goto out;
-
        if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
            IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) {
                kvm_err("Need to set vgic cpu and dist addresses first\n");
-               ret = -ENXIO;
-               goto out;
+               return -ENXIO;
        }
 
        if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) {
                kvm_err("VGIC CPU and dist frames overlap\n");
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
        /*
@@ -329,13 +324,13 @@ int vgic_v2_map_resources(struct kvm *kvm)
        ret = vgic_init(kvm);
        if (ret) {
                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);
        if (ret) {
                kvm_err("Unable to register VGIC MMIO regions\n");
-               goto out;
+               return ret;
        }
 
        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);
                if (ret) {
                        kvm_err("Unable to remap VGIC CPU to VCPU\n");
-                       goto out;
+                       return ret;
                }
        }
 
-       dist->ready = true;
-
-out:
-       return ret;
+       return 0;
 }
 
 DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
index 9cdf39a..52915b3 100644 (file)
@@ -500,29 +500,23 @@ int vgic_v3_map_resources(struct kvm *kvm)
        int ret = 0;
        int c;
 
-       if (vgic_ready(kvm))
-               goto out;
-
        kvm_for_each_vcpu(c, vcpu, kvm) {
                struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
 
                if (IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) {
                        kvm_debug("vcpu %d redistributor base not set\n", c);
-                       ret = -ENXIO;
-                       goto out;
+                       return -ENXIO;
                }
        }
 
        if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) {
                kvm_err("Need to set vgic distributor addresses first\n");
-               ret = -ENXIO;
-               goto out;
+               return -ENXIO;
        }
 
        if (!vgic_v3_check_base(kvm)) {
                kvm_err("VGIC redist and dist frames overlap\n");
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
        /*
@@ -530,22 +524,19 @@ int vgic_v3_map_resources(struct kvm *kvm)
         * the VGIC before we need to use it.
         */
        if (!vgic_initialized(kvm)) {
-               ret = -EBUSY;
-               goto out;
+               return -EBUSY;
        }
 
        ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3);
        if (ret) {
                kvm_err("Unable to register VGICv3 dist MMIO regions\n");
-               goto out;
+               return ret;
        }
 
        if (kvm_vgic_global_state.has_gicv4_1)
                vgic_v4_configure_vsgis(kvm);
-       dist->ready = true;
 
-out:
-       return ret;
+       return 0;
 }
 
 DEFINE_STATIC_KEY_FALSE(vgic_v3_cpuif_trap);
index 3ab7b46..3d6616f 100644 (file)
@@ -1010,9 +1010,21 @@ struct kvm_arch {
         */
        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;
-       /* 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;
 };
 
@@ -1287,6 +1299,8 @@ struct kvm_x86_ops {
        void (*migrate_timers)(struct kvm_vcpu *vcpu);
        void (*msr_filter_changed)(struct kvm_vcpu *vcpu);
        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 {
@@ -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_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);
 
 void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
index 3136e05..43ccead 100644 (file)
@@ -674,7 +674,7 @@ static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu)
                           (unsigned long long)vcpu->arch.pv_eoi.msr_val);
                return false;
        }
-       return val & 0x1;
+       return val & KVM_PV_EOI_ENABLED;
 }
 
 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 */
                        smp_rmb();
                        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;
                }
        }
index 9c4a9c8..581925e 100644 (file)
@@ -49,7 +49,7 @@ static inline u64 rsvd_bits(int s, int e)
        if (e < s)
                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);
index c478904..6d16481 100644 (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.
  * 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;
-       int leaf = vcpu->arch.mmu->root_level;
+       int leaf = -1;
        u64 spte;
 
-
        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_next(&iterator, spte)) {
                leaf = iterator.level;
                spte = mmu_spte_get_lockless(iterator.sptep);
 
-               sptes[leaf - 1] = spte;
+               sptes[leaf] = spte;
 
                if (!is_shadow_present_pte(spte))
                        break;
-
        }
 
        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 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)
 {
-       u64 sptes[PT64_ROOT_MAX_LEVEL];
+       u64 sptes[PT64_ROOT_MAX_LEVEL + 1];
        struct rsvd_bits_validate *rsvd_check;
-       int root = vcpu->arch.mmu->shadow_root_level;
-       int leaf;
-       int level;
+       int root, leaf, level;
        bool reserved = false;
 
        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))
-               leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes);
+               leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, &root);
        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;
 
-       for (level = root; level >= leaf; level--) {
-               if (!is_shadow_present_pte(sptes[level - 1]))
-                       break;
+       for (level = root; level >= leaf; level--)
                /*
                 * Use a bitwise-OR instead of a logical-OR to aggregate the
                 * reserved bit and EPT's invalid memtype/XWR checks to avoid
                 * adding a Jcc in the loop.
                 */
-               reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level - 1]) |
-                           __is_rsvd_bits_set(rsvd_check, sptes[level - 1],
-                                              level);
-       }
+               reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level]) |
+                           __is_rsvd_bits_set(rsvd_check, sptes[level], level);
 
        if (reserved) {
                pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n",
                       __func__, addr);
                for (level = root; level >= leaf; level--)
                        pr_err("------ spte 0x%llx level %d.\n",
-                              sptes[level - 1], level);
+                              sptes[level], level);
        }
 
-       *sptep = sptes[leaf - 1];
-
        return reserved;
 }
 
index 4bd2f1d..2ef8615 100644 (file)
@@ -44,7 +44,48 @@ void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm)
        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)
 
 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;
        bool flush = false;
 
-       for_each_tdp_mmu_root(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);
-
+       for_each_tdp_mmu_root_yield_safe(kvm, root)
                flush |= zap_gfn_range(kvm, root, start, end, true);
 
-               kvm_mmu_put_root(kvm, root);
-       }
-
        return flush;
 }
 
@@ -619,13 +651,7 @@ static int kvm_tdp_mmu_handle_hva_range(struct kvm *kvm, unsigned long start,
        int ret = 0;
        int as_id;
 
-       for_each_tdp_mmu_root(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);
-
+       for_each_tdp_mmu_root_yield_safe(kvm, root) {
                as_id = kvm_mmu_page_as_id(root);
                slots = __kvm_memslots(kvm, as_id);
                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,
                                       gfn_end, data);
                }
-
-               kvm_mmu_put_root(kvm, root);
        }
 
        return ret;
@@ -838,21 +862,13 @@ bool kvm_tdp_mmu_wrprot_slot(struct kvm *kvm, struct kvm_memory_slot *slot,
        int root_as_id;
        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);
                if (root_as_id != slot->as_id)
                        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,
                             slot->base_gfn + slot->npages, min_level);
-
-               kvm_mmu_put_root(kvm, root);
        }
 
        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;
        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);
                if (root_as_id != slot->as_id)
                        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,
                                slot->base_gfn + slot->npages);
-
-               kvm_mmu_put_root(kvm, root);
        }
 
        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;
        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);
                if (root_as_id != slot->as_id)
                        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,
                                slot->base_gfn + slot->npages);
-
-               kvm_mmu_put_root(kvm, root);
        }
        return spte_set;
 }
@@ -1089,21 +1089,13 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
        struct kvm_mmu_page *root;
        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);
                if (root_as_id != slot->as_id)
                        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,
                                           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.
  * 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 kvm_mmu *mmu = vcpu->arch.mmu;
-       int leaf = vcpu->arch.mmu->shadow_root_level;
        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) {
                leaf = iter.level;
-               sptes[leaf - 1] = iter.old_spte;
+               sptes[leaf] = iter.old_spte;
        }
 
        return leaf;
index 556e065..cbbdbad 100644 (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,
                                   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 */
index b0b6674..cb4c6ee 100644 (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)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
+
        if (!nested_svm_vmrun_msrpm(svm)) {
                vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
                vcpu->run->internal.suberror =
@@ -595,6 +596,8 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
        svm->nested.vmcb12_gpa = 0;
        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 */
        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);
                copy_vmcb_control_area(&vmcb->control, &hsave->control);
                nested_svm_uninit_mmu_context(&svm->vcpu);
+               vmcb_mark_all_dirty(svm->vmcb);
        }
 
        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
         * 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);
        hsave->save = *save;
 
index 9858d5a..c8ffdbc 100644 (file)
@@ -1563,6 +1563,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
                        goto vmgexit_err;
                break;
        case SVM_VMGEXIT_NMI_COMPLETE:
+       case SVM_VMGEXIT_AP_HLT_LOOP:
        case SVM_VMGEXIT_AP_JUMP_TABLE:
        case SVM_VMGEXIT_UNSUPPORTED_EVENT:
                break;
@@ -1888,6 +1889,9 @@ int sev_handle_vmgexit(struct vcpu_svm *svm)
        case SVM_VMGEXIT_NMI_COMPLETE:
                ret = svm_invoke_exit_handler(svm, SVM_EXIT_IRET);
                break;
+       case SVM_VMGEXIT_AP_HLT_LOOP:
+               ret = kvm_emulate_ap_reset_hold(&svm->vcpu);
+               break;
        case SVM_VMGEXIT_AP_JUMP_TABLE: {
                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
         * 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
@@ -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]);
        }
 }
+
+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);
+}
index cce0143..7ef1717 100644 (file)
@@ -3677,8 +3677,6 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
        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,
                                        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));
 }
 
+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)
 {
        avic_vm_destroy(kvm);
@@ -4526,6 +4532,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
 
        .msr_filter_changed = svm_msr_filter_changed,
        .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 = {
index 5431e63..0fe874a 100644 (file)
@@ -185,6 +185,7 @@ struct vcpu_svm {
        struct vmcb_save_area *vmsa;
        struct ghcb *ghcb;
        struct kvm_host_map ghcb_map;
+       bool received_first_sipi;
 
        /* SEV-ES scratch area support */
        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_vcpu_load(struct vcpu_svm *svm, int cpu);
 void sev_es_vcpu_put(struct vcpu_svm *svm);
+void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector);
 
 /* vmenter.S */
 
index e2f2656..0fbb469 100644 (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 */
        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. */
        if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
                kvm_vcpu_flush_tlb_current(vcpu);
index 75c9c6a..2af05d3 100644 (file)
@@ -7707,6 +7707,8 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = {
        .msr_filter_changed = vmx_msr_filter_changed,
        .complete_emulated_msr = kvm_complete_insn_gp,
        .cpu_dirty_log_size = vmx_cpu_dirty_log_size,
+
+       .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector,
 };
 
 static __init int hardware_setup(void)
index 3f7c1fc..9a8969a 100644 (file)
@@ -7976,17 +7976,22 @@ void kvm_arch_exit(void)
        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;
        if (lapic_in_kernel(vcpu)) {
-               vcpu->arch.mp_state = KVM_MP_STATE_HALTED;
+               vcpu->arch.mp_state = state;
                return 1;
        } else {
-               vcpu->run->exit_reason = KVM_EXIT_HLT;
+               vcpu->run->exit_reason = reason;
                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);
 
 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);
 
+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
 static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
                                unsigned long clock_type)
@@ -8789,7 +8802,9 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
 
        if (kvm_request_pending(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;
                                goto out;
                        }
@@ -9094,6 +9109,7 @@ static inline int vcpu_block(struct kvm *kvm, struct kvm_vcpu *vcpu)
        kvm_apic_accept_events(vcpu);
        switch(vcpu->arch.mp_state) {
        case KVM_MP_STATE_HALTED:
+       case KVM_MP_STATE_AP_RESET_HOLD:
                vcpu->arch.pv.pv_unhalted = false;
                vcpu->arch.mp_state =
                        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_apic_accept_events(vcpu);
-       if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED &&
-                                       vcpu->arch.pv.pv_unhalted)
+       if ((vcpu->arch.mp_state == KVM_MP_STATE_HALTED ||
+            vcpu->arch.mp_state == KVM_MP_STATE_AP_RESET_HOLD) &&
+           vcpu->arch.pv.pv_unhalted)
                mp_state->mp_state = KVM_MP_STATE_RUNNABLE;
        else
                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_rip_write(vcpu, 0);
 }
+EXPORT_SYMBOL_GPL(kvm_vcpu_deliver_sipi_vector);
 
 int kvm_arch_hardware_enable(void)
 {
index fc85f50..8dcb3e1 100644 (file)
@@ -13,7 +13,7 @@
 #define ARMV8_PMU_CYCLE_IDX            (ARMV8_PMU_MAX_COUNTERS - 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 {
        u8 idx; /* index into the pmu->pmc array */
index 886802b..374c678 100644 (file)
@@ -251,6 +251,7 @@ struct kvm_hyperv_exit {
 #define KVM_EXIT_X86_RDMSR        29
 #define KVM_EXIT_X86_WRMSR        30
 #define KVM_EXIT_DIRTY_RING_FULL  31
+#define KVM_EXIT_AP_RESET_HOLD    32
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -573,6 +574,7 @@ struct kvm_vapic_addr {
 #define KVM_MP_STATE_CHECK_STOP        6
 #define KVM_MP_STATE_OPERATING         7
 #define KVM_MP_STATE_LOAD              8
+#define KVM_MP_STATE_AP_RESET_HOLD     9
 
 struct kvm_mp_state {
        __u32 mp_state;
index c7ca4fa..fe41c6a 100644 (file)
@@ -33,7 +33,7 @@ ifeq ($(ARCH),s390)
        UNAME_M := s390x
 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_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
index 3d96a7b..cdad1ec 100644 (file)
@@ -7,23 +7,20 @@
  * Copyright (C) 2019, Google, Inc.
  */
 
-#define _GNU_SOURCE /* for program_invocation_name */
+#define _GNU_SOURCE /* for pipe2 */
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-#include <asm/unistd.h>
 #include <time.h>
 #include <poll.h>
 #include <pthread.h>
-#include <linux/bitmap.h>
-#include <linux/bitops.h>
 #include <linux/userfaultfd.h>
+#include <sys/syscall.h>
 
-#include "perf_test_util.h"
-#include "processor.h"
+#include "kvm_util.h"
 #include "test_util.h"
+#include "perf_test_util.h"
+#include "guest_modes.h"
 
 #ifdef __NR_userfaultfd
 
 #define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__)
 #endif
 
+static int nr_vcpus = 1;
+static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
 static char *guest_data_prototype;
 
 static void *vcpu_worker(void *data)
 {
        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;
        struct kvm_vm *vm = perf_test_args.vm;
        struct kvm_run *run;
@@ -248,9 +247,14 @@ static int setup_demand_paging(struct kvm_vm *vm,
        return 0;
 }
 
-static void run_test(enum vm_guest_mode mode, bool use_uffd,
-                    useconds_t uffd_delay)
+struct test_params {
+       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 *uffd_handler_threads = 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 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;
 
@@ -273,9 +277,9 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
        vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
        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 =
                        malloc(nr_vcpus * sizeof(*uffd_handler_threads));
                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,
                                                &uffd_handler_threads[vcpu_id],
                                                pipefds[vcpu_id * 2],
-                                               uffd_delay, &uffd_args[vcpu_id],
+                                               p->uffd_delay, &uffd_args[vcpu_id],
                                                vcpu_hva, guest_percpu_mem_size);
                        if (r < 0)
                                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");
 
-       if (use_uffd) {
+       if (p->use_uffd) {
                char c;
 
                /* 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 /
                ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
 
-       ucall_uninit(vm);
-       kvm_vm_free(vm);
+       perf_test_destroy_vm(vm);
 
        free(guest_data_prototype);
        free(vcpu_threads);
-       if (use_uffd) {
+       if (p->use_uffd) {
                free(uffd_handler_threads);
                free(uffd_args);
                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)
 {
-       int i;
-
        puts("");
        printf("usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec]\n"
               "          [-b memory] [-v vcpus]\n", name);
-       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)" : "");
-       }
+       guest_modes_help();
        printf(" -u: use User Fault FD to handle vCPU page\n"
               "     faults.\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 max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
-       bool mode_selected = false;
-       unsigned int mode;
-       int opt, i;
-       bool use_uffd = false;
-       useconds_t uffd_delay = 0;
-
-#ifdef __x86_64__
-       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
+       struct test_params p = {};
+       int opt;
+
+       guest_modes_append_default();
 
        while ((opt = getopt(argc, argv, "hm:ud:b:v:")) != -1) {
                switch (opt) {
                case 'm':
-                       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;
+                       guest_modes_cmdline(optarg);
                        break;
                case 'u':
-                       use_uffd = true;
+                       p.use_uffd = true;
                        break;
                case 'd':
-                       uffd_delay = strtoul(optarg, NULL, 0);
-                       TEST_ASSERT(uffd_delay >= 0,
-                                   "A negative UFFD delay is not supported.");
+                       p.uffd_delay = strtoul(optarg, NULL, 0);
+                       TEST_ASSERT(p.uffd_delay >= 0, "A negative UFFD delay is not supported.");
                        break;
                case 'b':
                        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) {
-               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);
-       }
+       for_each_guest_mode(run_test, &p);
 
        return 0;
 }
index 9c6a7be..2283a0e 100644 (file)
@@ -8,29 +8,28 @@
  * Copyright (C) 2020, Google, Inc.
  */
 
-#define _GNU_SOURCE /* for program_invocation_name */
-
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <time.h>
 #include <pthread.h>
 #include <linux/bitmap.h>
-#include <linux/bitops.h>
 
 #include "kvm_util.h"
-#include "perf_test_util.h"
-#include "processor.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)*/
 #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 */
 static u64 dirty_log_manual_caps;
 static bool host_quit;
 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)
 {
@@ -42,7 +41,7 @@ static void *vcpu_worker(void *data)
        struct timespec ts_diff;
        struct timespec total = (struct timespec){0};
        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;
 
        vcpu_args_set(vm, vcpu_id, 1, vcpu_id);
@@ -89,9 +88,15 @@ static void *vcpu_worker(void *data)
        return NULL;
 }
 
-static void run_test(enum vm_guest_mode mode, unsigned long iterations,
-                    uint64_t phys_offset, int wr_fract)
+struct test_params {
+       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;
        struct kvm_vm *vm;
        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 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 = 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));
        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);
 
@@ -150,13 +155,13 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
 
        /* Enable dirty logging */
        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);
        ts_diff = timespec_diff_now(start);
        pr_info("Enabling dirty logging time: %ld.%.9lds\n\n",
                ts_diff.tv_sec, ts_diff.tv_nsec);
 
-       while (iteration < iterations) {
+       while (iteration < p->iterations) {
                /*
                 * Incrementing the iteration number will start the vCPUs
                 * 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);
 
                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);
                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) {
                        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);
 
                        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 */
        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);
        pr_info("Disabling dirty logging time: %ld.%.9lds\n",
                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",
-               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);
 
        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",
-                       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);
        }
 
        free(bmap);
        free(vcpu_threads);
-       ucall_uninit(vm);
-       kvm_vm_free(vm);
+       perf_test_destroy_vm(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)
 {
-       int i;
-
        puts("");
        printf("usage: %s [-h] [-i iterations] [-p offset] "
               "[-m mode] [-b vcpu bytes] [-v vcpus]\n", name);
@@ -250,14 +242,7 @@ static void help(char *name)
               TEST_HOST_LOOP_N);
        printf(" -p: specify guest physical test memory offset\n"
               "     Warning: a low offset can conflict with the loaded test code.\n");
-       printf(" -m: specify the guest mode ID to test "
-              "(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)" : "");
-       }
+       guest_modes_help();
        printf(" -b: specify the size of the memory region which should be\n"
               "     dirtied by each vCPU. e.g. 10M or 3G.\n"
               "     (default: 1G)\n");
@@ -272,74 +257,43 @@ static void help(char *name)
 
 int main(int argc, char *argv[])
 {
-       unsigned long iterations = TEST_HOST_LOOP_N;
-       bool mode_selected = false;
-       uint64_t phys_offset = 0;
-       unsigned int mode;
-       int opt, i;
-       int wr_fract = 1;
+       int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
+       struct test_params p = {
+               .iterations = TEST_HOST_LOOP_N,
+               .wr_fract = 1,
+       };
+       int opt;
 
        dirty_log_manual_caps =
                kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
        dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
                                  KVM_DIRTY_LOG_INITIALLY_SET);
 
-#ifdef __x86_64__
-       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
+       guest_modes_append_default();
 
        while ((opt = getopt(argc, argv, "hi:p:m:b:f:v:")) != -1) {
                switch (opt) {
                case 'i':
-                       iterations = strtol(optarg, NULL, 10);
+                       p.iterations = strtol(optarg, NULL, 10);
                        break;
                case 'p':
-                       phys_offset = strtoull(optarg, NULL, 0);
+                       p.phys_offset = strtoull(optarg, NULL, 0);
                        break;
                case 'm':
-                       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;
+                       guest_modes_cmdline(optarg);
                        break;
                case 'b':
                        guest_percpu_mem_size = parse_size(optarg);
                        break;
                case 'f':
-                       wr_fract = atoi(optarg);
-                       TEST_ASSERT(wr_fract >= 1,
+                       p.wr_fract = atoi(optarg);
+                       TEST_ASSERT(p.wr_fract >= 1,
                                    "Write fraction cannot be less than one");
                        break;
                case 'v':
                        nr_vcpus = atoi(optarg);
-                       TEST_ASSERT(nr_vcpus > 0,
-                                   "Must have a positive number of vCPUs");
-                       TEST_ASSERT(nr_vcpus <= MAX_VCPUS,
-                                   "This test does not currently support\n"
-                                   "more than %d vCPUs.", MAX_VCPUS);
+                       TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
+                                   "Invalid number of vcpus, must be between 1 and %d", max_vcpus);
                        break;
                case 'h':
                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) {
-               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);
-       }
+       for_each_guest_mode(run_test, &p);
 
        return 0;
 }
index 471baec..bb2752d 100644 (file)
@@ -9,8 +9,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <time.h>
 #include <pthread.h>
 #include <semaphore.h>
 #include <sys/types.h>
@@ -20,8 +18,9 @@
 #include <linux/bitops.h>
 #include <asm/barrier.h>
 
-#include "test_util.h"
 #include "kvm_util.h"
+#include "test_util.h"
+#include "guest_modes.h"
 #include "processor.h"
 
 #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 PAGE_SHIFT_4K  12
 
-static void run_test(enum vm_guest_mode mode, unsigned long iterations,
-                    unsigned long interval, uint64_t phys_offset)
+struct test_params {
+       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;
        unsigned long *bmap;
 
@@ -709,12 +714,12 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
        host_page_size = getpagesize();
        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_num_pages) * guest_page_size;
                guest_test_phys_mem &= ~(host_page_size - 1);
        } else {
-               guest_test_phys_mem = phys_offset;
+               guest_test_phys_mem = p->phys_offset;
        }
 
 #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);
 
-       while (iteration < iterations) {
+       while (iteration < p->iterations) {
                /* 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,
                                             bmap, host_num_pages);
                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);
 }
 
-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)
 {
-       int i;
-
        puts("");
        printf("usage: %s [-h] [-i iterations] [-I interval] "
               "[-p offset] [-m mode]\n", name);
@@ -813,51 +806,23 @@ static void help(char *name)
        printf(" -M: specify the host logging mode "
               "(default: run all log modes).  Supported modes: \n\t");
        log_modes_dump();
-       printf(" -m: specify the guest mode ID to test "
-              "(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)" : "");
-       }
+       guest_modes_help();
        puts("");
        exit(0);
 }
 
 int main(int argc, char *argv[])
 {
-       unsigned long iterations = TEST_HOST_LOOP_N;
-       unsigned long interval = TEST_HOST_LOOP_INTERVAL;
-       bool mode_selected = false;
-       uint64_t phys_offset = 0;
-       unsigned int mode;
-       int opt, i, j;
+       struct test_params p = {
+               .iterations = TEST_HOST_LOOP_N,
+               .interval = TEST_HOST_LOOP_INTERVAL,
+       };
+       int opt, i;
 
        sem_init(&dirty_ring_vcpu_stop, 0, 0);
        sem_init(&dirty_ring_vcpu_cont, 0, 0);
 
-#ifdef __x86_64__
-       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
+       guest_modes_append_default();
 
        while ((opt = getopt(argc, argv, "c:hi:I:p:m:M:")) != -1) {
                switch (opt) {
@@ -865,24 +830,16 @@ int main(int argc, char *argv[])
                        test_dirty_ring_count = strtol(optarg, NULL, 10);
                        break;
                case 'i':
-                       iterations = strtol(optarg, NULL, 10);
+                       p.iterations = strtol(optarg, NULL, 10);
                        break;
                case 'I':
-                       interval = strtol(optarg, NULL, 10);
+                       p.interval = strtol(optarg, NULL, 10);
                        break;
                case 'p':
-                       phys_offset = strtoull(optarg, NULL, 0);
+                       p.phys_offset = strtoull(optarg, NULL, 0);
                        break;
                case 'm':
-                       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;
+                       guest_modes_cmdline(optarg);
                        break;
                case 'M':
                        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(interval > 0, "Interval must be greater than zero");
+       TEST_ASSERT(p.iterations > 2, "Iterations must be greater than two");
+       TEST_ASSERT(p.interval > 0, "Interval must be greater than zero");
 
        pr_info("Test iterations: %"PRIu64", interval: %"PRIu64" (ms)\n",
-               iterations, interval);
+               p.iterations, p.interval);
 
        srandom(time(0));
 
-       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));
-               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);
+       if (host_log_mode_option == LOG_MODE_ALL) {
+               /* Run each log mode */
+               for (i = 0; i < LOG_MODE_NUM; i++) {
+                       pr_info("Testing Log Mode '%s'\n", log_modes[i].name);
+                       host_log_mode = i;
+                       for_each_guest_mode(run_test, &p);
                }
+       } else {
+               host_log_mode = host_log_mode_option;
+               for_each_guest_mode(run_test, &p);
        }
 
        return 0;
diff --git a/tools/testing/selftests/kvm/include/guest_modes.h b/tools/testing/selftests/kvm/include/guest_modes.h
new file mode 100644 (file)
index 0000000..b691df3
--- /dev/null
@@ -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);
index dfa9d36..5cbb861 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "sparsebit.h"
 
+#define KVM_MAX_VCPUS 512
 
 /*
  * 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]
 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 {
        VM_MEM_SRC_ANONYMOUS,
        VM_MEM_SRC_ANONYMOUS_THP,
index 239421e..b118882 100644 (file)
@@ -9,38 +9,15 @@
 #define SELFTEST_KVM_PERF_TEST_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 */
 #define DEFAULT_GUEST_TEST_MEM         0xc0000000
 
 #define DEFAULT_PER_VCPU_MEM_SIZE      (1 << 30) /* 1G */
 
-/*
- * 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;
-
-/*
- * 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;
+#define PERF_TEST_MEM_SLOT_INDEX       1
 
-struct vcpu_args {
+struct perf_test_vcpu_args {
        uint64_t gva;
        uint64_t pages;
 
@@ -54,141 +31,21 @@ struct perf_test_args {
        uint64_t guest_page_size;
        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
- * specified region.
+ * 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 void guest_code(uint32_t vcpu_id)
-{
-       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. */
-       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);
-       }
-}
-
-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;
+extern uint64_t guest_test_phys_mem;
 
-               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);
-       }
-}
+struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
+                               uint64_t vcpu_memory_bytes);
+void perf_test_destroy_vm(struct kvm_vm *vm);
+void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes);
 
 #endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */
diff --git a/tools/testing/selftests/kvm/lib/guest_modes.c b/tools/testing/selftests/kvm/lib/guest_modes.c
new file mode 100644 (file)
index 0000000..25bff30
--- /dev/null
@@ -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;
+}
index 88ef706..fa5a90e 100644 (file)
@@ -153,14 +153,7 @@ const char * const vm_guest_mode_string[] = {
 _Static_assert(sizeof(vm_guest_mode_string)/sizeof(char *) == NUM_VM_MODES,
               "Missing new mode strings?");
 
-struct 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[] = {
+const struct vm_guest_mode_params vm_guest_mode_params[] = {
        { 52, 48,  0x1000, 12 },
        { 52, 48, 0x10000, 16 },
        { 48, 48,  0x1000, 12 },
diff --git a/tools/testing/selftests/kvm/lib/perf_test_util.c b/tools/testing/selftests/kvm/lib/perf_test_util.c
new file mode 100644 (file)
index 0000000..9be1944
--- /dev/null
@@ -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);
+       }
+}
index 5f26048..fa9e361 100644 (file)
@@ -485,9 +485,8 @@ static int kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
        kvm->mmu_notifier_count++;
        need_tlb_flush = kvm_unmap_hva_range(kvm, range->start, range->end,
                                             range->flags);
-       need_tlb_flush |= kvm->tlbs_dirty;
        /* 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);
 
        spin_unlock(&kvm->mmu_lock);