[POWERPC] Fix smp_call_function to be preempt-safe
smp_call_function_map() was not safe against preemption to another
cpu: its test for removing self from map was outside the spinlock.
Rearrange it a little to fix that.
smp_call_function_single() was also wrong: now get_cpu() before
excluding self, as other architectures do.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 22f1ef1..d577b71 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -201,13 +201,6 @@
/* Can deadlock when called with interrupts disabled */
WARN_ON(irqs_disabled());
- /* remove 'self' from the map */
- if (cpu_isset(smp_processor_id(), map))
- cpu_clear(smp_processor_id(), map);
-
- /* sanity check the map, remove any non-online processors. */
- cpus_and(map, map, cpu_online_map);
-
if (unlikely(smp_ops == NULL))
return ret;
@@ -222,10 +215,17 @@
/* Must grab online cpu count with preempt disabled, otherwise
* it can change. */
num_cpus = num_online_cpus() - 1;
- if (!num_cpus || cpus_empty(map)) {
- ret = 0;
- goto out;
- }
+ if (!num_cpus)
+ goto done;
+
+ /* remove 'self' from the map */
+ if (cpu_isset(smp_processor_id(), map))
+ cpu_clear(smp_processor_id(), map);
+
+ /* sanity check the map, remove any non-online processors. */
+ cpus_and(map, map, cpu_online_map);
+ if (cpus_empty(map))
+ goto done;
call_data = &data;
smp_wmb();
@@ -263,6 +263,7 @@
}
}
+ done:
ret = 0;
out:
@@ -282,16 +283,17 @@
int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int nonatomic,
int wait)
{
- cpumask_t map=CPU_MASK_NONE;
+ cpumask_t map = CPU_MASK_NONE;
+ int ret = -EBUSY;
if (!cpu_online(cpu))
return -EINVAL;
- if (cpu == smp_processor_id())
- return -EBUSY;
-
cpu_set(cpu, map);
- return smp_call_function_map(func,info,nonatomic,wait,map);
+ if (cpu != get_cpu())
+ ret = smp_call_function_map(func,info,nonatomic,wait,map);
+ put_cpu();
+ return ret;
}
EXPORT_SYMBOL(smp_call_function_single);