|
@@ -39,6 +39,7 @@
|
|
|
#include <linux/moduleparam.h>
|
|
|
#include <linux/percpu.h>
|
|
|
#include <linux/notifier.h>
|
|
|
+#include <linux/reboot.h>
|
|
|
#include <linux/freezer.h>
|
|
|
#include <linux/cpu.h>
|
|
|
#include <linux/delay.h>
|
|
@@ -108,7 +109,6 @@ struct rcu_torture {
|
|
|
int rtort_mbtest;
|
|
|
};
|
|
|
|
|
|
-static int fullstop = 0; /* stop generating callbacks at test end. */
|
|
|
static LIST_HEAD(rcu_torture_freelist);
|
|
|
static struct rcu_torture *rcu_torture_current = NULL;
|
|
|
static long rcu_torture_current_version = 0;
|
|
@@ -136,6 +136,30 @@ static int stutter_pause_test = 0;
|
|
|
#endif
|
|
|
int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT;
|
|
|
|
|
|
+#define FULLSTOP_SIGNALED 1 /* Bail due to signal. */
|
|
|
+#define FULLSTOP_CLEANUP 2 /* Orderly shutdown. */
|
|
|
+static int fullstop; /* stop generating callbacks at test end. */
|
|
|
+DEFINE_MUTEX(fullstop_mutex); /* protect fullstop transitions and */
|
|
|
+ /* spawning of kthreads. */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Detect and respond to a signal-based shutdown.
|
|
|
+ */
|
|
|
+static int
|
|
|
+rcutorture_shutdown_notify(struct notifier_block *unused1,
|
|
|
+ unsigned long unused2, void *unused3)
|
|
|
+{
|
|
|
+ if (fullstop)
|
|
|
+ return NOTIFY_DONE;
|
|
|
+ if (signal_pending(current)) {
|
|
|
+ mutex_lock(&fullstop_mutex);
|
|
|
+ if (!ACCESS_ONCE(fullstop))
|
|
|
+ fullstop = FULLSTOP_SIGNALED;
|
|
|
+ mutex_unlock(&fullstop_mutex);
|
|
|
+ }
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Allocate an element from the rcu_tortures pool.
|
|
|
*/
|
|
@@ -199,11 +223,12 @@ rcu_random(struct rcu_random_state *rrsp)
|
|
|
static void
|
|
|
rcu_stutter_wait(void)
|
|
|
{
|
|
|
- while (stutter_pause_test || !rcutorture_runnable)
|
|
|
+ while ((stutter_pause_test || !rcutorture_runnable) && !fullstop) {
|
|
|
if (rcutorture_runnable)
|
|
|
schedule_timeout_interruptible(1);
|
|
|
else
|
|
|
schedule_timeout_interruptible(round_jiffies_relative(HZ));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -599,7 +624,7 @@ rcu_torture_writer(void *arg)
|
|
|
rcu_stutter_wait();
|
|
|
} while (!kthread_should_stop() && !fullstop);
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping");
|
|
|
- while (!kthread_should_stop())
|
|
|
+ while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED)
|
|
|
schedule_timeout_uninterruptible(1);
|
|
|
return 0;
|
|
|
}
|
|
@@ -624,7 +649,7 @@ rcu_torture_fakewriter(void *arg)
|
|
|
} while (!kthread_should_stop() && !fullstop);
|
|
|
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping");
|
|
|
- while (!kthread_should_stop())
|
|
|
+ while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED)
|
|
|
schedule_timeout_uninterruptible(1);
|
|
|
return 0;
|
|
|
}
|
|
@@ -734,7 +759,7 @@ rcu_torture_reader(void *arg)
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping");
|
|
|
if (irqreader && cur_ops->irqcapable)
|
|
|
del_timer_sync(&t);
|
|
|
- while (!kthread_should_stop())
|
|
|
+ while (!kthread_should_stop() && fullstop != FULLSTOP_SIGNALED)
|
|
|
schedule_timeout_uninterruptible(1);
|
|
|
return 0;
|
|
|
}
|
|
@@ -831,7 +856,7 @@ rcu_torture_stats(void *arg)
|
|
|
do {
|
|
|
schedule_timeout_interruptible(stat_interval * HZ);
|
|
|
rcu_torture_stats_print();
|
|
|
- } while (!kthread_should_stop());
|
|
|
+ } while (!kthread_should_stop() && !fullstop);
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping");
|
|
|
return 0;
|
|
|
}
|
|
@@ -899,7 +924,7 @@ rcu_torture_shuffle(void *arg)
|
|
|
do {
|
|
|
schedule_timeout_interruptible(shuffle_interval * HZ);
|
|
|
rcu_torture_shuffle_tasks();
|
|
|
- } while (!kthread_should_stop());
|
|
|
+ } while (!kthread_should_stop() && !fullstop);
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping");
|
|
|
return 0;
|
|
|
}
|
|
@@ -914,10 +939,10 @@ rcu_torture_stutter(void *arg)
|
|
|
do {
|
|
|
schedule_timeout_interruptible(stutter * HZ);
|
|
|
stutter_pause_test = 1;
|
|
|
- if (!kthread_should_stop())
|
|
|
+ if (!kthread_should_stop() && !fullstop)
|
|
|
schedule_timeout_interruptible(stutter * HZ);
|
|
|
stutter_pause_test = 0;
|
|
|
- } while (!kthread_should_stop());
|
|
|
+ } while (!kthread_should_stop() && !fullstop);
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping");
|
|
|
return 0;
|
|
|
}
|
|
@@ -934,12 +959,27 @@ rcu_torture_print_module_parms(char *tag)
|
|
|
stutter, irqreader);
|
|
|
}
|
|
|
|
|
|
+static struct notifier_block rcutorture_nb = {
|
|
|
+ .notifier_call = rcutorture_shutdown_notify,
|
|
|
+};
|
|
|
+
|
|
|
static void
|
|
|
rcu_torture_cleanup(void)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- fullstop = 1;
|
|
|
+ mutex_lock(&fullstop_mutex);
|
|
|
+ if (!fullstop) {
|
|
|
+ /* If being signaled, let it happen, then exit. */
|
|
|
+ mutex_unlock(&fullstop_mutex);
|
|
|
+ schedule_timeout_interruptible(10 * HZ);
|
|
|
+ if (cur_ops->cb_barrier != NULL)
|
|
|
+ cur_ops->cb_barrier();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ fullstop = FULLSTOP_CLEANUP;
|
|
|
+ mutex_unlock(&fullstop_mutex);
|
|
|
+ unregister_reboot_notifier(&rcutorture_nb);
|
|
|
if (stutter_task) {
|
|
|
VERBOSE_PRINTK_STRING("Stopping rcu_torture_stutter task");
|
|
|
kthread_stop(stutter_task);
|
|
@@ -1015,6 +1055,8 @@ rcu_torture_init(void)
|
|
|
{ &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops,
|
|
|
&srcu_ops, &sched_ops, &sched_ops_sync, };
|
|
|
|
|
|
+ mutex_lock(&fullstop_mutex);
|
|
|
+
|
|
|
/* Process args and tell the world that the torturer is on the job. */
|
|
|
for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
|
|
|
cur_ops = torture_ops[i];
|
|
@@ -1024,6 +1066,7 @@ rcu_torture_init(void)
|
|
|
if (i == ARRAY_SIZE(torture_ops)) {
|
|
|
printk(KERN_ALERT "rcutorture: invalid torture type: \"%s\"\n",
|
|
|
torture_type);
|
|
|
+ mutex_unlock(&fullstop_mutex);
|
|
|
return (-EINVAL);
|
|
|
}
|
|
|
if (cur_ops->init)
|
|
@@ -1146,9 +1189,12 @@ rcu_torture_init(void)
|
|
|
goto unwind;
|
|
|
}
|
|
|
}
|
|
|
+ register_reboot_notifier(&rcutorture_nb);
|
|
|
+ mutex_unlock(&fullstop_mutex);
|
|
|
return 0;
|
|
|
|
|
|
unwind:
|
|
|
+ mutex_unlock(&fullstop_mutex);
|
|
|
rcu_torture_cleanup();
|
|
|
return firsterr;
|
|
|
}
|