diff options
author | Paul E. McKenney <paulmck@linux.ibm.com> | 2019-04-09 14:06:32 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.ibm.com> | 2019-05-28 12:06:09 -0400 |
commit | e8516c64fe97e27a28fd5bc65b616508ae0020cf (patch) | |
tree | a8735dcac32c1d817849495ab010617213de8657 | |
parent | 140e53f20b159722903f0c87358bcd809aa9767e (diff) |
rcutorture: Fix stutter_wait() return value and freelist checks
The stutter_wait() function is supposed to return true if it actually
waits and false otherwise, but it instead unconditionally returns false.
Which hides a bug in rcu_torture_writer() that fails to account for
the fact that one of the rcu_tortures[] array elements will normally be
referenced by rcu_torture_current, and thus not be on the freelist.
This commit therefore corrects the stutter_wait() return value and adds a
check for rcu_torture_current to rcu_torture_writer()'s check that things
get freed after everything goes quiescent. In addition, this commit
causes torture_stutter() to give a bit more than one second (instead of
only one jiffy) warning of the end of the stutter interval. Finally,
this commit disables long-delay readers and aggressive update-side
forward-progress checks while forward-progress testing is in flight.
Reported-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Paul E. McKenney <paulmck@linux.ibm.com>
-rw-r--r-- | kernel/rcu/rcutorture.c | 16 | ||||
-rw-r--r-- | kernel/torture.c | 17 |
2 files changed, 25 insertions, 8 deletions
diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 7906ba2d9dad..954ac2b98619 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c | |||
@@ -1010,10 +1010,13 @@ rcu_torture_writer(void *arg) | |||
1010 | !rcu_gp_is_normal(); | 1010 | !rcu_gp_is_normal(); |
1011 | } | 1011 | } |
1012 | rcu_torture_writer_state = RTWS_STUTTER; | 1012 | rcu_torture_writer_state = RTWS_STUTTER; |
1013 | if (stutter_wait("rcu_torture_writer")) | 1013 | if (stutter_wait("rcu_torture_writer") && |
1014 | !READ_ONCE(rcu_fwd_cb_nodelay)) | ||
1014 | for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) | 1015 | for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) |
1015 | if (list_empty(&rcu_tortures[i].rtort_free)) | 1016 | if (list_empty(&rcu_tortures[i].rtort_free) && |
1016 | WARN_ON_ONCE(1); | 1017 | rcu_access_pointer(rcu_torture_current) != |
1018 | &rcu_tortures[i]) | ||
1019 | WARN(1, "%s: rtort_pipe_count: %d\n", __func__, rcu_tortures[i].rtort_pipe_count); | ||
1017 | } while (!torture_must_stop()); | 1020 | } while (!torture_must_stop()); |
1018 | /* Reset expediting back to unexpedited. */ | 1021 | /* Reset expediting back to unexpedited. */ |
1019 | if (expediting > 0) | 1022 | if (expediting > 0) |
@@ -1709,6 +1712,8 @@ static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries) | |||
1709 | } | 1712 | } |
1710 | 1713 | ||
1711 | /* Tight loop containing cond_resched(). */ | 1714 | /* Tight loop containing cond_resched(). */ |
1715 | WRITE_ONCE(rcu_fwd_cb_nodelay, true); | ||
1716 | cur_ops->sync(); /* Later readers see above write. */ | ||
1712 | if (selfpropcb) { | 1717 | if (selfpropcb) { |
1713 | WRITE_ONCE(fcs.stop, 0); | 1718 | WRITE_ONCE(fcs.stop, 0); |
1714 | cur_ops->call(&fcs.rh, rcu_torture_fwd_prog_cb); | 1719 | cur_ops->call(&fcs.rh, rcu_torture_fwd_prog_cb); |
@@ -1747,6 +1752,8 @@ static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries) | |||
1747 | WARN_ON(READ_ONCE(fcs.stop) != 2); | 1752 | WARN_ON(READ_ONCE(fcs.stop) != 2); |
1748 | destroy_rcu_head_on_stack(&fcs.rh); | 1753 | destroy_rcu_head_on_stack(&fcs.rh); |
1749 | } | 1754 | } |
1755 | schedule_timeout_uninterruptible(HZ / 10); /* Let kthreads recover. */ | ||
1756 | WRITE_ONCE(rcu_fwd_cb_nodelay, false); | ||
1750 | } | 1757 | } |
1751 | 1758 | ||
1752 | /* Carry out call_rcu() forward-progress testing. */ | 1759 | /* Carry out call_rcu() forward-progress testing. */ |
@@ -1816,7 +1823,6 @@ static void rcu_torture_fwd_prog_cr(void) | |||
1816 | cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */ | 1823 | cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */ |
1817 | (void)rcu_torture_fwd_prog_cbfree(); | 1824 | (void)rcu_torture_fwd_prog_cbfree(); |
1818 | 1825 | ||
1819 | WRITE_ONCE(rcu_fwd_cb_nodelay, false); | ||
1820 | if (!torture_must_stop() && !READ_ONCE(rcu_fwd_emergency_stop)) { | 1826 | if (!torture_must_stop() && !READ_ONCE(rcu_fwd_emergency_stop)) { |
1821 | WARN_ON(n_max_gps < MIN_FWD_CBS_LAUNDERED); | 1827 | WARN_ON(n_max_gps < MIN_FWD_CBS_LAUNDERED); |
1822 | pr_alert("%s Duration %lu barrier: %lu pending %ld n_launders: %ld n_launders_sa: %ld n_max_gps: %ld n_max_cbs: %ld cver %ld gps %ld\n", | 1828 | pr_alert("%s Duration %lu barrier: %lu pending %ld n_launders: %ld n_launders_sa: %ld n_max_gps: %ld n_max_cbs: %ld cver %ld gps %ld\n", |
@@ -1827,6 +1833,8 @@ static void rcu_torture_fwd_prog_cr(void) | |||
1827 | n_max_gps, n_max_cbs, cver, gps); | 1833 | n_max_gps, n_max_cbs, cver, gps); |
1828 | rcu_torture_fwd_cb_hist(); | 1834 | rcu_torture_fwd_cb_hist(); |
1829 | } | 1835 | } |
1836 | schedule_timeout_uninterruptible(HZ); /* Let CBs drain. */ | ||
1837 | WRITE_ONCE(rcu_fwd_cb_nodelay, false); | ||
1830 | } | 1838 | } |
1831 | 1839 | ||
1832 | 1840 | ||
diff --git a/kernel/torture.c b/kernel/torture.c index 17b2be9bde12..de0e0ecf88e1 100644 --- a/kernel/torture.c +++ b/kernel/torture.c | |||
@@ -578,10 +578,12 @@ static int stutter; | |||
578 | bool stutter_wait(const char *title) | 578 | bool stutter_wait(const char *title) |
579 | { | 579 | { |
580 | int spt; | 580 | int spt; |
581 | bool ret = false; | ||
581 | 582 | ||
582 | cond_resched_tasks_rcu_qs(); | 583 | cond_resched_tasks_rcu_qs(); |
583 | spt = READ_ONCE(stutter_pause_test); | 584 | spt = READ_ONCE(stutter_pause_test); |
584 | for (; spt; spt = READ_ONCE(stutter_pause_test)) { | 585 | for (; spt; spt = READ_ONCE(stutter_pause_test)) { |
586 | ret = true; | ||
585 | if (spt == 1) { | 587 | if (spt == 1) { |
586 | schedule_timeout_interruptible(1); | 588 | schedule_timeout_interruptible(1); |
587 | } else if (spt == 2) { | 589 | } else if (spt == 2) { |
@@ -592,7 +594,7 @@ bool stutter_wait(const char *title) | |||
592 | } | 594 | } |
593 | torture_shutdown_absorb(title); | 595 | torture_shutdown_absorb(title); |
594 | } | 596 | } |
595 | return !!spt; | 597 | return ret; |
596 | } | 598 | } |
597 | EXPORT_SYMBOL_GPL(stutter_wait); | 599 | EXPORT_SYMBOL_GPL(stutter_wait); |
598 | 600 | ||
@@ -602,13 +604,20 @@ EXPORT_SYMBOL_GPL(stutter_wait); | |||
602 | */ | 604 | */ |
603 | static int torture_stutter(void *arg) | 605 | static int torture_stutter(void *arg) |
604 | { | 606 | { |
607 | int wtime; | ||
608 | |||
605 | VERBOSE_TOROUT_STRING("torture_stutter task started"); | 609 | VERBOSE_TOROUT_STRING("torture_stutter task started"); |
606 | do { | 610 | do { |
607 | if (!torture_must_stop() && stutter > 1) { | 611 | if (!torture_must_stop() && stutter > 1) { |
608 | WRITE_ONCE(stutter_pause_test, 1); | 612 | wtime = stutter; |
609 | schedule_timeout_interruptible(stutter - 1); | 613 | if (stutter > HZ + 1) { |
614 | WRITE_ONCE(stutter_pause_test, 1); | ||
615 | wtime = stutter - HZ - 1; | ||
616 | schedule_timeout_interruptible(wtime); | ||
617 | wtime = HZ + 1; | ||
618 | } | ||
610 | WRITE_ONCE(stutter_pause_test, 2); | 619 | WRITE_ONCE(stutter_pause_test, 2); |
611 | schedule_timeout_interruptible(1); | 620 | schedule_timeout_interruptible(wtime); |
612 | } | 621 | } |
613 | WRITE_ONCE(stutter_pause_test, 0); | 622 | WRITE_ONCE(stutter_pause_test, 0); |
614 | if (!torture_must_stop()) | 623 | if (!torture_must_stop()) |