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

oprofile, nmi-timer: 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 nmi-timer code in oprofile by using this latter form of callback
registration.

Cc: Robert Richter <rric@kernel.org>
Cc: Ingo Molnar <mingo@kernel.org>
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
180d8646 07494d54

+13 -10
+13 -10
drivers/oprofile/nmi_timer_int.c
··· 108 108 struct perf_event *event; 109 109 int cpu; 110 110 111 - get_online_cpus(); 112 - unregister_cpu_notifier(&nmi_timer_cpu_nb); 111 + cpu_notifier_register_begin(); 112 + __unregister_cpu_notifier(&nmi_timer_cpu_nb); 113 113 for_each_possible_cpu(cpu) { 114 114 event = per_cpu(nmi_timer_events, cpu); 115 115 if (!event) ··· 119 119 perf_event_release_kernel(event); 120 120 } 121 121 122 - put_online_cpus(); 122 + cpu_notifier_register_done(); 123 123 } 124 124 125 125 static int nmi_timer_setup(void) ··· 132 132 do_div(period, HZ); 133 133 nmi_timer_attr.sample_period = period; 134 134 135 - get_online_cpus(); 136 - err = register_cpu_notifier(&nmi_timer_cpu_nb); 135 + cpu_notifier_register_begin(); 136 + err = __register_cpu_notifier(&nmi_timer_cpu_nb); 137 137 if (err) 138 138 goto out; 139 + 139 140 /* can't attach events to offline cpus: */ 140 141 for_each_online_cpu(cpu) { 141 142 err = nmi_timer_start_cpu(cpu); 142 - if (err) 143 - break; 143 + if (err) { 144 + cpu_notifier_register_done(); 145 + nmi_timer_shutdown(); 146 + return err; 147 + } 144 148 } 145 - if (err) 146 - nmi_timer_shutdown(); 149 + 147 150 out: 148 - put_online_cpus(); 151 + cpu_notifier_register_done(); 149 152 return err; 150 153 } 151 154