]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - kernel/rcutorture.c
rcu: make rcutorture more vicious: reinstate boot-time testing
[mirror_ubuntu-zesty-kernel.git] / kernel / rcutorture.c
index 33acc424667e03381597d76771fa12c9ad777124..27003e2421c77d20181cdc1aa45fb603ca5cc3d9 100644 (file)
@@ -57,7 +57,8 @@ static int stat_interval;     /* Interval between stats, in seconds. */
                                /*  Defaults to "only at end of test". */
 static int verbose;            /* Print more debug info. */
 static int test_no_idle_hz;    /* Test RCU's support for tickless idle CPUs. */
-static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/
+static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/
+static int stutter = 5;                /* Start/stop testing interval (in sec) */
 static char *torture_type = "rcu"; /* What RCU implementation to torture. */
 
 module_param(nreaders, int, 0444);
@@ -72,6 +73,8 @@ module_param(test_no_idle_hz, bool, 0444);
 MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs");
 module_param(shuffle_interval, int, 0444);
 MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles");
+module_param(stutter, int, 0444);
+MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test");
 module_param(torture_type, charp, 0444);
 MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)");
 
@@ -91,6 +94,7 @@ static struct task_struct **fakewriter_tasks;
 static struct task_struct **reader_tasks;
 static struct task_struct *stats_task;
 static struct task_struct *shuffler_task;
+static struct task_struct *stutter_task;
 
 #define RCU_TORTURE_PIPE_LEN 10
 
@@ -119,6 +123,15 @@ static atomic_t n_rcu_torture_mberror;
 static atomic_t n_rcu_torture_error;
 static struct list_head rcu_torture_removed;
 
+static int stutter_pause_test = 0;
+
+#if defined(MODULE) || defined(CONFIG_RCU_TORTURE_TEST_RUNNABLE)
+#define RCUTORTURE_RUNNABLE_INIT 1
+#else
+#define RCUTORTURE_RUNNABLE_INIT 0
+#endif
+int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT;
+
 /*
  * Allocate an element from the rcu_tortures pool.
  */
@@ -179,6 +192,13 @@ rcu_random(struct rcu_random_state *rrsp)
        return swahw32(rrsp->rrs_state);
 }
 
+static void
+rcu_stutter_wait(void)
+{
+       while (stutter_pause_test || !rcutorture_runnable)
+               schedule_timeout_interruptible(1);
+}
+
 /*
  * Operations vector for selecting different types of tests.
  */
@@ -192,6 +212,7 @@ struct rcu_torture_ops {
        int (*completed)(void);
        void (*deferredfree)(struct rcu_torture *p);
        void (*sync)(void);
+       void (*cb_barrier)(void);
        int (*stats)(char *page);
        char *name;
 };
@@ -265,6 +286,7 @@ static struct rcu_torture_ops rcu_ops = {
        .completed = rcu_torture_completed,
        .deferredfree = rcu_torture_deferred_free,
        .sync = synchronize_rcu,
+       .cb_barrier = rcu_barrier,
        .stats = NULL,
        .name = "rcu"
 };
@@ -304,6 +326,7 @@ static struct rcu_torture_ops rcu_sync_ops = {
        .completed = rcu_torture_completed,
        .deferredfree = rcu_sync_torture_deferred_free,
        .sync = synchronize_rcu,
+       .cb_barrier = NULL,
        .stats = NULL,
        .name = "rcu_sync"
 };
@@ -364,6 +387,7 @@ static struct rcu_torture_ops rcu_bh_ops = {
        .completed = rcu_bh_torture_completed,
        .deferredfree = rcu_bh_torture_deferred_free,
        .sync = rcu_bh_torture_synchronize,
+       .cb_barrier = rcu_barrier_bh,
        .stats = NULL,
        .name = "rcu_bh"
 };
@@ -377,6 +401,7 @@ static struct rcu_torture_ops rcu_bh_sync_ops = {
        .completed = rcu_bh_torture_completed,
        .deferredfree = rcu_sync_torture_deferred_free,
        .sync = rcu_bh_torture_synchronize,
+       .cb_barrier = NULL,
        .stats = NULL,
        .name = "rcu_bh_sync"
 };
@@ -458,6 +483,7 @@ static struct rcu_torture_ops srcu_ops = {
        .completed = srcu_torture_completed,
        .deferredfree = rcu_sync_torture_deferred_free,
        .sync = srcu_torture_synchronize,
+       .cb_barrier = NULL,
        .stats = srcu_torture_stats,
        .name = "srcu"
 };
@@ -482,6 +508,11 @@ static int sched_torture_completed(void)
        return 0;
 }
 
