]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/rcu/rcutorture.c
Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
[linux.git] / kernel / rcu / rcutorture.c
index aa0be7ec2a26f4bf0114208e0a1f7fac24f44eea..c596c6f1e45717af99da5139d3c5078782deafbe 100644 (file)
@@ -55,6 +55,7 @@
 #include <linux/torture.h>
 #include <linux/vmalloc.h>
 #include <linux/sched/debug.h>
+#include <linux/sched/sysctl.h>
 
 #include "rcu.h"
 
@@ -168,7 +169,7 @@ static long n_rcu_torture_boost_failure;
 static long n_rcu_torture_boosts;
 static atomic_long_t n_rcu_torture_timers;
 static long n_barrier_attempts;
-static long n_barrier_successes;
+static long n_barrier_successes; /* did rcu_barrier test succeed? */
 static atomic_long_t n_cbfloods;
 static struct list_head rcu_torture_removed;
 
@@ -772,6 +773,44 @@ static void rcu_torture_boost_cb(struct rcu_head *head)
        smp_store_release(&rbip->inflight, 0);
 }
 
+static int old_rt_runtime = -1;
+
+static void rcu_torture_disable_rt_throttle(void)
+{
+       /*
+        * Disable RT throttling so that rcutorture's boost threads don't get
+        * throttled. Only possible if rcutorture is built-in otherwise the
+        * user should manually do this by setting the sched_rt_period_us and
+        * sched_rt_runtime sysctls.
+        */
+       if (!IS_BUILTIN(CONFIG_RCU_TORTURE_TEST) || old_rt_runtime != -1)
+               return;
+
+       old_rt_runtime = sysctl_sched_rt_runtime;
+       sysctl_sched_rt_runtime = -1;
+}
+
+static void rcu_torture_enable_rt_throttle(void)
+{
+       if (!IS_BUILTIN(CONFIG_RCU_TORTURE_TEST) || old_rt_runtime == -1)
+               return;
+
+       sysctl_sched_rt_runtime = old_rt_runtime;
+       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;
@@ -792,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)) {
@@ -810,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");
@@ -822,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
@@ -834,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;
                        }
@@ -1479,7 +1539,7 @@ rcu_torture_stats(void *arg)
        return 0;
 }
 
-static inline void
+static void
 rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
 {
        pr_alert("%s" TORTURE_FLAG
@@ -1511,6 +1571,7 @@ static int rcutorture_booster_cleanup(unsigned int cpu)
        mutex_lock(&boost_mutex);
        t = boost_tasks[cpu];
        boost_tasks[cpu] = NULL;
+       rcu_torture_enable_rt_throttle();
        mutex_unlock(&boost_mutex);
 
        /* This must be outside of the mutex, otherwise deadlock! */
@@ -1527,6 +1588,7 @@ static int rcutorture_booster_init(unsigned int cpu)
 
        /* Don't allow time recalculation while creating a new task. */
        mutex_lock(&boost_mutex);
+       rcu_torture_disable_rt_throttle();
        VERBOSE_TOROUT_STRING("Creating rcu_torture_boost task");
        boost_tasks[cpu] = kthread_create_on_node(rcu_torture_boost, NULL,
                                                  cpu_to_node(cpu),
@@ -1560,7 +1622,7 @@ static int rcu_torture_stall(void *args)
                VERBOSE_TOROUT_STRING("rcu_torture_stall end holdoff");
        }
        if (!kthread_should_stop()) {
-               stop_at = get_seconds() + stall_cpu;
+               stop_at = ktime_get_seconds() + stall_cpu;
                /* RCU CPU stall is expected behavior in following code. */
                rcu_read_lock();
                if (stall_cpu_irqsoff)
@@ -1569,7 +1631,8 @@ static int rcu_torture_stall(void *args)
                        preempt_disable();
                pr_alert("rcu_torture_stall start on CPU %d.\n",
                         smp_processor_id());
-               while (ULONG_CMP_LT(get_seconds(), stop_at))
+               while (ULONG_CMP_LT((unsigned long)ktime_get_seconds(),
+                                   stop_at))
                        continue;  /* Induce RCU CPU stall warning. */
                if (stall_cpu_irqsoff)
                        local_irq_enable();
@@ -1660,8 +1723,9 @@ static int rcu_torture_barrier(void *arg)
                               atomic_read(&barrier_cbs_invoked),
                               n_barrier_cbs);
                        WARN_ON_ONCE(1);
+               } else {
+                       n_barrier_successes++;
                }
-               n_barrier_successes++;
                schedule_timeout_interruptible(HZ / 10);
        } while (!torture_must_stop());
        torture_kthread_stopping("rcu_torture_barrier");
@@ -1724,6 +1788,30 @@ static void rcu_torture_barrier_cleanup(void)
        }
 }
 
+static bool rcu_torture_can_boost(void)
+{
+       static int boost_warn_once;
+       int prio;
+
+       if (!(test_boost == 1 && cur_ops->can_boost) && test_boost != 2)
+               return false;
+
+       prio = rcu_get_gp_kthreads_prio();
+       if (!prio)
+               return false;
+
+       if (prio < 2) {
+               if (boost_warn_once  == 1)
+                       return false;
+
+               pr_alert("%s: WARN: RCU kthread priority too low to test boosting.  Skipping RCU boost test. Try passing rcutree.kthread_prio > 1 on the kernel command line.\n", KBUILD_MODNAME);
+               boost_warn_once = 1;
+               return false;
+       }
+
+       return true;
+}
+
 static enum cpuhp_state rcutor_hp;
 
 static void
@@ -1768,8 +1856,7 @@ rcu_torture_cleanup(void)
        torture_stop_kthread(rcu_torture_fqs, fqs_task);
        for (i = 0; i < ncbflooders; i++)
                torture_stop_kthread(rcu_torture_cbflood, cbflood_task[i]);
-       if ((test_boost == 1 && cur_ops->can_boost) ||
-           test_boost == 2)
+       if (rcu_torture_can_boost())
                cpuhp_remove_state(rcutor_hp);
 
        /*
@@ -1874,8 +1961,8 @@ rcu_torture_init(void)
                         torture_type);
                pr_alert("rcu-torture types:");
                for (i = 0; i < ARRAY_SIZE(torture_ops); i++)
-                       pr_alert(" %s", torture_ops[i]->name);
-               pr_alert("\n");
+                       pr_cont(" %s", torture_ops[i]->name);
+               pr_cont("\n");
                firsterr = -EINVAL;
                goto unwind;
        }
@@ -1993,8 +2080,7 @@ rcu_torture_init(void)
                test_boost_interval = 1;
        if (test_boost_duration < 2)
                test_boost_duration = 2;
-       if ((test_boost == 1 && cur_ops->can_boost) ||
-           test_boost == 2) {
+       if (rcu_torture_can_boost()) {
 
                boost_starttime = jiffies + test_boost_interval * HZ;