diff options
Diffstat (limited to 'kernel/smp.c')
| -rw-r--r-- | kernel/smp.c | 46 |
1 files changed, 33 insertions, 13 deletions
diff --git a/kernel/smp.c b/kernel/smp.c index aaeee20c563..7c6ded5effd 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); |
