aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/smp.c')
-rw-r--r--kernel/smp.c46
1 files changed, 33 insertions, 13 deletions
diff --git a/kernel/smp.c b/kernel/smp.c
index aaeee20c5634..7c6ded5effd9 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -483,23 +483,42 @@ void smp_call_function_many(const struct cpumask *mask,
483 483
484 data = &__get_cpu_var(cfd_data); 484 data = &__get_cpu_var(cfd_data);
485 csd_lock(&data->csd); 485 csd_lock(&data->csd);
486 BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask));
487 486
488 data->csd.func = func; 487 /* This BUG_ON verifies our reuse assertions and can be removed */
489 data->csd.info = info; 488 BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask));
490 cpumask_and(data->cpumask, mask, cpu_online_mask);
491 cpumask_clear_cpu(this_cpu, data->cpumask);
492 489
493 /* 490 /*
491 * The global call function queue list add and delete are protected
492 * by a lock, but the list is traversed without any lock, relying
493 * on the rcu list add and delete to allow safe concurrent traversal.
494 * We reuse the call function data without waiting for any grace 494 * We reuse the call function data without waiting for any grace
495 * period after some other cpu removes it from the global queue. 495 * period after some other cpu removes it from the global queue.
496 * This means a cpu might find our data block as it is writen. 496 * This means a cpu might find our data block as it is being
497 * The interrupt handler waits until it sees refs filled out 497 * filled out.
498 * while its cpu mask bit is set; here we may only clear our 498 *
499 * own cpu mask bit, and must wait to set refs until we are sure 499 * We hold off the interrupt handler on the other cpu by
500 * previous writes are complete and we have obtained the lock to 500 * ordering our writes to the cpu mask vs our setting of the
501 * add the element to the queue. 501 * refs counter. We assert only the cpu owning the data block
502 * will set a bit in cpumask, and each bit will only be cleared
503 * by the subject cpu. Each cpu must first find its bit is
504 * set and then check that refs is set indicating the element is
505 * ready to be processed, otherwise it must skip the entry.
506 *
507 * On the previous iteration refs was set to 0 by another cpu.
508 * To avoid the use of transitivity, set the counter to 0 here
509 * so the wmb will pair with the rmb in the interrupt handler.
502 */ 510 */
511 atomic_set(&data->refs, 0); /* convert 3rd to 1st party write */
512
513 data->csd.func = func;
514 data->csd.info = info;
515
516 /* Ensure 0 refs is visible before mask. Also orders func and info */
517 smp_wmb();
518
519 /* We rely on the "and" being processed before the store */
520 cpumask_and(data->cpumask, mask, cpu_online_mask);
521 cpumask_clear_cpu(this_cpu, data->cpumask);
503 522
504 raw_spin_lock_irqsave(&call_function.lock, flags); 523 raw_spin_lock_irqsave(&call_function.lock, flags);
505 /* 524 /*
@@ -509,8 +528,9 @@ void smp_call_function_many(const struct cpumask *mask,
509 */ 528 */
510 list_add_rcu(&data->csd.list, &call_function.queue); 529 list_add_rcu(&data->csd.list, &call_function.queue);
511 /* 530 /*
512 * We rely on the wmb() in list_add_rcu to order the writes 531 * We rely on the wmb() in list_add_rcu to complete our writes
513 * to func, data, and cpumask before this write to refs. 532 * to the cpumask before this write to refs, which indicates
533 * data is on the list and is ready to be processed.
514 */ 534 */
515 atomic_set(&data->refs, cpumask_weight(data->cpumask)); 535 atomic_set(&data->refs, cpumask_weight(data->cpumask));
516 raw_spin_unlock_irqrestore(&call_function.lock, flags); 536 raw_spin_unlock_irqrestore(&call_function.lock, flags);