History

KVM: arm64: Refuse to run VCPU if the PMU doesn't match the physical CPU

Userspace can assign a PMU to a VCPU with the KVM_ARM_VCPU_PMU_V3_SET_PMU device ioctl. If the VCPU is scheduled on a physical CPU which has a different PMU, the perf events needed to emulate a guest PMU won't be scheduled in and the guest performance counters will stop counting. Treat it as an userspace error and refuse to run the VCPU in this situation.

Alexandru Elisei committed 4 years ago

Commit 583cda11 parent 6ee7fca5 files · +38 −1Browse files

Unified · Split

5 files
  1. Documentation/virt/kvm/devices/vcpu.rst +5 −1
  2. arch/arm64/include/asm/kvm_host.h +12 −0
  3. arch/arm64/include/uapi/asm/kvm.h +3 −0
  4. arch/arm64/kvm/arm.c +17 −0
  5. arch/arm64/kvm/pmu-emul.c +1 −0
Documentation/virt/kvm/devices/vcpu.rst+5−1
@@ -131,7 +131,11 @@ if a PMU event filter is already present. · 130 unmodified lines
131131
132132 Note that KVM will not make any attempts to run the VCPU on the physical CPUs
133133 associated with the PMU specified by this attribute. This is entirely left to
134−userspace.
134+userspace. However, attempting to run the VCPU on a physical CPU not supported
135+by the PMU will fail and KVM_RUN will return with
136+exit_reason = KVM_EXIT_FAIL_ENTRY and populate the fail_entry struct by setting
137+hardare_entry_failure_reason field to KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED and
138+the cpu field to the processor id.
135139
136140 2. GROUP: KVM_ARM_VCPU_TIMER_CTRL
137141 =================================
arch/arm64/include/asm/kvm_host.h+12−0
@@ -131,6 +131,8 @@ struct kvm_arch { · 130 unmodified lines
131131 unsigned long *pmu_filter;
132132 struct arm_pmu *arm_pmu;
133133
134+ cpumask_var_t supported_cpus;
135+
134136 u8 pfr0_csv2;
135137 u8 pfr0_csv3;
136138
@@ -436,6 +438,7 @@ struct kvm_vcpu_arch { · 299 unmodified lines
436438 #define KVM_ARM64_DEBUG_STATE_SAVE_SPE (1 << 12) /* Save SPE context if active */
437439 #define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13) /* Save TRBE context if active */
438440 #define KVM_ARM64_FP_FOREIGN_FPSTATE (1 << 14)
441+#define KVM_ARM64_ON_UNSUPPORTED_CPU (1 << 15) /* Physical CPU not in supported_cpus */
439442
440443 #define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \
441444 KVM_GUESTDBG_USE_SW_BP | \
@@ -454,6 +457,15 @@ struct kvm_vcpu_arch { · 12 unmodified lines
454457 #define vcpu_has_ptrauth(vcpu) false
455458 #endif
456459
460+#define vcpu_on_unsupported_cpu(vcpu) \
461+ ((vcpu)->arch.flags & KVM_ARM64_ON_UNSUPPORTED_CPU)
462+
463+#define vcpu_set_on_unsupported_cpu(vcpu) \
464+ ((vcpu)->arch.flags |= KVM_ARM64_ON_UNSUPPORTED_CPU)
465+
466+#define vcpu_clear_on_unsupported_cpu(vcpu) \
467+ ((vcpu)->arch.flags &= ~KVM_ARM64_ON_UNSUPPORTED_CPU)
468+
457469 #define vcpu_gp_regs(v) (&(v)->arch.ctxt.regs)
458470
459471 /*
arch/arm64/include/uapi/asm/kvm.h+3−0
@@ -414,6 +414,9 @@ struct kvm_arm_copy_mte_tags { · 413 unmodified lines
414414 #define KVM_PSCI_RET_INVAL PSCI_RET_INVALID_PARAMS
415415 #define KVM_PSCI_RET_DENIED PSCI_RET_DENIED
416416
417+/* run->fail_entry.hardware_entry_failure_reason codes. */
418+#define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0)
419+
417420 #endif
418421
419422 #endif /* __ARM_KVM_H__ */
arch/arm64/kvm/arm.c+17−0
@@ -150,6 +150,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) · 149 unmodified lines
150150 if (ret)
151151 goto out_free_stage2_pgd;
152152
153+ if (!zalloc_cpumask_var(&kvm->arch.supported_cpus, GFP_KERNEL))
154+ goto out_free_stage2_pgd;
155+ cpumask_copy(kvm->arch.supported_cpus, cpu_possible_mask);
156+
153157 kvm_vgic_early_init(kvm);
154158
155159 /* The maximum number of VCPUs is limited by the host's GIC model */
@@ -176,6 +180,7 @@ vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) · 20 unmodified lines
176180 void kvm_arch_destroy_vm(struct kvm *kvm)
177181 {
178182 bitmap_free(kvm->arch.pmu_filter);
183+ free_cpumask_var(kvm->arch.supported_cpus);
179184
180185 kvm_vgic_destroy(kvm);
181186
@@ -411,6 +416,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · 229 unmodified lines
411416 if (vcpu_has_ptrauth(vcpu))
412417 vcpu_ptrauth_disable(vcpu);
413418 kvm_arch_vcpu_load_debug_state_flags(vcpu);
419+
420+ if (!cpumask_test_cpu(smp_processor_id(), vcpu->kvm->arch.supported_cpus))
421+ vcpu_set_on_unsupported_cpu(vcpu);
414422 }
415423
416424 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
@@ -423,6 +431,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) · 6 unmodified lines
423431 kvm_vgic_put(vcpu);
424432 kvm_vcpu_pmu_restore_host(vcpu);
425433
434+ vcpu_clear_on_unsupported_cpu(vcpu);
426435 vcpu->cpu = -1;
427436 }
428437
@@ -796,6 +805,14 @@ static bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu, int *ret) · 367 unmodified lines
796805 }
797806 }
798807
808+ if (unlikely(vcpu_on_unsupported_cpu(vcpu))) {
809+ run->exit_reason = KVM_EXIT_FAIL_ENTRY;
810+ run->fail_entry.hardware_entry_failure_reason = KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED;
811+ run->fail_entry.cpu = smp_processor_id();
812+ *ret = 0;
813+ return true;
814+ }
815+
799816 return kvm_request_pending(vcpu) ||
800817 need_new_vmid_gen(&vcpu->arch.hw_mmu->vmid) ||
801818 xfer_to_guest_mode_work_pending();
arch/arm64/kvm/pmu-emul.c+1−0
@@ -968,6 +968,7 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id) · 967 unmodified lines
968968 }
969969
970970 kvm->arch.arm_pmu = arm_pmu;
971+ cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus);
971972 ret = 0;
972973 break;
973974 }