xfs: remove nr_to_write writeback windup.
[safe/jmp/linux-2.6] / kernel / rcutorture.c
index 0b52175..6535ac8 100644 (file)
@@ -61,6 +61,9 @@ static int test_no_idle_hz;   /* Test RCU's support for tickless idle CPUs. */
 static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/
 static int stutter = 5;                /* Start/stop testing interval (in sec) */
 static int irqreader = 1;      /* RCU readers from irq (timers). */
+static int fqs_duration = 0;   /* Duration of bursts (us), 0 to disable. */
+static int fqs_holdoff = 0;    /* Hold time within burst (us). */
+static int fqs_stutter = 3;    /* Wait time between bursts (s). */
 static char *torture_type = "rcu"; /* What RCU implementation to torture. */
 
 module_param(nreaders, int, 0444);
@@ -79,6 +82,12 @@ module_param(stutter, int, 0444);
 MODULE_PARM_DESC(stutter, "Number of seconds to run/halt test");
 module_param(irqreader, int, 0444);
 MODULE_PARM_DESC(irqreader, "Allow RCU readers from irq handlers");
+module_param(fqs_duration, int, 0444);
+MODULE_PARM_DESC(fqs_duration, "Duration of fqs bursts (us)");
+module_param(fqs_holdoff, int, 0444);
+MODULE_PARM_DESC(fqs_holdoff, "Holdoff time within fqs bursts (us)");
+module_param(fqs_stutter, int, 0444);
+MODULE_PARM_DESC(fqs_stutter, "Wait time between fqs bursts (s)");
 module_param(torture_type, charp, 0444);
 MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)");
 
@@ -99,6 +108,7 @@ static struct task_struct **reader_tasks;
 static struct task_struct *stats_task;
 static struct task_struct *shuffler_task;
 static struct task_struct *stutter_task;
+static struct task_struct *fqs_task;
 
 #define RCU_TORTURE_PIPE_LEN 10
 
