diff options
Diffstat (limited to 'kernel/smp.c')
-rw-r--r-- | kernel/smp.c | 72 |
1 files changed, 60 insertions, 12 deletions
diff --git a/kernel/smp.c b/kernel/smp.c index 462c785ca1ee..f362a8553777 100644 --- a/kernel/smp.c +++ b/kernel/smp.c | |||
@@ -33,7 +33,7 @@ struct call_single_queue { | |||
33 | spinlock_t lock; | 33 | spinlock_t lock; |
34 | }; | 34 | }; |
35 | 35 | ||
36 | void __cpuinit init_call_single_data(void) | 36 | static int __cpuinit init_call_single_data(void) |
37 | { | 37 | { |
38 | int i; | 38 | int i; |
39 | 39 | ||
@@ -43,7 +43,9 @@ void __cpuinit init_call_single_data(void) | |||
43 | spin_lock_init(&q->lock); | 43 | spin_lock_init(&q->lock); |
44 | INIT_LIST_HEAD(&q->list); | 44 | INIT_LIST_HEAD(&q->list); |
45 | } | 45 | } |
46 | return 0; | ||
46 | } | 47 | } |
48 | early_initcall(init_call_single_data); | ||
47 | 49 | ||
48 | static void csd_flag_wait(struct call_single_data *data) | 50 | static void csd_flag_wait(struct call_single_data *data) |
49 | { | 51 | { |
@@ -133,7 +135,8 @@ void generic_smp_call_function_interrupt(void) | |||
133 | */ | 135 | */ |
134 | smp_wmb(); | 136 | smp_wmb(); |
135 | data->csd.flags &= ~CSD_FLAG_WAIT; | 137 | data->csd.flags &= ~CSD_FLAG_WAIT; |
136 | } else | 138 | } |
139 | if (data->csd.flags & CSD_FLAG_ALLOC) | ||
137 | call_rcu(&data->rcu_head, rcu_free_call_data); | 140 | call_rcu(&data->rcu_head, rcu_free_call_data); |
138 | } | 141 | } |
139 | rcu_read_unlock(); | 142 | rcu_read_unlock(); |
@@ -207,8 +210,10 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, | |||
207 | { | 210 | { |
208 | struct call_single_data d; | 211 | struct call_single_data d; |
209 | unsigned long flags; | 212 | unsigned long flags; |
210 | /* prevent preemption and reschedule on another processor */ | 213 | /* prevent preemption and reschedule on another processor, |
214 | as well as CPU removal */ | ||
211 | int me = get_cpu(); | 215 | int me = get_cpu(); |
216 | int err = 0; | ||
212 | 217 | ||
213 | /* Can deadlock when called with interrupts disabled */ | 218 | /* Can deadlock when called with interrupts disabled */ |
214 | WARN_ON(irqs_disabled()); | 219 | WARN_ON(irqs_disabled()); |
@@ -217,7 +222,7 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, | |||
217 | local_irq_save(flags); | 222 | local_irq_save(flags); |
218 | func(info); | 223 | func(info); |
219 | local_irq_restore(flags); | 224 | local_irq_restore(flags); |
220 | } else { | 225 | } else if ((unsigned)cpu < NR_CPUS && cpu_online(cpu)) { |
221 | struct call_single_data *data = NULL; | 226 | struct call_single_data *data = NULL; |
222 | 227 | ||
223 | if (!wait) { | 228 | if (!wait) { |
@@ -233,10 +238,12 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, | |||
233 | data->func = func; | 238 | data->func = func; |
234 | data->info = info; | 239 | data->info = info; |
235 | generic_exec_single(cpu, data); | 240 | generic_exec_single(cpu, data); |
241 | } else { | ||
242 | err = -ENXIO; /* CPU not online */ | ||
236 | } | 243 | } |
237 | 244 | ||
238 | put_cpu(); | 245 | put_cpu(); |
239 | return 0; | 246 | return err; |
240 | } | 247 | } |
241 | EXPORT_SYMBOL(smp_call_function_single); | 248 | EXPORT_SYMBOL(smp_call_function_single); |
242 | 249 | ||
@@ -258,6 +265,42 @@ void __smp_call_function_single(int cpu, struct call_single_data *data) | |||
258 | generic_exec_single(cpu, data); | 265 | generic_exec_single(cpu, data); |
259 | } | 266 | } |
260 | 267 | ||
268 | /* Dummy function */ | ||
269 | static void quiesce_dummy(void *unused) | ||
270 | { | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * Ensure stack based data used in call function mask is safe to free. | ||
275 | * | ||
276 | * This is needed by smp_call_function_mask when using on-stack data, because | ||
277 | * a single call function queue is shared by all CPUs, and any CPU may pick up | ||
278 | * the data item on the queue at any time before it is deleted. So we need to | ||
279 | * ensure that all CPUs have transitioned through a quiescent state after | ||
280 | * this call. | ||
281 | * | ||
282 | * This is a very slow function, implemented by sending synchronous IPIs to | ||
283 | * all possible CPUs. For this reason, we have to alloc data rather than use | ||
284 | * stack based data even in the case of synchronous calls. The stack based | ||
285 | * data is then just used for deadlock/oom fallback which will be very rare. | ||
286 | * | ||
287 | * If a faster scheme can be made, we could go back to preferring stack based | ||
288 | * data -- the data allocation/free is non-zero cost. | ||
289 | */ | ||
290 | static void smp_call_function_mask_quiesce_stack(cpumask_t mask) | ||
291 | { | ||
292 | struct call_single_data data; | ||
293 | int cpu; | ||
294 | |||
295 | data.func = quiesce_dummy; | ||
296 | data.info = NULL; | ||
297 | |||
298 | for_each_cpu_mask(cpu, mask) { | ||
299 | data.flags = CSD_FLAG_WAIT; | ||
300 | generic_exec_single(cpu, &data); | ||
301 | } | ||
302 | } | ||
303 | |||
261 | /** | 304 | /** |
262 | * smp_call_function_mask(): Run a function on a set of other CPUs. | 305 | * smp_call_function_mask(): Run a function on a set of other CPUs. |
263 | * @mask: The set of cpus to run on. | 306 | * @mask: The set of cpus to run on. |
@@ -283,6 +326,7 @@ int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, | |||
283 | cpumask_t allbutself; | 326 | cpumask_t allbutself; |
284 | unsigned long flags; | 327 | unsigned long flags; |
285 | int cpu, num_cpus; | 328 | int cpu, num_cpus; |
329 | int slowpath = 0; | ||
286 | 330 | ||
287 | /* Can deadlock when called with interrupts disabled */ | 331 | /* Can deadlock when called with interrupts disabled */ |
288 | WARN_ON(irqs_disabled()); | 332 | WARN_ON(irqs_disabled()); |
@@ -304,15 +348,16 @@ int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, | |||
304 | return smp_call_function_single(cpu, func, info, wait); | 348 | return smp_call_function_single(cpu, func, info, wait); |
305 | } | 349 | } |
306 | 350 | ||
307 | if (!wait) { | 351 | data = kmalloc(sizeof(*data), GFP_ATOMIC); |
308 | data = kmalloc(sizeof(*data), GFP_ATOMIC); | 352 | if (data) { |
309 | if (data) | 353 | data->csd.flags = CSD_FLAG_ALLOC; |
310 | data->csd.flags = CSD_FLAG_ALLOC; | 354 | if (wait) |
311 | } | 355 | data->csd.flags |= CSD_FLAG_WAIT; |
312 | if (!data) { | 356 | } else { |
313 | data = &d; | 357 | data = &d; |
314 | data->csd.flags = CSD_FLAG_WAIT; | 358 | data->csd.flags = CSD_FLAG_WAIT; |
315 | wait = 1; | 359 | wait = 1; |
360 | slowpath = 1; | ||
316 | } | 361 | } |
317 | 362 | ||
318 | spin_lock_init(&data->lock); | 363 | spin_lock_init(&data->lock); |
@@ -329,8 +374,11 @@ int smp_call_function_mask(cpumask_t mask, void (*func)(void *), void *info, | |||
329 | arch_send_call_function_ipi(mask); | 374 | arch_send_call_function_ipi(mask); |
330 | 375 | ||
331 | /* optionally wait for the CPUs to complete */ | 376 | /* optionally wait for the CPUs to complete */ |
332 | if (wait) | 377 | if (wait) { |
333 | csd_flag_wait(&data->csd); | 378 | csd_flag_wait(&data->csd); |
379 | if (unlikely(slowpath)) | ||
380 | smp_call_function_mask_quiesce_stack(mask); | ||
381 | } | ||
334 | 382 | ||
335 | return 0; | 383 | return 0; |
336 | } | 384 | } |