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


Reply via email to