diff options
author | Paul E. McKenney <paul.mckenney@linaro.org> | 2012-05-28 22:21:41 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2012-07-02 15:34:03 -0400 |
commit | c6ebcbb60c8c68a88160fe54302e851700d1362c (patch) | |
tree | d95b0c10d7107d3d34a108aa75222820157fd599 /kernel/rcutorture.c | |
parent | e3f8d3788ed7cf55946030dc9b76e73edb111602 (diff) |
rcu: Fix bug in rcu_barrier() torture test
The child threads in the rcu_torture_barrier_cbs() are improperly
synchronized, which can cause the rcu_barrier() tests to hang. The
failure mode is as follows:
1. CPU 0 running in rcu_torture_barrier() sets barrier_cbs_count
to n_barrier_cbs.
2. CPU 1 running in rcu_torture_barrier_cbs() wakes up, posts
its RCU callback, and atomically decrements barrier_cbs_count.
Because barrier_cbs_count is not zero, it does not do the wake_up().
3. CPU 2 running in rcu_torture_barrier_cbs() wakes up, but
finds that barrier_cbs_count is not equal to n_barrier_cbs,
and so returns to sleep.
4. The value of barrier_cbs_count therefore never reaches zero,
which causes the test to hang.
This commit therefore uses a phase variable to coordinate the test,
preventing this scenario from occurring.
Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/rcutorture.c')
-rw-r--r-- | kernel/rcutorture.c | 9 |
1 files changed, 7 insertions, 2 deletions
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 7b6935e0cee3..f7fe73e59c9f 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
@@ -206,6 +206,7 @@ static unsigned long boost_starttime; /* jiffies of next boost test start. */ | |||
206 | DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */ | 206 | DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */ |
207 | /* and boost task create/destroy. */ | 207 | /* and boost task create/destroy. */ |
208 | static atomic_t barrier_cbs_count; /* Barrier callbacks registered. */ | 208 | static atomic_t barrier_cbs_count; /* Barrier callbacks registered. */ |
209 | static bool barrier_phase; /* Test phase. */ | ||
209 | static atomic_t barrier_cbs_invoked; /* Barrier callbacks invoked. */ | 210 | static atomic_t barrier_cbs_invoked; /* Barrier callbacks invoked. */ |
210 | static wait_queue_head_t *barrier_cbs_wq; /* Coordinate barrier testing. */ | 211 | static wait_queue_head_t *barrier_cbs_wq; /* Coordinate barrier testing. */ |
211 | static DECLARE_WAIT_QUEUE_HEAD(barrier_wq); | 212 | static DECLARE_WAIT_QUEUE_HEAD(barrier_wq); |
@@ -1642,6 +1643,7 @@ void rcu_torture_barrier_cbf(struct rcu_head *rcu) | |||
1642 | static int rcu_torture_barrier_cbs(void *arg) | 1643 | static int rcu_torture_barrier_cbs(void *arg) |
1643 | { | 1644 | { |
1644 | long myid = (long)arg; | 1645 | long myid = (long)arg; |
1646 | bool lastphase = 0; | ||
1645 | struct rcu_head rcu; | 1647 | struct rcu_head rcu; |
1646 | 1648 | ||
1647 | init_rcu_head_on_stack(&rcu); | 1649 | init_rcu_head_on_stack(&rcu); |
@@ -1649,9 +1651,11 @@ static int rcu_torture_barrier_cbs(void *arg) | |||
1649 | set_user_nice(current, 19); | 1651 | set_user_nice(current, 19); |
1650 | do { | 1652 | do { |
1651 | wait_event(barrier_cbs_wq[myid], | 1653 | wait_event(barrier_cbs_wq[myid], |
1652 | atomic_read(&barrier_cbs_count) == n_barrier_cbs || | 1654 | barrier_phase != lastphase || |
1653 | kthread_should_stop() || | 1655 | kthread_should_stop() || |
1654 | fullstop != FULLSTOP_DONTSTOP); | 1656 | fullstop != FULLSTOP_DONTSTOP); |
1657 | lastphase = barrier_phase; | ||
1658 | smp_mb(); /* ensure barrier_phase load before ->call(). */ | ||
1655 | if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) | 1659 | if (kthread_should_stop() || fullstop != FULLSTOP_DONTSTOP) |
1656 | break; | 1660 | break; |
1657 | cur_ops->call(&rcu, rcu_torture_barrier_cbf); | 1661 | cur_ops->call(&rcu, rcu_torture_barrier_cbf); |
@@ -1676,7 +1680,8 @@ static int rcu_torture_barrier(void *arg) | |||
1676 | do { | 1680 | do { |
1677 | atomic_set(&barrier_cbs_invoked, 0); | 1681 | atomic_set(&barrier_cbs_invoked, 0); |
1678 | atomic_set(&barrier_cbs_count, n_barrier_cbs); | 1682 | atomic_set(&barrier_cbs_count, n_barrier_cbs); |
1679 | /* wake_up() path contains the required barriers. */ | 1683 | smp_mb(); /* Ensure barrier_phase after prior assignments. */ |
1684 | barrier_phase = !barrier_phase; | ||
1680 | for (i = 0; i < n_barrier_cbs; i++) | 1685 | for (i = 0; i < n_barrier_cbs; i++) |
1681 | wake_up(&barrier_cbs_wq[i]); | 1686 | wake_up(&barrier_cbs_wq[i]); |
1682 | wait_event(barrier_wq, | 1687 | wait_event(barrier_wq, |