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.
Unified · Split
| @@ -131,7 +131,11 @@ if a PMU event filter is already present. · 130 unmodified lines | ||
| 131 | 131 | |
| 132 | 132 | Note that KVM will not make any attempts to run the VCPU on the physical CPUs |
| 133 | 133 | 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. | |
| 135 | 139 | |
| 136 | 140 | 2. GROUP: KVM_ARM_VCPU_TIMER_CTRL |
| 137 | 141 | ================================= |
| @@ -131,6 +131,8 @@ struct kvm_arch { · 130 unmodified lines | ||
| 131 | 131 | unsigned long *pmu_filter; |
| 132 | 132 | struct arm_pmu *arm_pmu; |
| 133 | 133 | |
| 134 | + cpumask_var_t supported_cpus; | |
| 135 | + | |
| 134 | 136 | u8 pfr0_csv2; |
| 135 | 137 | u8 pfr0_csv3; |
| 136 | 138 | |
| @@ -436,6 +438,7 @@ struct kvm_vcpu_arch { · 299 unmodified lines | ||
| 436 | 438 | #define KVM_ARM64_DEBUG_STATE_SAVE_SPE (1 << 12) /* Save SPE context if active */ |
| 437 | 439 | #define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13) /* Save TRBE context if active */ |
| 438 | 440 | #define KVM_ARM64_FP_FOREIGN_FPSTATE (1 << 14) |
| 441 | +#define KVM_ARM64_ON_UNSUPPORTED_CPU (1 << 15) /* Physical CPU not in supported_cpus */ | |
| 439 | 442 | |
| 440 | 443 | #define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \ |
| 441 | 444 | KVM_GUESTDBG_USE_SW_BP | \ |
| @@ -454,6 +457,15 @@ struct kvm_vcpu_arch { · 12 unmodified lines | ||
| 454 | 457 | #define vcpu_has_ptrauth(vcpu) false |
| 455 | 458 | #endif |
| 456 | 459 | |
| 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 | + | |
| 457 | 469 | #define vcpu_gp_regs(v) (&(v)->arch.ctxt.regs) |
| 458 | 470 | |
| 459 | 471 | /* |
| @@ -414,6 +414,9 @@ struct kvm_arm_copy_mte_tags { · 413 unmodified lines | ||
| 414 | 414 | #define KVM_PSCI_RET_INVAL PSCI_RET_INVALID_PARAMS |
| 415 | 415 | #define KVM_PSCI_RET_DENIED PSCI_RET_DENIED |
| 416 | 416 | |
| 417 | +/* run->fail_entry.hardware_entry_failure_reason codes. */ | |
| 418 | +#define KVM_EXIT_FAIL_ENTRY_CPU_UNSUPPORTED (1ULL << 0) | |
| 419 | + | |
| 417 | 420 | #endif |
| 418 | 421 | |
| 419 | 422 | #endif /* __ARM_KVM_H__ */ |
| @@ -150,6 +150,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) · 149 unmodified lines | ||
| 150 | 150 | if (ret) |
| 151 | 151 | goto out_free_stage2_pgd; |
| 152 | 152 | |
| 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 | + | |
| 153 | 157 | kvm_vgic_early_init(kvm); |
| 154 | 158 | |
| 155 | 159 | /* 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 | ||
| 176 | 180 | void kvm_arch_destroy_vm(struct kvm *kvm) |
| 177 | 181 | { |
| 178 | 182 | bitmap_free(kvm->arch.pmu_filter); |
| 183 | + free_cpumask_var(kvm->arch.supported_cpus); | |
| 179 | 184 | |
| 180 | 185 | kvm_vgic_destroy(kvm); |
| 181 | 186 | |
| @@ -411,6 +416,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · 229 unmodified lines | ||
| 411 | 416 | if (vcpu_has_ptrauth(vcpu)) |
| 412 | 417 | vcpu_ptrauth_disable(vcpu); |
| 413 | 418 | 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); | |
| 414 | 422 | } |
| 415 | 423 | |
| 416 | 424 | 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 | ||
| 423 | 431 | kvm_vgic_put(vcpu); |
| 424 | 432 | kvm_vcpu_pmu_restore_host(vcpu); |
| 425 | 433 | |
| 434 | + vcpu_clear_on_unsupported_cpu(vcpu); | |
| 426 | 435 | vcpu->cpu = -1; |
| 427 | 436 | } |
| 428 | 437 | |
| @@ -796,6 +805,14 @@ static bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu, int *ret) · 367 unmodified lines | ||
| 796 | 805 | } |
| 797 | 806 | } |
| 798 | 807 | |
| 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 | + | |
| 799 | 816 | return kvm_request_pending(vcpu) || |
| 800 | 817 | need_new_vmid_gen(&vcpu->arch.hw_mmu->vmid) || |
| 801 | 818 | xfer_to_guest_mode_work_pending(); |
| @@ -968,6 +968,7 @@ static int kvm_arm_pmu_v3_set_pmu(struct kvm_vcpu *vcpu, int pmu_id) · 967 unmodified lines | ||
| 968 | 968 | } |
| 969 | 969 | |
| 970 | 970 | kvm->arch.arm_pmu = arm_pmu; |
| 971 | + cpumask_copy(kvm->arch.supported_cpus, &arm_pmu->supported_cpus); | |
| 971 | 972 | ret = 0; |
| 972 | 973 | break; |
| 973 | 974 | } |