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.
Unified · Split
| @@ -1010,10 +1010,13 @@ rcu_torture_writer(void *arg) · 1009 unmodified lines | ||
| 1010 | 1010 | !rcu_gp_is_normal(); |
| 1011 | 1011 | } |
| 1012 | 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 | 1015 | for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) |
| 1015 | − if (list_empty(&rcu_tortures[i].rtort_free)) | |
| 1016 | − WARN_ON_ONCE(1); | |
| 1016 | + if (list_empty(&rcu_tortures[i].rtort_free) && | |
| 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 | 1020 | } while (!torture_must_stop()); |
| 1018 | 1021 | /* Reset expediting back to unexpedited. */ |
| 1019 | 1022 | if (expediting > 0) |
| @@ -1709,6 +1712,8 @@ static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries) · 689 unmodified lines | ||
| 1709 | 1712 | } |
| 1710 | 1713 | |
| 1711 | 1714 | /* Tight loop containing cond_resched(). */ |
| 1715 | + WRITE_ONCE(rcu_fwd_cb_nodelay, true); | |
| 1716 | + cur_ops->sync(); /* Later readers see above write. */ | |
| 1712 | 1717 | if (selfpropcb) { |
| 1713 | 1718 | WRITE_ONCE(fcs.stop, 0); |
| 1714 | 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) · 32 unmodified lines | ||
| 1747 | 1752 | WARN_ON(READ_ONCE(fcs.stop) != 2); |
| 1748 | 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 | 1759 | /* Carry out call_rcu() forward-progress testing. */ |
| @@ -1816,7 +1823,6 @@ static void rcu_torture_fwd_prog_cr(void) · 63 unmodified lines | ||
| 1816 | 1823 | cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */ |
| 1817 | 1824 | (void)rcu_torture_fwd_prog_cbfree(); |
| 1818 | 1825 | |
| 1819 | − WRITE_ONCE(rcu_fwd_cb_nodelay, false); | |
| 1820 | 1826 | if (!torture_must_stop() && !READ_ONCE(rcu_fwd_emergency_stop)) { |
| 1821 | 1827 | WARN_ON(n_max_gps < MIN_FWD_CBS_LAUNDERED); |
| 1822 | 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) · 4 unmodified lines | ||
| 1827 | 1833 | n_max_gps, n_max_cbs, cver, gps); |
| 1828 | 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 | |
| @@ -578,10 +578,12 @@ static int stutter; · 577 unmodified lines | ||
| 578 | 578 | bool stutter_wait(const char *title) |
| 579 | 579 | { |
| 580 | 580 | int spt; |
| 581 | + bool ret = false; | |
| 581 | 582 | |
| 582 | 583 | cond_resched_tasks_rcu_qs(); |
| 583 | 584 | spt = READ_ONCE(stutter_pause_test); |
| 584 | 585 | for (; spt; spt = READ_ONCE(stutter_pause_test)) { |
| 586 | + ret = true; | |
| 585 | 587 | if (spt == 1) { |
| 586 | 588 | schedule_timeout_interruptible(1); |
| 587 | 589 | } else if (spt == 2) { |
| @@ -592,7 +594,7 @@ bool stutter_wait(const char *title) · 4 unmodified lines | ||
| 592 | 594 | } |
| 593 | 595 | torture_shutdown_absorb(title); |
| 594 | 596 | } |
| 595 | − return !!spt; | |
| 597 | + return ret; | |
| 596 | 598 | } |
| 597 | 599 | EXPORT_SYMBOL_GPL(stutter_wait); |
| 598 | 600 | |
| @@ -602,13 +604,20 @@ EXPORT_SYMBOL_GPL(stutter_wait); · 3 unmodified lines | ||
| 602 | 604 | */ |
| 603 | 605 | static int torture_stutter(void *arg) |
| 604 | 606 | { |
| 607 | + int wtime; | |
| 608 | + | |
| 605 | 609 | VERBOSE_TOROUT_STRING("torture_stutter task started"); |
| 606 | 610 | do { |
| 607 | 611 | if (!torture_must_stop() && stutter > 1) { |
| 608 | − WRITE_ONCE(stutter_pause_test, 1); | |
| 609 | − schedule_timeout_interruptible(stutter - 1); | |
| 612 | + wtime = stutter; | |
| 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 | 619 | WRITE_ONCE(stutter_pause_test, 2); |
| 611 | − schedule_timeout_interruptible(1); | |
| 620 | + schedule_timeout_interruptible(wtime); | |
| 612 | 621 | } |
| 613 | 622 | WRITE_ONCE(stutter_pause_test, 0); |
| 614 | 623 | if (!torture_must_stop()) |