diff options
author | Hugh Dickins <hugh@veritas.com> | 2007-05-18 12:47:01 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-05-22 06:20:56 -0400 |
commit | d3fdaed9e973687f088c9c156a6e20870386e0b7 (patch) | |
tree | c08aab6356b9674f97f8bd1b5e1595ea09f705d6 | |
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>
-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 22f1ef1b3100..d577b71db375 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, | |||
201 | /* Can deadlock when called with interrupts disabled */ | 201 | /* Can deadlock when called with interrupts disabled */ |
202 | WARN_ON(irqs_disabled()); | 202 | WARN_ON(irqs_disabled()); |
203 | 203 | ||
204 | /* remove 'self' from the map */ | ||
205 | if (cpu_isset(smp_processor_id(), map)) | ||
206 | cpu_clear(smp_processor_id(), map); | ||
207 | |||
208 | /* sanity check the map, remove any non-online processors. */ | ||
209 | cpus_and(map, map, cpu_online_map); | ||
210 | |||
211 | if (unlikely(smp_ops == NULL)) | 204 | if (unlikely(smp_ops == NULL)) |
212 | return ret; | 205 | return ret; |
213 | 206 | ||
@@ -222,10 +215,17 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, | |||
222 | /* Must grab online cpu count with preempt disabled, otherwise | 215 | /* Must grab online cpu count with preempt disabled, otherwise |
223 | * it can change. */ | 216 | * it can change. */ |
224 | num_cpus = num_online_cpus() - 1; | 217 | num_cpus = num_online_cpus() - 1; |
225 | if (!num_cpus || cpus_empty(map)) { | 218 | if (!num_cpus) |
226 | ret = 0; | 219 | goto done; |
227 | goto out; | 220 | |
228 | } | 221 | /* remove 'self' from the map */ |
222 | if (cpu_isset(smp_processor_id(), map)) | ||
223 | cpu_clear(smp_processor_id(), map); | ||
224 | |||
225 | /* sanity check the map, remove any non-online processors. */ | ||
226 | cpus_and(map, map, cpu_online_map); | ||
227 | if (cpus_empty(map)) | ||
228 | goto done; | ||
229 | 229 | ||
230 | call_data = &data; | 230 | call_data = &data; |
231 | smp_wmb(); | 231 | smp_wmb(); |
@@ -263,6 +263,7 @@ int smp_call_function_map(void (*func) (void *info), void *info, int nonatomic, | |||
263 | } | 263 | } |
264 | } | 264 | } |
265 | 265 | ||
266 | done: | ||
266 | ret = 0; | 267 | ret = 0; |
267 | 268 | ||
268 | out: | 269 | out: |
@@ -282,16 +283,17 @@ EXPORT_SYMBOL(smp_call_function); | |||
282 | int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int nonatomic, | 283 | int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int nonatomic, |
283 | int wait) | 284 | int wait) |
284 | { | 285 | { |
285 | cpumask_t map=CPU_MASK_NONE; | 286 | cpumask_t map = CPU_MASK_NONE; |
287 | int ret = -EBUSY; | ||
286 | 288 | ||
287 | if (!cpu_online(cpu)) | 289 | if (!cpu_online(cpu)) |
288 | return -EINVAL; | 290 | return -EINVAL; |
289 | 291 | ||
290 | if (cpu == smp_processor_id()) | ||
291 | return -EBUSY; | ||
292 | |||
293 | cpu_set(cpu, map); | 292 | cpu_set(cpu, map); |
294 | return smp_call_function_map(func,info,nonatomic,wait,map); | 293 | if (cpu != get_cpu()) |
294 | ret = smp_call_function_map(func,info,nonatomic,wait,map); | ||
295 | put_cpu(); | ||
296 | return ret; | ||
295 | } | 297 | } |
296 | EXPORT_SYMBOL(smp_call_function_single); | 298 | EXPORT_SYMBOL(smp_call_function_single); |
297 | 299 | ||