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

intel-idle: 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 intel-idle code by using this latter form of callback registration.

Cc: Len Brown <lenb@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
07494d54 8daa127f

+10 -2
+10 -2
drivers/idle/intel_idle.c
··· 681 681 if (intel_idle_cpuidle_devices == NULL) 682 682 return -ENOMEM; 683 683 684 + cpu_notifier_register_begin(); 685 + 684 686 for_each_online_cpu(i) { 685 687 retval = intel_idle_cpu_init(i); 686 688 if (retval) { 689 + cpu_notifier_register_done(); 687 690 cpuidle_unregister_driver(&intel_idle_driver); 688 691 return retval; 689 692 } 690 693 } 691 - register_cpu_notifier(&cpu_hotplug_notifier); 694 + __register_cpu_notifier(&cpu_hotplug_notifier); 695 + 696 + cpu_notifier_register_done(); 692 697 693 698 return 0; 694 699 } ··· 703 698 intel_idle_cpuidle_devices_uninit(); 704 699 cpuidle_unregister_driver(&intel_idle_driver); 705 700 701 + cpu_notifier_register_begin(); 706 702 707 703 if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) 708 704 on_each_cpu(__setup_broadcast_timer, (void *)false, 1); 709 - unregister_cpu_notifier(&cpu_hotplug_notifier); 705 + __unregister_cpu_notifier(&cpu_hotplug_notifier); 706 + 707 + cpu_notifier_register_done(); 710 708 711 709 return; 712 710 }