[PATCH 14/15] perf: Disallow bulk unregistering of guest callbacks and do cleanup

From: Sean Christopherson
Date: Thu Aug 26 2021 - 20:59:32 EST


Drop the helper that allows bulk unregistering of the per-CPU callbacks
now that KVM, the only entity that actually unregisters callbacks, uses
the per-CPU helpers. Bulk unregistering is inherently unsafe as there
are no protections against nullifying a pointer for a CPU that is using
said pointer in a PMI handler.

Opportunistically tweak names to better reflect reality.

Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx>
---
arch/x86/xen/pmu.c | 2 +-
include/linux/kvm_host.h | 2 +-
include/linux/perf_event.h | 9 +++------
kernel/events/core.c | 31 +++++++++++--------------------
virt/kvm/kvm_main.c | 2 +-
5 files changed, 17 insertions(+), 29 deletions(-)

diff --git a/arch/x86/xen/pmu.c b/arch/x86/xen/pmu.c
index e13b0b49fcdf..57834de043c3 100644
--- a/arch/x86/xen/pmu.c
+++ b/arch/x86/xen/pmu.c
@@ -548,7 +548,7 @@ void xen_pmu_init(int cpu)
per_cpu(xenpmu_shared, cpu).flags = 0;

if (cpu == 0) {
- perf_register_guest_info_callbacks(&xen_guest_cbs);
+ perf_register_guest_info_callbacks_all_cpus(&xen_guest_cbs);
xen_pmu_arch_init();
}

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 0db9af0b628c..d68a49d5fc53 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -1171,7 +1171,7 @@ unsigned long kvm_arch_vcpu_get_ip(struct kvm_vcpu *vcpu);
void kvm_register_perf_callbacks(void);
static inline void kvm_unregister_perf_callbacks(void)
{
- __perf_unregister_guest_info_callbacks();
+ perf_unregister_guest_info_callbacks();
}
#endif

diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 7a367bf1b78d..db701409a62f 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1238,10 +1238,9 @@ extern void perf_event_bpf_event(struct bpf_prog *prog,

#ifdef CONFIG_HAVE_GUEST_PERF_EVENTS
DECLARE_PER_CPU(struct perf_guest_info_callbacks *, perf_guest_cbs);
-extern void __perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs);
-extern void __perf_unregister_guest_info_callbacks(void);
-extern void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks);
+extern void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs);
extern void perf_unregister_guest_info_callbacks(void);
+extern void perf_register_guest_info_callbacks_all_cpus(struct perf_guest_info_callbacks *cbs);
#endif /* CONFIG_HAVE_GUEST_PERF_EVENTS */

extern void perf_event_exec(void);
@@ -1486,9 +1485,7 @@ static inline void
perf_bp_event(struct perf_event *event, void *data) { }

#ifdef CONFIG_HAVE_GUEST_PERF_EVENTS
-static inline void perf_register_guest_info_callbacks
-(struct perf_guest_info_callbacks *callbacks) { }
-static inline void perf_unregister_guest_info_callbacks(void) { }
+extern void perf_register_guest_info_callbacks_all_cpus(struct perf_guest_info_callbacks *cbs);
#endif

static inline void perf_event_mmap(struct vm_area_struct *vma) { }
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 2f28d9d8dc94..f1964096c4c2 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6485,35 +6485,26 @@ static void perf_pending_event(struct irq_work *entry)
#ifdef CONFIG_HAVE_GUEST_PERF_EVENTS
DEFINE_PER_CPU(struct perf_guest_info_callbacks *, perf_guest_cbs);

-void __perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
-{
- __this_cpu_write(perf_guest_cbs, cbs);
-}
-EXPORT_SYMBOL_GPL(__perf_register_guest_info_callbacks);
-
-void __perf_unregister_guest_info_callbacks(void)
-{
- __this_cpu_write(perf_guest_cbs, NULL);
-}
-EXPORT_SYMBOL_GPL(__perf_unregister_guest_info_callbacks);
-
void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
{
- int cpu;
-
- for_each_possible_cpu(cpu)
- per_cpu(perf_guest_cbs, cpu) = cbs;
+ __this_cpu_write(perf_guest_cbs, cbs);
}
EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks);

void perf_unregister_guest_info_callbacks(void)
{
- int cpu;
-
- for_each_possible_cpu(cpu)
- per_cpu(perf_guest_cbs, cpu) = NULL;
+ __this_cpu_write(perf_guest_cbs, NULL);
}
EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
+
+void perf_register_guest_info_callbacks_all_cpus(struct perf_guest_info_callbacks *cbs)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ per_cpu(perf_guest_cbs, cpu) = cbs;
+}
+EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks_all_cpus);
#endif

static void
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index e0b1c9386926..1bcc3eab510b 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -5502,7 +5502,7 @@ EXPORT_SYMBOL_GPL(kvm_set_intel_pt_intr_handler);

void kvm_register_perf_callbacks(void)
{
- __perf_register_guest_info_callbacks(&kvm_guest_cbs);
+ perf_register_guest_info_callbacks(&kvm_guest_cbs);
}
EXPORT_SYMBOL_GPL(kvm_register_perf_callbacks);
#endif
--
2.33.0.259.gc128427fd7-goog