Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

CPU hotplug, perf: Fix CPU hotplug callback registration

Subsystems that want to register CPU hotplug callbacks, as well as perform
initialization for the CPUs that are already online, often do it as shown
below:

get_online_cpus();

for_each_online_cpu(cpu)
init_cpu(cpu);

register_cpu_notifier(&foobar_cpu_notifier);

put_online_cpus();

This is wrong, since it is prone to ABBA deadlocks involving the
cpu_add_remove_lock and the cpu_hotplug.lock (when running concurrently
with CPU hotplug operations).

Instead, the correct and race-free way of performing the callback
registration is:

cpu_notifier_register_begin();

for_each_online_cpu(cpu)
init_cpu(cpu);

/* Note the use of the double underscored version of the API */
__register_cpu_notifier(&foobar_cpu_notifier);

cpu_notifier_register_done();

Fix the perf subsystem's hotplug notifier by using this latter form of
callback registration.

Also provide a bare-bones version of perf_cpu_notifier() that doesn't
invoke the notifiers for the already online CPUs. This would be useful
for subsystems that need to perform a different set of initialization
for the already online CPUs, or don't need the initialization altogether.

Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Srivatsa S. Bhat and committed by
Rafael J. Wysocki
f0bdb5e0 8489d90b

+15 -1
+15 -1
include/linux/perf_event.h
··· 835 835 { .notifier_call = fn, .priority = CPU_PRI_PERF }; \ 836 836 unsigned long cpu = smp_processor_id(); \ 837 837 unsigned long flags; \ 838 + \ 839 + cpu_notifier_register_begin(); \ 838 840 fn(&fn##_nb, (unsigned long)CPU_UP_PREPARE, \ 839 841 (void *)(unsigned long)cpu); \ 840 842 local_irq_save(flags); \ ··· 845 843 local_irq_restore(flags); \ 846 844 fn(&fn##_nb, (unsigned long)CPU_ONLINE, \ 847 845 (void *)(unsigned long)cpu); \ 848 - register_cpu_notifier(&fn##_nb); \ 846 + __register_cpu_notifier(&fn##_nb); \ 847 + cpu_notifier_register_done(); \ 849 848 } while (0) 850 849 850 + /* 851 + * Bare-bones version of perf_cpu_notifier(), which doesn't invoke the 852 + * callback for already online CPUs. 853 + */ 854 + #define __perf_cpu_notifier(fn) \ 855 + do { \ 856 + static struct notifier_block fn##_nb = \ 857 + { .notifier_call = fn, .priority = CPU_PRI_PERF }; \ 858 + \ 859 + __register_cpu_notifier(&fn##_nb); \ 860 + } while (0) 851 861 852 862 struct perf_pmu_events_attr { 853 863 struct device_attribute attr;