diff options
Diffstat (limited to 'kernel/rcu/tree.c')
-rw-r--r-- | kernel/rcu/tree.c | 72 |
1 files changed, 19 insertions, 53 deletions
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 338ea61929bd..44245ae4c1c2 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c | |||
@@ -3568,10 +3568,10 @@ static void rcu_barrier_callback(struct rcu_head *rhp) | |||
3568 | struct rcu_state *rsp = rdp->rsp; | 3568 | struct rcu_state *rsp = rdp->rsp; |
3569 | 3569 | ||
3570 | if (atomic_dec_and_test(&rsp->barrier_cpu_count)) { | 3570 | if (atomic_dec_and_test(&rsp->barrier_cpu_count)) { |
3571 | _rcu_barrier_trace(rsp, "LastCB", -1, rsp->n_barrier_done); | 3571 | _rcu_barrier_trace(rsp, "LastCB", -1, rsp->barrier_sequence); |
3572 | complete(&rsp->barrier_completion); | 3572 | complete(&rsp->barrier_completion); |
3573 | } else { | 3573 | } else { |
3574 | _rcu_barrier_trace(rsp, "CB", -1, rsp->n_barrier_done); | 3574 | _rcu_barrier_trace(rsp, "CB", -1, rsp->barrier_sequence); |
3575 | } | 3575 | } |
3576 | } | 3576 | } |
3577 | 3577 | ||
@@ -3583,7 +3583,7 @@ static void rcu_barrier_func(void *type) | |||
3583 | struct rcu_state *rsp = type; | 3583 | struct rcu_state *rsp = type; |
3584 | struct rcu_data *rdp = raw_cpu_ptr(rsp->rda); | 3584 | struct rcu_data *rdp = raw_cpu_ptr(rsp->rda); |
3585 | 3585 | ||
3586 | _rcu_barrier_trace(rsp, "IRQ", -1, rsp->n_barrier_done); | 3586 | _rcu_barrier_trace(rsp, "IRQ", -1, rsp->barrier_sequence); |
3587 | atomic_inc(&rsp->barrier_cpu_count); | 3587 | atomic_inc(&rsp->barrier_cpu_count); |
3588 | rsp->call(&rdp->barrier_head, rcu_barrier_callback); | 3588 | rsp->call(&rdp->barrier_head, rcu_barrier_callback); |
3589 | } | 3589 | } |
@@ -3596,55 +3596,24 @@ static void _rcu_barrier(struct rcu_state *rsp) | |||
3596 | { | 3596 | { |
3597 | int cpu; | 3597 | int cpu; |
3598 | struct rcu_data *rdp; | 3598 | struct rcu_data *rdp; |
3599 | unsigned long snap = READ_ONCE(rsp->n_barrier_done); | 3599 | unsigned long s = rcu_seq_snap(&rsp->barrier_sequence); |
3600 | unsigned long snap_done; | ||
3601 | 3600 | ||
3602 | _rcu_barrier_trace(rsp, "Begin", -1, snap); | 3601 | _rcu_barrier_trace(rsp, "Begin", -1, s); |
3603 | 3602 | ||
3604 | /* Take mutex to serialize concurrent rcu_barrier() requests. */ | 3603 | /* Take mutex to serialize concurrent rcu_barrier() requests. */ |
3605 | mutex_lock(&rsp->barrier_mutex); | 3604 | mutex_lock(&rsp->barrier_mutex); |
3606 | 3605 | ||
3607 | /* | 3606 | /* Did someone else do our work for us? */ |
3608 | * Ensure that all prior references, including to ->n_barrier_done, | 3607 | if (rcu_seq_done(&rsp->barrier_sequence, s)) { |
3609 | * are ordered before the _rcu_barrier() machinery. | 3608 | _rcu_barrier_trace(rsp, "EarlyExit", -1, rsp->barrier_sequence); |
3610 | */ | ||
3611 | smp_mb(); /* See above block comment. */ | ||
3612 | |||
3613 | /* | ||
3614 | * Recheck ->n_barrier_done to see if others did our work for us. | ||
3615 | * This means checking ->n_barrier_done for an even-to-odd-to-even | ||
3616 | * transition. The "if" expression below therefore rounds the old | ||
3617 | * value up to the next even number and adds two before comparing. | ||
3618 | */ | ||
3619 | snap_done = rsp->n_barrier_done; | ||
3620 | _rcu_barrier_trace(rsp, "Check", -1, snap_done); | ||
3621 | |||
3622 | /* | ||
3623 | * If the value in snap is odd, we needed to wait for the current | ||
3624 | * rcu_barrier() to complete, then wait for the next one, in other | ||
3625 | * words, we need the value of snap_done to be three larger than | ||
3626 | * the value of snap. On the other hand, if the value in snap is | ||
3627 | * even, we only had to wait for the next rcu_barrier() to complete, | ||
3628 | * in other words, we need the value of snap_done to be only two | ||
3629 | * greater than the value of snap. The "(snap + 3) & ~0x1" computes | ||
3630 | * this for us (thank you, Linus!). | ||
3631 | */ | ||
3632 | if (ULONG_CMP_GE(snap_done, (snap + 3) & ~0x1)) { | ||
3633 | _rcu_barrier_trace(rsp, "EarlyExit", -1, snap_done); | ||
3634 | smp_mb(); /* caller's subsequent code after above check. */ | 3609 | smp_mb(); /* caller's subsequent code after above check. */ |
3635 | mutex_unlock(&rsp->barrier_mutex); | 3610 | mutex_unlock(&rsp->barrier_mutex); |
3636 | return; | 3611 | return; |
3637 | } | 3612 | } |
3638 | 3613 | ||
3639 | /* | 3614 | /* Mark the start of the barrier operation. */ |
3640 | * Increment ->n_barrier_done to avoid duplicate work. Use | 3615 | rcu_seq_start(&rsp->barrier_sequence); |
3641 | * WRITE_ONCE() to prevent the compiler from speculating | 3616 | _rcu_barrier_trace(rsp, "Inc1", -1, rsp->barrier_sequence); |
3642 | * the increment to precede the early-exit check. | ||
3643 | */ | ||
3644 | WRITE_ONCE(rsp->n_barrier_done, rsp->n_barrier_done + 1); | ||
3645 | WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 1); | ||
3646 | _rcu_barrier_trace(rsp, "Inc1", -1, rsp->n_barrier_done); | ||
3647 | smp_mb(); /* Order ->n_barrier_done increment with below mechanism. */ | ||
3648 | 3617 | ||
3649 | /* | 3618 | /* |
3650 | * Initialize the count to one rather than to zero in order to | 3619 | * Initialize the count to one rather than to zero in order to |
@@ -3668,10 +3637,10 @@ static void _rcu_barrier(struct rcu_state *rsp) | |||
3668 | if (rcu_is_nocb_cpu(cpu)) { | 3637 | if (rcu_is_nocb_cpu(cpu)) { |
3669 | if (!rcu_nocb_cpu_needs_barrier(rsp, cpu)) { | 3638 | if (!rcu_nocb_cpu_needs_barrier(rsp, cpu)) { |
3670 | _rcu_barrier_trace(rsp, "OfflineNoCB", cpu, | 3639 | _rcu_barrier_trace(rsp, "OfflineNoCB", cpu, |
3671 | rsp->n_barrier_done); | 3640 | rsp->barrier_sequence); |
3672 | } else { | 3641 | } else { |
3673 | _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, | 3642 | _rcu_barrier_trace(rsp, "OnlineNoCB", cpu, |
3674 | rsp->n_barrier_done); | 3643 | rsp->barrier_sequence); |
3675 | smp_mb__before_atomic(); | 3644 | smp_mb__before_atomic(); |
3676 | atomic_inc(&rsp->barrier_cpu_count); | 3645 | atomic_inc(&rsp->barrier_cpu_count); |
3677 | __call_rcu(&rdp->barrier_head, | 3646 | __call_rcu(&rdp->barrier_head, |
@@ -3679,11 +3648,11 @@ static void _rcu_barrier(struct rcu_state *rsp) | |||
3679 | } | 3648 | } |
3680 | } else if (READ_ONCE(rdp->qlen)) { | 3649 | } else if (READ_ONCE(rdp->qlen)) { |
3681 | _rcu_barrier_trace(rsp, "OnlineQ", cpu, | 3650 | _rcu_barrier_trace(rsp, "OnlineQ", cpu, |
3682 | rsp->n_barrier_done); | 3651 | rsp->barrier_sequence); |
3683 | smp_call_function_single(cpu, rcu_barrier_func, rsp, 1); | 3652 | smp_call_function_single(cpu, rcu_barrier_func, rsp, 1); |
3684 | } else { | 3653 | } else { |
3685 | _rcu_barrier_trace(rsp, "OnlineNQ", cpu, | 3654 | _rcu_barrier_trace(rsp, "OnlineNQ", cpu, |
3686 | rsp->n_barrier_done); | 3655 | rsp->barrier_sequence); |
3687 | } | 3656 | } |
3688 | } | 3657 | } |
3689 | put_online_cpus(); | 3658 | put_online_cpus(); |
@@ -3695,16 +3664,13 @@ static void _rcu_barrier(struct rcu_state *rsp) | |||
3695 | if (atomic_dec_and_test(&rsp->barrier_cpu_count)) | 3664 | if (atomic_dec_and_test(&rsp->barrier_cpu_count)) |
3696 | complete(&rsp->barrier_completion); | 3665 | complete(&rsp->barrier_completion); |
3697 | 3666 | ||
3698 | /* Increment ->n_barrier_done to prevent duplicate work. */ | ||
3699 | smp_mb(); /* Keep increment after above mechanism. */ | ||
3700 | WRITE_ONCE(rsp->n_barrier_done, rsp->n_barrier_done + 1); | ||
3701 | WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 0); | ||
3702 | _rcu_barrier_trace(rsp, "Inc2", -1, rsp->n_barrier_done); | ||
3703 | smp_mb(); /* Keep increment before caller's subsequent code. */ | ||
3704 | |||
3705 | /* Wait for all rcu_barrier_callback() callbacks to be invoked. */ | 3667 | /* Wait for all rcu_barrier_callback() callbacks to be invoked. */ |
3706 | wait_for_completion(&rsp->barrier_completion); | 3668 | wait_for_completion(&rsp->barrier_completion); |
3707 | 3669 | ||
3670 | /* Mark the end of the barrier operation. */ | ||
3671 | _rcu_barrier_trace(rsp, "Inc2", -1, rsp->barrier_sequence); | ||
3672 | rcu_seq_end(&rsp->barrier_sequence); | ||
3673 | |||
3708 | /* Other rcu_barrier() invocations can now safely proceed. */ | 3674 | /* Other rcu_barrier() invocations can now safely proceed. */ |
3709 | mutex_unlock(&rsp->barrier_mutex); | 3675 | mutex_unlock(&rsp->barrier_mutex); |
3710 | } | 3676 | } |