+static void rcu_sched_torture_deferred_free(struct rcu_torture *p)
+{
+       call_rcu_sched(&p->rtort_rcu, rcu_torture_cb);
+}
+
 static void sched_torture_synchronize(void)
 {
        synchronize_sched();
@@ -494,12 +525,27 @@ static struct rcu_torture_ops sched_ops = {
        .readdelay = rcu_read_delay,  /* just reuse rcu's version. */
        .readunlock = sched_torture_read_unlock,
        .completed = sched_torture_completed,
-       .deferredfree = rcu_sync_torture_deferred_free,
+       .deferredfree = rcu_sched_torture_deferred_free,
        .sync = sched_torture_synchronize,
+       .cb_barrier = rcu_barrier_sched,
        .stats = NULL,
        .name = "sched"
 };
 
+static struct rcu_torture_ops sched_ops_sync = {
+       .init = rcu_sync_torture_init,
+       .cleanup = NULL,
+       .readlock = sched_torture_read_lock,
+       .readdelay = rcu_read_delay,  /* just reuse rcu's version. */
+       .readunlock = sched_torture_read_unlock,
+       .completed = sched_torture_completed,
+       .deferredfree = rcu_sync_torture_deferred_free,
+       .sync = sched_torture_synchronize,
+       .cb_barrier = NULL,
+       .stats = NULL,
+       .name = "sched_sync"
+};
+
 /*
  * RCU torture writer kthread.  Repeatedly substitutes a new structure
  * for that pointed to by rcu_torture_current, freeing the old structure
@@ -537,6 +583,7 @@ rcu_torture_writer(void *arg)
                }
                rcu_torture_current_version++;
                oldbatch = cur_ops->completed();
+               rcu_stutter_wait();
        } while (!kthread_should_stop() && !fullstop);
        VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping");
        while (!kthread_should_stop())
@@ -560,6 +607,7 @@ rcu_torture_fakewriter(void *arg)
                schedule_timeout_uninterruptible(1 + rcu_random(&rand)%10);
                udelay(rcu_random(&rand) & 0x3ff);
                cur_ops->sync();
+               rcu_stutter_wait();
        } while (!kthread_should_stop() && !fullstop);
 
        VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping");
@@ -615,6 +663,7 @@ rcu_torture_reader(void *arg)
                preempt_enable();
                cur_ops->readunlock(idx);
                schedule();
+               rcu_stutter_wait();
        } while (!kthread_should_stop() && !fullstop);
        VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping");
        while (!kthread_should_stop())
@@ -661,6 +710,7 @@ rcu_torture_printk(char *page)
        if (i > 1) {
                cnt += sprintf(&page[cnt], "!!! ");
                atomic_inc(&n_rcu_torture_error);
+               WARN_ON_ONCE(1);
        }
        cnt += sprintf(&page[cnt], "Reader Pipe: ");
        for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++)
@@ -785,15 +835,34 @@ rcu_torture_shuffle(void *arg)
        return 0;
 }
 
+/* Cause the rcutorture test to "stutter", starting and stopping all
+ * threads periodically.
+ */
+static int
+rcu_torture_stutter(void *arg)
+{
+       VERBOSE_PRINTK_STRING("rcu_torture_stutter task started");
+       do {
+               schedule_timeout_interruptible(stutter * HZ);
+               stutter_pause_test = 1;
+               if (!kthread_should_stop())
+                       schedule_timeout_interruptible(stutter * HZ);
+               stutter_pause_test = 0;
+       } while (!kthread_should_stop());
+       VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping");
+       return 0;
+}
+
 static inline void
 rcu_torture_print_module_parms(char *tag)
 {
        printk(KERN_ALERT "%s" TORTURE_FLAG
                "--- %s: nreaders=%d nfakewriters=%d "
                "stat_interval=%d verbose=%d test_no_idle_hz=%d "
-               "shuffle_interval = %d\n",
+               "shuffle_interval=%d stutter=%d\n",
                torture_type, tag, nrealreaders, nfakewriters,
-               stat_interval, verbose, test_no_idle_hz, shuffle_interval);
+               stat_interval, verbose, test_no_idle_hz, shuffle_interval,
+               stutter);
 }
 
 static void
@@ -802,6 +871,11 @@ rcu_torture_cleanup(void)
        int i;
 
        fullstop = 1;
+       if (stutter_task) {
+               VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task");
+               kthread_stop(stutter_task);
+       }
+       stutter_task = NULL;
        if (shuffler_task) {
                VERBOSE_PRINTK_STRING("Stopping rcu_torture_shuffle task");
                kthread_stop(shuffler_task);
@@ -848,7 +922,9 @@ rcu_torture_cleanup(void)
        stats_task = NULL;
 
        /* Wait for all RCU callbacks to fire.  */
-       rcu_barrier();
+
+       if (cur_ops->cb_barrier != NULL)
+               cur_ops->cb_barrier();
 
        rcu_torture_stats_print();  /* -After- the stats thread is stopped! */
 
@@ -868,7 +944,7 @@ rcu_torture_init(void)
        int firsterr = 0;
        static struct rcu_torture_ops *torture_ops[] =
                { &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops,
-                 &srcu_ops, &sched_ops, };
+                 &srcu_ops, &sched_ops, &sched_ops_sync, };
 
        /* Process args and tell the world that the torturer is on the job. */
        for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
@@ -988,6 +1064,19 @@ rcu_torture_init(void)
                        goto unwind;
                }
        }
+       if (stutter < 0)
+               stutter = 0;
+       if (stutter) {
+               /* Create the stutter thread */
+               stutter_task = kthread_run(rcu_torture_stutter, NULL,
+                                         "rcu_torture_stutter");
+               if (IS_ERR(stutter_task)) {
+                       firsterr = PTR_ERR(stutter_task);
+                       VERBOSE_PRINTK_ERRSTRING("Failed to create stutter");
+                       stutter_task = NULL;
+                       goto unwind;
+               }
+       }
        return 0;
 
 unwind: