diff options
author | Hugh Dickins <hugh@veritas.com> | 2007-05-19 02:47:01 +1000 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-05-22 20:20:56 +1000 |
commit | d3fdaed9e973687f088c9c156a6e20870386e0b7 (patch) | |
tree | c08aab6356b9674f97f8bd1b5e1595ea09f705d6 /arch/powerpc/kernel/smp.c | |
parent | d25790532370e7448e3d3bd25a17e1e9f1299816 (diff) |
[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>
Diffstat (limited to 'arch/powerpc/kernel/smp.c')
-rw-r--r-- | arch/powerpc/kernel/smp.c | 34 |
1 files changed, 18 insertions, 16 deletions
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 22f1ef1b310..d577b71db37 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -201,13 +201,6 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, /* 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 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, /* 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 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, } } + done: ret = 0; out: @@ -282,16 +283,17 @@ EXPORT_SYMBOL(smp_call_function); 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); |