x86: events: Do not return bogus capabilities if PMU is broken
authorPaolo Bonzini <pbonzini@redhat.com>
Wed, 1 Jun 2022 09:45:17 +0000 (05:45 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Wed, 8 Jun 2022 17:06:15 +0000 (13:06 -0400)
If the PMU is broken due to firmware issues, check_hw_exists() will return
false but perf_get_x86_pmu_capability() will still return data from x86_pmu.
Likewise if some of the hotplug callbacks cannot be installed the contents
of x86_pmu will not be reverted.

Handle the failure in both cases by clearing x86_pmu if init_hw_perf_events()
or reverts to software events only.

Co-developed-by: Like Xu <likexu@tencent.com>
Signed-off-by: Like Xu <likexu@tencent.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/events/core.c

index 2e16c26..f969410 100644 (file)
@@ -2103,14 +2103,15 @@ static int __init init_hw_perf_events(void)
        }
        if (err != 0) {
                pr_cont("no PMU driver, software events only.\n");
-               return 0;
+               err = 0;
+               goto out_bad_pmu;
        }
 
        pmu_check_apic();
 
        /* sanity check that the hardware exists or is emulated */
        if (!check_hw_exists(&pmu, x86_pmu.num_counters, x86_pmu.num_counters_fixed))
-               return 0;
+               goto out_bad_pmu;
 
        pr_cont("%s PMU driver.\n", x86_pmu.name);
 
@@ -2219,6 +2220,8 @@ out1:
        cpuhp_remove_state(CPUHP_AP_PERF_X86_STARTING);
 out:
        cpuhp_remove_state(CPUHP_PERF_X86_PREPARE);
+out_bad_pmu:
+       memset(&x86_pmu, 0, sizeof(x86_pmu));
        return err;
 }
 early_initcall(init_hw_perf_events);
@@ -2990,6 +2993,11 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
 
 void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
 {
+       if (!x86_pmu_initialized()) {
+               memset(cap, 0, sizeof(*cap));
+               return;
+       }
+
        cap->version            = x86_pmu.version;
        /*
         * KVM doesn't support the hybrid PMU yet.