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

powerpc: Fix broken cpu_idle_wait() implementation

commit 771dae818 (powerpc/cpuidle: Add cpu_idle_wait() to allow
switching of idle routines) implemented cpu_idle_wait() for powerpc.

The changelog says:
"The equivalent routine for x86 is in arch/x86/kernel/process.c
but the powerpc implementation is different.":

Unfortunately the changelog is completely useless as it does not tell
_WHY_ it is different.

Aside of being different the implementation is patently wrong.

The rescheduling IPI is async. That means that there is no guarantee,
that the other cores have executed the IPI when cpu_idle_wait()
returns. But that's the whole purpose of this function: to guarantee
that no CPU uses the old idle handler anymore.

Use the smp_functional_call() based implementation, which fulfils the
requirements.

[ This code is going to replaced by a core version to remove all the
pointless copies in arch/*, but this one should go to stable ]

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra <peterz@infradead.org>
Cc: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Cc: Trinabh Gupta <g.trinabh@gmail.com>
Cc: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Link: http://lkml.kernel.org/r/20120507175651.980164748@linutronix.de
Cc: stable@vger.kernel.org

+5 -9
+5 -9
arch/powerpc/kernel/idle.c
··· 113 113 } 114 114 } 115 115 116 + static void do_nothing(void *unused) 117 + { 118 + } 116 119 117 120 /* 118 121 * cpu_idle_wait - Used to ensure that all the CPUs come out of the old ··· 126 123 */ 127 124 void cpu_idle_wait(void) 128 125 { 129 - int cpu; 130 126 smp_mb(); 131 - 132 - /* kick all the CPUs so that they exit out of old idle routine */ 133 - get_online_cpus(); 134 - for_each_online_cpu(cpu) { 135 - if (cpu != smp_processor_id()) 136 - smp_send_reschedule(cpu); 137 - } 138 - put_online_cpus(); 127 + /* kick all the CPUs so that they exit out of pm_idle */ 128 + smp_call_function(do_nothing, NULL, 1); 139 129 } 140 130 EXPORT_SYMBOL_GPL(cpu_idle_wait); 141 131