@@ -263,6 +273,7 @@ struct rcu_torture_ops {
        void (*deferred_free)(struct rcu_torture *p);
        void (*sync)(void);
        void (*cb_barrier)(void);
+       void (*fqs)(void);
        int (*stats)(char *page);
        int irq_capable;
        char *name;
@@ -347,6 +358,7 @@ static struct rcu_torture_ops rcu_ops = {
        .deferred_free  = rcu_torture_deferred_free,
        .sync           = synchronize_rcu,
        .cb_barrier     = rcu_barrier,
+       .fqs            = rcu_force_quiescent_state,
        .stats          = NULL,
        .irq_capable    = 1,
        .name           = "rcu"
@@ -388,6 +400,7 @@ static struct rcu_torture_ops rcu_sync_ops = {
        .deferred_free  = rcu_sync_torture_deferred_free,
        .sync           = synchronize_rcu,
        .cb_barrier     = NULL,
+       .fqs            = rcu_force_quiescent_state,
        .stats          = NULL,
        .irq_capable    = 1,
        .name           = "rcu_sync"
@@ -403,6 +416,7 @@ static struct rcu_torture_ops rcu_expedited_ops = {
        .deferred_free  = rcu_sync_torture_deferred_free,
        .sync           = synchronize_rcu_expedited,
        .cb_barrier     = NULL,
+       .fqs            = rcu_force_quiescent_state,
        .stats          = NULL,
        .irq_capable    = 1,
        .name           = "rcu_expedited"
@@ -450,9 +464,11 @@ static void rcu_bh_torture_synchronize(void)
 {
        struct rcu_bh_torture_synchronize rcu;
 
+       init_rcu_head_on_stack(&rcu.head);
        init_completion(&rcu.completion);
        call_rcu_bh(&rcu.head, rcu_bh_torture_wakeme_after_cb);
        wait_for_completion(&rcu.completion);
+       destroy_rcu_head_on_stack(&rcu.head);
 }
 
 static struct rcu_torture_ops rcu_bh_ops = {
@@ -465,6 +481,7 @@ static struct rcu_torture_ops rcu_bh_ops = {
        .deferred_free  = rcu_bh_torture_deferred_free,
        .sync           = rcu_bh_torture_synchronize,
        .cb_barrier     = rcu_barrier_bh,
+       .fqs            = rcu_bh_force_quiescent_state,
        .stats          = NULL,
        .irq_capable    = 1,
        .name           = "rcu_bh"
@@ -480,6 +497,7 @@ static struct rcu_torture_ops rcu_bh_sync_ops = {
        .deferred_free  = rcu_sync_torture_deferred_free,
        .sync           = rcu_bh_torture_synchronize,
        .cb_barrier     = NULL,
+       .fqs            = rcu_bh_force_quiescent_state,
        .stats          = NULL,
        .irq_capable    = 1,
        .name           = "rcu_bh_sync"
@@ -621,6 +639,7 @@ static struct rcu_torture_ops sched_ops = {
        .deferred_free  = rcu_sched_torture_deferred_free,
        .sync           = sched_torture_synchronize,
        .cb_barrier     = rcu_barrier_sched,
+       .fqs            = rcu_sched_force_quiescent_state,
        .stats          = NULL,
        .irq_capable    = 1,
        .name           = "sched"
@@ -636,6 +655,7 @@ static struct rcu_torture_ops sched_sync_ops = {
        .deferred_free  = rcu_sync_torture_deferred_free,
        .sync           = sched_torture_synchronize,
        .cb_barrier     = NULL,
+       .fqs            = rcu_sched_force_quiescent_state,
        .stats          = NULL,
        .name           = "sched_sync"
 };
@@ -650,12 +670,45 @@ static struct rcu_torture_ops sched_expedited_ops = {
        .deferred_free  = rcu_sync_torture_deferred_free,
        .sync           = synchronize_sched_expedited,
        .cb_barrier     = NULL,
-       .stats          = rcu_expedited_torture_stats,
+       .fqs            = rcu_sched_force_quiescent_state,
+       .stats          = NULL,
        .irq_capable    = 1,
        .name           = "sched_expedited"
 };
 
 /*
+ * RCU torture force-quiescent-state kthread.  Repeatedly induces
+ * bursts of calls to force_quiescent_state(), increasing the probability
+ * of occurrence of some important types of race conditions.
+ */
+static int
+rcu_torture_fqs(void *arg)
+{
+       unsigned long fqs_resume_time;
+       int fqs_burst_remaining;
+
+       VERBOSE_PRINTK_STRING("rcu_torture_fqs task started");
+       do {
+               fqs_resume_time = jiffies + fqs_stutter * HZ;
+               while (jiffies - fqs_resume_time > LONG_MAX) {
+                       schedule_timeout_interruptible(1);
+               }
+               fqs_burst_remaining = fqs_duration;
+               while (fqs_burst_remaining > 0) {
+                       cur_ops->fqs();
+                       udelay(fqs_holdoff);
+                       fqs_burst_remaining -= fqs_holdoff;
+               }
+               rcu_stutter_wait("rcu_torture_fqs");
+       } while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP);
+       VERBOSE_PRINTK_STRING("rcu_torture_fqs task stopping");
+       rcutorture_shutdown_absorb("rcu_torture_fqs");
+       while (!kthread_should_stop())
+               schedule_timeout_uninterruptible(1);
+       return 0;
+}
+
+/*
  * RCU torture writer kthread.  Repeatedly substitutes a new structure
  * for that pointed to by rcu_torture_current, freeing the old structure
  * after a series of grace periods (the "pipeline").
@@ -745,7 +798,11 @@ static void rcu_torture_timer(unsigned long unused)
 
        idx = cur_ops->readlock();
        completed = cur_ops->completed();
-       p = rcu_dereference(rcu_torture_current);
+       p = rcu_dereference_check(rcu_torture_current,
+                                 rcu_read_lock_held() ||
+                                 rcu_read_lock_bh_held() ||
+                                 rcu_read_lock_sched_held() ||
+                                 srcu_read_lock_held(&srcu_ctl));
        if (p == NULL) {
                /* Leave because rcu_torture_writer is not yet underway */
                cur_ops->readunlock(idx);
@@ -798,11 +855,15 @@ rcu_torture_reader(void *arg)
        do {
                if (irqreader && cur_ops->irq_capable) {
                        if (!timer_pending(&t))
-                               mod_timer(&t, 1);
+                               mod_timer(&t, jiffies + 1);
                }
                idx = cur_ops->readlock();
                completed = cur_ops->completed();
-               p = rcu_dereference(rcu_torture_current);
+               p = rcu_dereference_check(rcu_torture_current,
+                                         rcu_read_lock_held() ||
+                                         rcu_read_lock_bh_held() ||
+                                         rcu_read_lock_sched_held() ||
+                                         srcu_read_lock_held(&srcu_ctl));
                if (p == NULL) {
                        /* Wait for rcu_torture_writer to get underway */
                        cur_ops->readunlock(idx);
@@ -1030,10 +1091,11 @@ 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 stutter=%d irqreader=%d\n",
+               "shuffle_interval=%d stutter=%d irqreader=%d "
+               "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d\n",
                torture_type, tag, nrealreaders, nfakewriters,
                stat_interval, verbose, test_no_idle_hz, shuffle_interval,
-               stutter, irqreader);
+               stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter);
 }
 
 static struct notifier_block rcutorture_nb = {
@@ -1109,6 +1171,12 @@ rcu_torture_cleanup(void)
        }
        stats_task = NULL;
 
+       if (fqs_task) {
+               VERBOSE_PRINTK_STRING("Stopping rcu_torture_fqs task");
+               kthread_stop(fqs_task);
+       }
+       fqs_task = NULL;
+
        /* Wait for all RCU callbacks to fire.  */
 
        if (cur_ops->cb_barrier != NULL)
@@ -1154,6 +1222,11 @@ rcu_torture_init(void)
                mutex_unlock(&fullstop_mutex);
                return -EINVAL;
        }
+       if (cur_ops->fqs == NULL && fqs_duration != 0) {
+               printk(KERN_ALERT "rcu-torture: ->fqs NULL and non-zero "
+                                 "fqs_duration, fqs disabled.\n");
+               fqs_duration = 0;
+       }
        if (cur_ops->init)
                cur_ops->init(); /* no "goto unwind" prior to this point!!! */
 
@@ -1282,6 +1355,19 @@ rcu_torture_init(void)
                        goto unwind;
                }
        }
+       if (fqs_duration < 0)
+               fqs_duration = 0;
+       if (fqs_duration) {
+               /* Create the stutter thread */
+               fqs_task = kthread_run(rcu_torture_fqs, NULL,
+                                      "rcu_torture_fqs");
+               if (IS_ERR(fqs_task)) {
+                       firsterr = PTR_ERR(fqs_task);
+                       VERBOSE_PRINTK_ERRSTRING("Failed to create fqs");
+                       fqs_task = NULL;
+                       goto unwind;
+               }
+       }
        register_reboot_notifier(&rcutorture_nb);
        mutex_unlock(&fullstop_mutex);
        return 0;