Commit ec3eb9ed6081 ("KVM: arm64: PMU: Disallow vPMU on non-uniform
PMUVer") made KVM reject vPMU unless the system-wide PMUVer is usable.
That covers systems where PMUv3 is absent or non-uniform, as well as
systems where IMPDEF PMUv3 sysreg traps are unavailable.However, KVM can still accept vPMU when all CPUs uniformly trap PMUv3 sysregs, but the pPMUs registered with KVM only cover a subset of possible CPUs. Reject vPMU unless the registered pPMUs cover every possible CPU. This avoids carrying support for partial pPMU coverage into the fixed-counters-only UAPI introduced later in the series. Signed-off-by: Akihiko Odaki <[email protected]> --- arch/arm64/kvm/arm.c | 16 +++++++++++++--- arch/arm64/kvm/pmu-emul.c | 20 ++++++++++++++++++-- include/kvm/arm_pmu.h | 6 +++--- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 29f48f2c63ec..68767bb08285 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -1527,14 +1527,19 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, return -EINVAL; } -static unsigned long system_supported_vcpu_features(void) +static long system_supported_vcpu_features(void) { unsigned long features = KVM_VCPU_VALID_FEATURES; + int r; if (!cpus_have_final_cap(ARM64_HAS_32BIT_EL1)) clear_bit(KVM_ARM_VCPU_EL1_32BIT, &features); - if (!kvm_supports_guest_pmuv3()) + r = kvm_supports_guest_pmuv3(); + if (r < 0) + return r; + + if (!r) clear_bit(KVM_ARM_VCPU_PMU_V3, &features); if (!system_supports_sve()) @@ -1555,6 +1560,7 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu, const struct kvm_vcpu_init *init) { unsigned long features = init->features[0]; + long r; int i; if (features & ~KVM_VCPU_VALID_FEATURES) @@ -1565,7 +1571,11 @@ static int kvm_vcpu_init_check_features(struct kvm_vcpu *vcpu, return -ENOENT; } - if (features & ~system_supported_vcpu_features()) + r = system_supported_vcpu_features(); + if (r < 0) + return r; + + if (features & ~r) return -EINVAL; /* diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index c816db5d6761..f50bb9d9a1e7 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -24,10 +24,26 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc); static void kvm_pmu_release_perf_event(struct kvm_pmc *pmc); static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc); -bool kvm_supports_guest_pmuv3(void) +int kvm_supports_guest_pmuv3(void) { + cpumask_var_t cpus __free(free_cpumask_var) = CPUMASK_VAR_NULL; + struct arm_pmu_entry *entry; + + if (!alloc_cpumask_var(&cpus, GFP_KERNEL)) + return -ENOMEM; + + cpumask_copy(cpus, cpu_possible_mask); + guard(mutex)(&arm_pmus_lock); - return !list_empty(&arm_pmus); + + list_for_each_entry(entry, &arm_pmus, entry) { + struct arm_pmu *pmu = entry->arm_pmu; + + if (!cpumask_andnot(cpus, cpus, &pmu->supported_cpus)) + return 1; + } + + return 0; } static struct kvm_vcpu *kvm_pmc_to_vcpu(const struct kvm_pmc *pmc) diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 0a36a3d5c894..354f06edfdee 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -40,7 +40,7 @@ struct arm_pmu_entry { struct arm_pmu *arm_pmu; }; -bool kvm_supports_guest_pmuv3(void); +int kvm_supports_guest_pmuv3(void); #define kvm_arm_pmu_irq_initialized(v) ((v)->arch.pmu.irq_num != 0) u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx); void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val); @@ -99,9 +99,9 @@ void kvm_pmu_nested_transition(struct kvm_vcpu *vcpu); struct kvm_pmu { }; -static inline bool kvm_supports_guest_pmuv3(void) +static inline int kvm_supports_guest_pmuv3(void) { - return false; + return 0; } #define kvm_arm_pmu_irq_initialized(v) (false) -- 2.55.0

