rcutorture: Fix stuttering races and other issues
authorJoel Fernandes (Google) <joel@joelfernandes.org>
Sat, 29 Jul 2023 14:27:31 +0000 (14:27 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 28 Nov 2023 17:20:08 +0000 (17:20 +0000)
[ Upstream commit cca42bd8eb1b54a4c9bbf48c79d120e66619a3e4 ]

The stuttering code isn't functioning as expected. Ideally, it should
pause the torture threads for a designated period before resuming. Yet,
it fails to halt the test for the correct duration. Additionally, a race
condition exists, potentially causing the stuttering code to pause for
an extended period if the 'spt' variable is non-zero due to the stutter
orchestration thread's inadequate CPU time.

Moreover, over-stuttering can hinder RCU's progress on TREE07 kernels.
This happens as the stuttering code may run within a softirq due to RCU
callbacks. Consequently, ksoftirqd keeps a CPU busy for several seconds,
thus obstructing RCU's progress. This situation triggers a warning
message in the logs:

[ 2169.481783] rcu_torture_writer: rtort_pipe_count: 9

This warning suggests that an RCU torture object, although invisible to
RCU readers, couldn't make it past the pipe array and be freed -- a
strong indication that there weren't enough grace periods during the
stutter interval.

To address these issues, this patch sets the "stutter end" time to an
absolute point in the future set by the main stutter thread. This is
then used for waiting in stutter_wait(). While the stutter thread still
defines this absolute time, the waiters' waiting logic doesn't rely on
the stutter thread receiving sufficient CPU time to halt the stuttering
as the halting is now self-controlled.

Cc: stable@vger.kernel.org
Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
kernel/torture.c

index e851b8e..c7b4758 100644 (file)
@@ -721,7 +721,7 @@ static void torture_shutdown_cleanup(void)
  * suddenly applied to or removed from the system.
  */
 static struct task_struct *stutter_task;
  * suddenly applied to or removed from the system.
  */
 static struct task_struct *stutter_task;
-static int stutter_pause_test;
+static ktime_t stutter_till_abs_time;
 static int stutter;
 static int stutter_gap;
 
 static int stutter;
 static int stutter_gap;
 
@@ -731,30 +731,16 @@ static int stutter_gap;
  */
 bool stutter_wait(const char *title)
 {
  */
 bool stutter_wait(const char *title)
 {
-       unsigned int i = 0;
        bool ret = false;
        bool ret = false;
-       int spt;
+       ktime_t till_ns;
 
        cond_resched_tasks_rcu_qs();
 
        cond_resched_tasks_rcu_qs();
-       spt = READ_ONCE(stutter_pause_test);
-       for (; spt; spt = READ_ONCE(stutter_pause_test)) {
-               if (!ret && !rt_task(current)) {
-                       sched_set_normal(current, MAX_NICE);
-                       ret = true;
-               }
-               if (spt == 1) {
-                       torture_hrtimeout_jiffies(1, NULL);
-               } else if (spt == 2) {
-                       while (READ_ONCE(stutter_pause_test)) {
-                               if (!(i++ & 0xffff))
-                                       torture_hrtimeout_us(10, 0, NULL);
-                               cond_resched();
-                       }
-               } else {
-                       torture_hrtimeout_jiffies(round_jiffies_relative(HZ), NULL);
-               }
-               torture_shutdown_absorb(title);
+       till_ns = READ_ONCE(stutter_till_abs_time);
+       if (till_ns && ktime_before(ktime_get(), till_ns)) {
+               torture_hrtimeout_ns(till_ns, 0, HRTIMER_MODE_ABS, NULL);
+               ret = true;
        }
        }
+       torture_shutdown_absorb(title);
        return ret;
 }
 EXPORT_SYMBOL_GPL(stutter_wait);
        return ret;
 }
 EXPORT_SYMBOL_GPL(stutter_wait);
@@ -765,23 +751,16 @@ EXPORT_SYMBOL_GPL(stutter_wait);
  */
 static int torture_stutter(void *arg)
 {
  */
 static int torture_stutter(void *arg)
 {
-       DEFINE_TORTURE_RANDOM(rand);
-       int wtime;
+       ktime_t till_ns;
 
        VERBOSE_TOROUT_STRING("torture_stutter task started");
        do {
                if (!torture_must_stop() && stutter > 1) {
 
        VERBOSE_TOROUT_STRING("torture_stutter task started");
        do {
                if (!torture_must_stop() && stutter > 1) {
-                       wtime = stutter;
-                       if (stutter > 2) {
-                               WRITE_ONCE(stutter_pause_test, 1);
-                               wtime = stutter - 3;
-                               torture_hrtimeout_jiffies(wtime, &rand);
-                               wtime = 2;
-                       }
-                       WRITE_ONCE(stutter_pause_test, 2);
-                       torture_hrtimeout_jiffies(wtime, NULL);
+                       till_ns = ktime_add_ns(ktime_get(),
+                                              jiffies_to_nsecs(stutter));
+                       WRITE_ONCE(stutter_till_abs_time, till_ns);
+                       torture_hrtimeout_jiffies(stutter - 1, NULL);
                }
                }
-               WRITE_ONCE(stutter_pause_test, 0);
                if (!torture_must_stop())
                        torture_hrtimeout_jiffies(stutter_gap, NULL);
                torture_shutdown_absorb("torture_stutter");
                if (!torture_must_stop())
                        torture_hrtimeout_jiffies(stutter_gap, NULL);
                torture_shutdown_absorb("torture_stutter");