rcutorture: Make boost test more robust
authorJoel Fernandes (Google) <joel@joelfernandes.org>
Sun, 10 Jun 2018 23:45:44 +0000 (16:45 -0700)
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Thu, 12 Jul 2018 22:42:06 +0000 (15:42 -0700)
Currently, with RCU_BOOST disabled, I get no failures when forcing
rcutorture to test RCU boost priority inversion. The reason seems to be
that we don't check for failures if the callback never ran at all for
the duration of the boost-test loop.

Further, the 'rtb' and 'rtbf' counters seem to be used inconsistently.
'rtb' is incremented at the start of each test and 'rtbf' is incremented
per-cpu on each failure of call_rcu. So its possible 'rtbf' > 'rtb'.

To test the boost with rcutorture, I did following on a 4-CPU x86 machine:

modprobe rcutorture  test_boost=2
sleep 20
rmmod rcutorture

With patch:
rtbf: 8 rtb: 12

Without patch:
rtbf: 0 rtb: 2

In summary this patch:
 - Increments failed and total test counters once per boost-test.
 - Checks for failure cases correctly.

Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
kernel/rcu/rcutorture.c

index 74e47d0a618c6fa11ee1853a5f11ea2dc6ba1a6e..36b9b82662131509f0e6e6cf2da4760b548d4cdd 100644 (file)
@@ -799,6 +799,18 @@ static void rcu_torture_enable_rt_throttle(void)
        old_rt_runtime = -1;
 }
 
+static bool rcu_torture_boost_failed(unsigned long start, unsigned long end)
+{
+       if (end - start > test_boost_duration * HZ - HZ / 2) {
+               VERBOSE_TOROUT_STRING("rcu_torture_boost boosting failed");
+               n_rcu_torture_boost_failure++;
+
+               return true; /* failed */
+       }
+
+       return false; /* passed */
+}
+
 static int rcu_torture_boost(void *arg)
 {
        unsigned long call_rcu_time;
@@ -819,6 +831,21 @@ static int rcu_torture_boost(void *arg)
        init_rcu_head_on_stack(&rbi.rcu);
        /* Each pass through the following loop does one boost-test cycle. */
        do {
+               /* Track if the test failed already in this test interval? */
+               bool failed = false;
+
+               /* Increment n_rcu_torture_boosts once per boost-test */
+               while (!kthread_should_stop()) {
+                       if (mutex_trylock(&boost_mutex)) {
+                               n_rcu_torture_boosts++;
+                               mutex_unlock(&boost_mutex);
+                               break;
+                       }
+                       schedule_timeout_uninterruptible(1);
+               }
+               if (kthread_should_stop())
+                       goto checkwait;
+
                /* Wait for the next test interval. */
                oldstarttime = boost_starttime;
                while (ULONG_CMP_LT(jiffies, oldstarttime)) {
@@ -837,11 +864,10 @@ static int rcu_torture_boost(void *arg)
                                /* RCU core before ->inflight = 1. */
                                smp_store_release(&rbi.inflight, 1);
                                call_rcu(&rbi.rcu, rcu_torture_boost_cb);
-                               if (jiffies - call_rcu_time >
-                                        test_boost_duration * HZ - HZ / 2) {
-                                       VERBOSE_TOROUT_STRING("rcu_torture_boost boosting failed");
-                                       n_rcu_torture_boost_failure++;
-                               }
+                               /* Check if the boost test failed */
+                               failed = failed ||
+                                        rcu_torture_boost_failed(call_rcu_time,
+                                                                jiffies);
                                call_rcu_time = jiffies;
                        }
                        stutter_wait("rcu_torture_boost");
@@ -849,6 +875,14 @@ static int rcu_torture_boost(void *arg)
                                goto checkwait;
                }
 
+               /*
+                * If boost never happened, then inflight will always be 1, in
+                * this case the boost check would never happen in the above
+                * loop so do another one here.
+                */
+               if (!failed && smp_load_acquire(&rbi.inflight))
+                       rcu_torture_boost_failed(call_rcu_time, jiffies);
+
                /*
                 * Set the start time of the next test interval.
                 * Yes, this is vulnerable to long delays, but such
@@ -861,7 +895,6 @@ static int rcu_torture_boost(void *arg)
                        if (mutex_trylock(&boost_mutex)) {
                                boost_starttime = jiffies +
                                                  test_boost_interval * HZ;
-                               n_rcu_torture_boosts++;
                                mutex_unlock(&boost_mutex);
                                break;
                        }