diff options
-rw-r--r-- | include/trace/events/rcu.h | 1 | ||||
-rw-r--r-- | kernel/rcu/tree.c | 72 | ||||
-rw-r--r-- | kernel/rcu/tree.h | 2 | ||||
-rw-r--r-- | kernel/rcu/tree_trace.c | 4 |
4 files changed, 22 insertions, 57 deletions
diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index c78e88ce5ea3..ef72c4aada56 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h | |||
@@ -661,7 +661,6 @@ TRACE_EVENT(rcu_torture_read, | |||
661 | * Tracepoint for _rcu_barrier() execution. The string "s" describes | 661 | * Tracepoint for _rcu_barrier() execution. The string "s" describes |
662 | * the _rcu_barrier phase: | 662 | * the _rcu_barrier phase: |
663 | * "Begin": _rcu_barrier() started. | 663 | * "Begin": _rcu_barrier() started. |
664 | * "Check": _rcu_barrier() checking for piggybacking. | ||
665 | * "EarlyExit": _rcu_barrier() piggybacked, thus early exit. | 664 | * "EarlyExit": _rcu_barrier() piggybacked, thus early exit. |
666 | * "Inc1": _rcu_barrier() piggyback check counter incremented. | 665 | * "Inc1": _rcu_barrier() piggyback check counter incremented. |
667 | * "OfflineNoCB": _rcu_barrier() found callback on never-online CPU | 666 | * "OfflineNoCB": _rcu_barrier() found callback on never-online CPU |
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 | } |
diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 4edc277d08eb..5c1042d9c310 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h | |||
@@ -486,7 +486,7 @@ struct rcu_state { | |||
486 | struct mutex barrier_mutex; /* Guards barrier fields. */ | 486 | struct mutex barrier_mutex; /* Guards barrier fields. */ |
487 | atomic_t barrier_cpu_count; /* # CPUs waiting on. */ | 487 | atomic_t barrier_cpu_count; /* # CPUs waiting on. */ |
488 | struct completion barrier_completion; /* Wake at barrier end. */ | 488 | struct completion barrier_completion; /* Wake at barrier end. */ |
489 | unsigned long n_barrier_done; /* ++ at start and end of */ | 489 | unsigned long barrier_sequence; /* ++ at start and end of */ |
490 | /* _rcu_barrier(). */ | 490 | /* _rcu_barrier(). */ |
491 | /* End of fields guarded by barrier_mutex. */ | 491 | /* End of fields guarded by barrier_mutex. */ |
492 | 492 | ||
diff --git a/kernel/rcu/tree_trace.c b/kernel/rcu/tree_trace.c index 36c04b46d3b8..d9982a2ce305 100644 --- a/kernel/rcu/tree_trace.c +++ b/kernel/rcu/tree_trace.c | |||
@@ -81,9 +81,9 @@ static void r_stop(struct seq_file *m, void *v) | |||
81 | static int show_rcubarrier(struct seq_file *m, void *v) | 81 | static int show_rcubarrier(struct seq_file *m, void *v) |
82 | { | 82 | { |
83 | struct rcu_state *rsp = (struct rcu_state *)m->private; | 83 | struct rcu_state *rsp = (struct rcu_state *)m->private; |
84 | seq_printf(m, "bcc: %d nbd: %lu\n", | 84 | seq_printf(m, "bcc: %d bseq: %lu\n", |
85 | atomic_read(&rsp->barrier_cpu_count), | 85 | atomic_read(&rsp->barrier_cpu_count), |
86 | rsp->n_barrier_done); | 86 | rsp->barrier_sequence); |
87 | return 0; | 87 | return 0; |
88 | } | 88 | } |
89 | 89 | ||