aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2013-09-24 18:04:06 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2013-12-03 13:10:18 -0500
commit78e4bc34e5d966cfd95f1238565afc399d56225c (patch)
treee430291c48ec41b22e31865bf5aa13c4db87a3cb /kernel
parent6193c76aba8ec3cc5f083c35efbab9ed924125f6 (diff)
rcu: Fix and comment ordering around wait_event()
It is all too easy to forget that wait_event() does not necessarily imply a full memory barrier. The case where it does not is where the condition transitions to true just as wait_event() starts execution. This is actually a feature: The standard use of wait_event() involves locking, in which case the locks provide the needed ordering (you hold a lock across the wake_up() and acquire that same lock after wait_event() returns). Given that I did forget that wait_event() does not necessarily imply a full memory barrier in one case, this commit fixes that case. This commit also adds comments calling out the placement of existing memory barriers relied on by wait_event() calls. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/rcu/torture.c8
-rw-r--r--kernel/rcu/tree.c3
-rw-r--r--kernel/rcu/tree_plugin.h6
3 files changed, 13 insertions, 4 deletions
diff --git a/kernel/rcu/torture.c b/kernel/rcu/torture.c
index 3929cd451511..69a4ec80a788 100644
--- a/kernel/rcu/torture.c
+++ b/kernel/rcu/torture.c
@@ -1578,6 +1578,7 @@ static int rcu_torture_barrier_cbs(void *arg)
1578{ 1578{
1579 long myid = (long)arg; 1579 long myid = (long)arg;
1580 bool lastphase = 0; 1580 bool lastphase = 0;
1581 bool newphase;
1581 struct rcu_head rcu; 1582 struct rcu_head rcu;
1582 1583
1583 init_rcu_head_on_stack(&rcu); 1584 init_rcu_head_on_stack(&rcu);
@@ -1585,10 +1586,11 @@ static int rcu_torture_barrier_cbs(void *arg)
1585 set_user_nice(current, 19); 1586 set_user_nice(current, 19);
1586 do { 1587 do {
1587 wait_event(barrier_cbs_wq[myid], 1588 wait_event(barrier_cbs_wq[myid],
1588 barrier_phase != lastphase || 1589 (newphase =
1590 ACCESS_ONCE(barrier_phase)) != lastphase ||
1589 kthread_should_stop() || 1591 kthread_should_stop() ||
1590 fullstop != FULLSTOP_DONTSTOP); 1592 fullstop != FULLSTOP_DONTSTOP);
1591 lastphase = barrier_phase; 1593 lastphase = newphase;
1592 smp_mb(); /* ensure barrier_phase load before ->call(). */ 1594 smp_mb(); /* ensure barrier_phase load before ->call(). */
1593 if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) 1595 if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP)
1594 break; 1596 break;
@@ -1625,7 +1627,7 @@ static int rcu_torture_barrier(void *arg)
1625 if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) 1627 if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP)
1626 break; 1628 break;
1627 n_barrier_attempts++; 1629 n_barrier_attempts++;
1628 cur_ops->cb_barrier(); 1630 cur_ops->cb_barrier(); /* Implies smp_mb() for wait_event(). */
1629 if (atomic_read(&barrier_cbs_invoked) != n_barrier_cbs) { 1631 if (atomic_read(&barrier_cbs_invoked) != n_barrier_cbs) {
1630 n_rcu_torture_barrier_error++; 1632 n_rcu_torture_barrier_error++;
1631 WARN_ON_ONCE(1); 1633 WARN_ON_ONCE(1);
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 5243ebea0fc1..abef9c358d47 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -1533,6 +1533,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
1533 rdp = this_cpu_ptr(rsp->rda); 1533 rdp = this_cpu_ptr(rsp->rda);
1534 if (rnp == rdp->mynode) 1534 if (rnp == rdp->mynode)
1535 __note_gp_changes(rsp, rnp, rdp); 1535 __note_gp_changes(rsp, rnp, rdp);
1536 /* smp_mb() provided by prior unlock-lock pair. */
1536 nocb += rcu_future_gp_cleanup(rsp, rnp); 1537 nocb += rcu_future_gp_cleanup(rsp, rnp);
1537 raw_spin_unlock_irq(&rnp->lock); 1538 raw_spin_unlock_irq(&rnp->lock);
1538 cond_resched(); 1539 cond_resched();
@@ -1577,6 +1578,7 @@ static int __noreturn rcu_gp_kthread(void *arg)
1577 wait_event_interruptible(rsp->gp_wq, 1578 wait_event_interruptible(rsp->gp_wq,
1578 ACCESS_ONCE(rsp->gp_flags) & 1579 ACCESS_ONCE(rsp->gp_flags) &
1579 RCU_GP_FLAG_INIT); 1580 RCU_GP_FLAG_INIT);
1581 /* Locking provides needed memory barrier. */
1580 if (rcu_gp_init(rsp)) 1582 if (rcu_gp_init(rsp))
1581 break; 1583 break;
1582 cond_resched(); 1584 cond_resched();
@@ -1606,6 +1608,7 @@ static int __noreturn rcu_gp_kthread(void *arg)
1606 (!ACCESS_ONCE(rnp->qsmask) && 1608 (!ACCESS_ONCE(rnp->qsmask) &&
1607 !rcu_preempt_blocked_readers_cgp(rnp)), 1609 !rcu_preempt_blocked_readers_cgp(rnp)),
1608 j); 1610 j);
1611 /* Locking provides needed memory barriers. */
1609 /* If grace period done, leave loop. */ 1612 /* If grace period done, leave loop. */
1610 if (!ACCESS_ONCE(rnp->qsmask) && 1613 if (!ACCESS_ONCE(rnp->qsmask) &&
1611 !rcu_preempt_blocked_readers_cgp(rnp)) 1614 !rcu_preempt_blocked_readers_cgp(rnp))
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index 6abb03dff5c0..b023e5407111 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -779,8 +779,10 @@ static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
779 } 779 }
780 if (rnp->parent == NULL) { 780 if (rnp->parent == NULL) {
781 raw_spin_unlock_irqrestore(&rnp->lock, flags); 781 raw_spin_unlock_irqrestore(&rnp->lock, flags);
782 if (wake) 782 if (wake) {
783 smp_mb(); /* EGP done before wake_up(). */
783 wake_up(&sync_rcu_preempt_exp_wq); 784 wake_up(&sync_rcu_preempt_exp_wq);
785 }
784 break; 786 break;
785 } 787 }
786 mask = rnp->grpmask; 788 mask = rnp->grpmask;
@@ -1852,6 +1854,7 @@ static int rcu_oom_notify(struct notifier_block *self,
1852 1854
1853 /* Wait for callbacks from earlier instance to complete. */ 1855 /* Wait for callbacks from earlier instance to complete. */
1854 wait_event(oom_callback_wq, atomic_read(&oom_callback_count) == 0); 1856 wait_event(oom_callback_wq, atomic_read(&oom_callback_count) == 0);
1857 smp_mb(); /* Ensure callback reuse happens after callback invocation. */
1855 1858
1856 /* 1859 /*
1857 * Prevent premature wakeup: ensure that all increments happen 1860 * Prevent premature wakeup: ensure that all increments happen
@@ -2250,6 +2253,7 @@ static int rcu_nocb_kthread(void *arg)
2250 trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, 2253 trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
2251 TPS("Sleep")); 2254 TPS("Sleep"));
2252 wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head); 2255 wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head);
2256 /* Memory barrier provide by xchg() below. */
2253 } else if (firsttime) { 2257 } else if (firsttime) {
2254 firsttime = 0; 2258 firsttime = 0;
2255 trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, 2259 trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,