aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/smp.c
diff options
context:
space:
mode:
authorMilton Miller <miltonm@bga.com>2011-03-15 15:27:17 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-17 19:58:10 -0400
commit723aae25d5cdb09962901d36d526b44d4be1051c (patch)
tree0e2a83848e9b7884d69bfbbeba77283b531ba2ea /kernel/smp.c
parent45a5791920ae643eafc02e2eedef1a58e341b736 (diff)
smp_call_function_many: handle concurrent clearing of mask
Mike Galbraith reported finding a lockup ("perma-spin bug") where the cpumask passed to smp_call_function_many was cleared by other cpu(s) while a cpu was preparing its call_data block, resulting in no cpu to clear the last ref and unlock the block. Having cpus clear their bit asynchronously could be useful on a mask of cpus that might have a translation context, or cpus that need a push to complete an rcu window. Instead of adding a BUG_ON and requiring yet another cpumask copy, just detect the race and handle it. Note: arch_send_call_function_ipi_mask must still handle an empty cpumask because the data block is globally visible before the that arch callback is made. And (obviously) there are no guarantees to which cpus are notified if the mask is changed during the call; only cpus that were online and had their mask bit set during the whole call are guaranteed to be called. Reported-by: Mike Galbraith <efault@gmx.de> Reported-by: Jan Beulich <JBeulich@novell.com> Acked-by: Jan Beulich <jbeulich@novell.com> Cc: stable@kernel.org Signed-off-by: Milton Miller <miltonm@bga.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/smp.c')
-rw-r--r--kernel/smp.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/kernel/smp.c b/kernel/smp.c
index 7c6ded5effd9..954548906afb 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -450,7 +450,7 @@ void smp_call_function_many(const struct cpumask *mask,
450{ 450{
451 struct call_function_data *data; 451 struct call_function_data *data;
452 unsigned long flags; 452 unsigned long flags;
453 int cpu, next_cpu, this_cpu = smp_processor_id(); 453 int refs, cpu, next_cpu, this_cpu = smp_processor_id();
454 454
455 /* 455 /*
456 * Can deadlock when called with interrupts disabled. 456 * Can deadlock when called with interrupts disabled.
@@ -461,7 +461,7 @@ void smp_call_function_many(const struct cpumask *mask,
461 WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled() 461 WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled()
462 && !oops_in_progress && !early_boot_irqs_disabled); 462 && !oops_in_progress && !early_boot_irqs_disabled);
463 463
464 /* So, what's a CPU they want? Ignoring this one. */ 464 /* Try to fastpath. So, what's a CPU they want? Ignoring this one. */
465 cpu = cpumask_first_and(mask, cpu_online_mask); 465 cpu = cpumask_first_and(mask, cpu_online_mask);
466 if (cpu == this_cpu) 466 if (cpu == this_cpu)
467 cpu = cpumask_next_and(cpu, mask, cpu_online_mask); 467 cpu = cpumask_next_and(cpu, mask, cpu_online_mask);
@@ -519,6 +519,13 @@ void smp_call_function_many(const struct cpumask *mask,
519 /* We rely on the "and" being processed before the store */ 519 /* We rely on the "and" being processed before the store */
520 cpumask_and(data->cpumask, mask, cpu_online_mask); 520 cpumask_and(data->cpumask, mask, cpu_online_mask);
521 cpumask_clear_cpu(this_cpu, data->cpumask); 521 cpumask_clear_cpu(this_cpu, data->cpumask);
522 refs = cpumask_weight(data->cpumask);
523
524 /* Some callers race with other cpus changing the passed mask */
525 if (unlikely(!refs)) {
526 csd_unlock(&data->csd);
527 return;
528 }
522 529
523 raw_spin_lock_irqsave(&call_function.lock, flags); 530 raw_spin_lock_irqsave(&call_function.lock, flags);
524 /* 531 /*
@@ -532,7 +539,7 @@ void smp_call_function_many(const struct cpumask *mask,
532 * to the cpumask before this write to refs, which indicates 539 * to the cpumask before this write to refs, which indicates
533 * data is on the list and is ready to be processed. 540 * data is on the list and is ready to be processed.
534 */ 541 */
535 atomic_set(&data->refs, cpumask_weight(data->cpumask)); 542 atomic_set(&data->refs, refs);
536 raw_spin_unlock_irqrestore(&call_function.lock, flags); 543 raw_spin_unlock_irqrestore(&call_function.lock, flags);
537 544
538 /* 545 /*