|
|
|
@ -136,28 +136,46 @@ static int stutter_pause_test = 0;
|
|
|
|
|
#endif
|
|
|
|
|
int rcutorture_runnable = RCUTORTURE_RUNNABLE_INIT;
|
|
|
|
|
|
|
|
|
|
#define FULLSTOP_SHUTDOWN 1 /* Bail due to system shutdown/panic. */
|
|
|
|
|
#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. */
|
|
|
|
|
/* Mediate rmmod and system shutdown. Concurrent rmmod & shutdown illegal! */
|
|
|
|
|
|
|
|
|
|
#define FULLSTOP_DONTSTOP 0 /* Normal operation. */
|
|
|
|
|
#define FULLSTOP_SHUTDOWN 1 /* System shutdown with rcutorture running. */
|
|
|
|
|
#define FULLSTOP_RMMOD 2 /* Normal rmmod of rcutorture. */
|
|
|
|
|
static int fullstop = FULLSTOP_RMMOD;
|
|
|
|
|
DEFINE_MUTEX(fullstop_mutex); /* Protect fullstop transitions and spawning */
|
|
|
|
|
/* of kthreads. */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Detect and respond to a signal-based shutdown.
|
|
|
|
|
* Detect and respond to a system shutdown.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
rcutorture_shutdown_notify(struct notifier_block *unused1,
|
|
|
|
|
unsigned long unused2, void *unused3)
|
|
|
|
|
{
|
|
|
|
|
if (fullstop)
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
mutex_lock(&fullstop_mutex);
|
|
|
|
|
if (!fullstop)
|
|
|
|
|
if (fullstop == FULLSTOP_DONTSTOP)
|
|
|
|
|
fullstop = FULLSTOP_SHUTDOWN;
|
|
|
|
|
else
|
|
|
|
|
printk(KERN_WARNING /* but going down anyway, so... */
|
|
|
|
|
"Concurrent 'rmmod rcutorture' and shutdown illegal!\n");
|
|
|
|
|
mutex_unlock(&fullstop_mutex);
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Absorb kthreads into a kernel function that won't return, so that
|
|
|
|
|
* they won't ever access module text or data again.
|
|
|
|
|
*/
|
|
|
|
|
static void rcutorture_shutdown_absorb(char *title)
|
|
|
|
|
{
|
|
|
|
|
if (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {
|
|
|
|
|
printk(KERN_NOTICE
|
|
|
|
|
"rcutorture thread %s parking due to system shutdown\n",
|
|
|
|
|
title);
|
|
|
|
|
schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Allocate an element from the rcu_tortures pool.
|
|
|
|
|
*/
|
|
|
|
@ -219,13 +237,14 @@ rcu_random(struct rcu_random_state *rrsp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
rcu_stutter_wait(void)
|
|
|
|
|
rcu_stutter_wait(char *title)
|
|
|
|
|
{
|
|
|
|
|
while ((stutter_pause_test || !rcutorture_runnable) && !fullstop) {
|
|
|
|
|
while (stutter_pause_test || !rcutorture_runnable) {
|
|
|
|
|
if (rcutorture_runnable)
|
|
|
|
|
schedule_timeout_interruptible(1);
|
|
|
|
|
else
|
|
|
|
|
schedule_timeout_interruptible(round_jiffies_relative(HZ));
|
|
|
|
|
rcutorture_shutdown_absorb(title);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -287,7 +306,7 @@ rcu_torture_cb(struct rcu_head *p)
|
|
|
|
|
int i;
|
|
|
|
|
struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu);
|
|
|
|
|
|
|
|
|
|
if (fullstop) {
|
|
|
|
|
if (fullstop != FULLSTOP_DONTSTOP) {
|
|
|
|
|
/* Test is ending, just drop callbacks on the floor. */
|
|
|
|
|
/* The next initialization will pick up the pieces. */
|
|
|
|
|
return;
|
|
|
|
@ -619,10 +638,11 @@ rcu_torture_writer(void *arg)
|
|
|
|
|
}
|
|
|
|
|
rcu_torture_current_version++;
|
|
|
|
|
oldbatch = cur_ops->completed();
|
|
|
|
|
rcu_stutter_wait();
|
|
|
|
|
} while (!kthread_should_stop() && !fullstop);
|
|
|
|
|
rcu_stutter_wait("rcu_torture_writer");
|
|
|
|
|
} while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP);
|
|
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping");
|
|
|
|
|
while (!kthread_should_stop() && fullstop != FULLSTOP_SHUTDOWN)
|
|
|
|
|
rcutorture_shutdown_absorb("rcu_torture_writer");
|
|
|
|
|
while (!kthread_should_stop())
|
|
|
|
|
schedule_timeout_uninterruptible(1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -643,11 +663,12 @@ 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);
|
|
|
|
|
rcu_stutter_wait("rcu_torture_fakewriter");
|
|
|
|
|
} while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP);
|
|
|
|
|
|
|
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_fakewriter task stopping");
|
|
|
|
|
while (!kthread_should_stop() && fullstop != FULLSTOP_SHUTDOWN)
|
|
|
|
|
rcutorture_shutdown_absorb("rcu_torture_fakewriter");
|
|
|
|
|
while (!kthread_should_stop())
|
|
|
|
|
schedule_timeout_uninterruptible(1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -752,12 +773,13 @@ rcu_torture_reader(void *arg)
|
|
|
|
|
preempt_enable();
|
|
|
|
|
cur_ops->readunlock(idx);
|
|
|
|
|
schedule();
|
|
|
|
|
rcu_stutter_wait();
|
|
|
|
|
} while (!kthread_should_stop() && !fullstop);
|
|
|
|
|
rcu_stutter_wait("rcu_torture_reader");
|
|
|
|
|
} while (!kthread_should_stop() && fullstop == FULLSTOP_DONTSTOP);
|
|
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping");
|
|
|
|
|
rcutorture_shutdown_absorb("rcu_torture_reader");
|
|
|
|
|
if (irqreader && cur_ops->irqcapable)
|
|
|
|
|
del_timer_sync(&t);
|
|
|
|
|
while (!kthread_should_stop() && fullstop != FULLSTOP_SHUTDOWN)
|
|
|
|
|
while (!kthread_should_stop())
|
|
|
|
|
schedule_timeout_uninterruptible(1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -854,7 +876,8 @@ rcu_torture_stats(void *arg)
|
|
|
|
|
do {
|
|
|
|
|
schedule_timeout_interruptible(stat_interval * HZ);
|
|
|
|
|
rcu_torture_stats_print();
|
|
|
|
|
} while (!kthread_should_stop() && !fullstop);
|
|
|
|
|
rcutorture_shutdown_absorb("rcu_torture_stats");
|
|
|
|
|
} while (!kthread_should_stop());
|
|
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_stats task stopping");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -866,52 +889,49 @@ static int rcu_idle_cpu; /* Force all torture tasks off this CPU */
|
|
|
|
|
*/
|
|
|
|
|
static void rcu_torture_shuffle_tasks(void)
|
|
|
|
|
{
|
|
|
|
|
cpumask_var_t tmp_mask;
|
|
|
|
|
cpumask_t tmp_mask;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!alloc_cpumask_var(&tmp_mask, GFP_KERNEL))
|
|
|
|
|
BUG();
|
|
|
|
|
|
|
|
|
|
cpumask_setall(tmp_mask);
|
|
|
|
|
cpus_setall(tmp_mask);
|
|
|
|
|
get_online_cpus();
|
|
|
|
|
|
|
|
|
|
/* No point in shuffling if there is only one online CPU (ex: UP) */
|
|
|
|
|
if (num_online_cpus() == 1)
|
|
|
|
|
goto out;
|
|
|
|
|
if (num_online_cpus() == 1) {
|
|
|
|
|
put_online_cpus();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rcu_idle_cpu != -1)
|
|
|
|
|
cpumask_clear_cpu(rcu_idle_cpu, tmp_mask);
|
|
|
|
|
cpu_clear(rcu_idle_cpu, tmp_mask);
|
|
|
|
|
|
|
|
|
|
set_cpus_allowed_ptr(current, tmp_mask);
|
|
|
|
|
set_cpus_allowed_ptr(current, &tmp_mask);
|
|
|
|
|
|
|
|
|
|
if (reader_tasks) {
|
|
|
|
|
for (i = 0; i < nrealreaders; i++)
|
|
|
|
|
if (reader_tasks[i])
|
|
|
|
|
set_cpus_allowed_ptr(reader_tasks[i],
|
|
|
|
|
tmp_mask);
|
|
|
|
|
&tmp_mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fakewriter_tasks) {
|
|
|
|
|
for (i = 0; i < nfakewriters; i++)
|
|
|
|
|
if (fakewriter_tasks[i])
|
|
|
|
|
set_cpus_allowed_ptr(fakewriter_tasks[i],
|
|
|
|
|
tmp_mask);
|
|
|
|
|
&tmp_mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (writer_task)
|
|
|
|
|
set_cpus_allowed_ptr(writer_task, tmp_mask);
|
|
|
|
|
set_cpus_allowed_ptr(writer_task, &tmp_mask);
|
|
|
|
|
|
|
|
|
|
if (stats_task)
|
|
|
|
|
set_cpus_allowed_ptr(stats_task, tmp_mask);
|
|
|
|
|
set_cpus_allowed_ptr(stats_task, &tmp_mask);
|
|
|
|
|
|
|
|
|
|
if (rcu_idle_cpu == -1)
|
|
|
|
|
rcu_idle_cpu = num_online_cpus() - 1;
|
|
|
|
|
else
|
|
|
|
|
rcu_idle_cpu--;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
put_online_cpus();
|
|
|
|
|
free_cpumask_var(tmp_mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Shuffle tasks across CPUs, with the intent of allowing each CPU in the
|
|
|
|
@ -925,7 +945,8 @@ rcu_torture_shuffle(void *arg)
|
|
|
|
|
do {
|
|
|
|
|
schedule_timeout_interruptible(shuffle_interval * HZ);
|
|
|
|
|
rcu_torture_shuffle_tasks();
|
|
|
|
|
} while (!kthread_should_stop() && !fullstop);
|
|
|
|
|
rcutorture_shutdown_absorb("rcu_torture_shuffle");
|
|
|
|
|
} while (!kthread_should_stop());
|
|
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_shuffle task stopping");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -940,10 +961,11 @@ rcu_torture_stutter(void *arg)
|
|
|
|
|
do {
|
|
|
|
|
schedule_timeout_interruptible(stutter * HZ);
|
|
|
|
|
stutter_pause_test = 1;
|
|
|
|
|
if (!kthread_should_stop() && !fullstop)
|
|
|
|
|
if (!kthread_should_stop())
|
|
|
|
|
schedule_timeout_interruptible(stutter * HZ);
|
|
|
|
|
stutter_pause_test = 0;
|
|
|
|
|
} while (!kthread_should_stop() && !fullstop);
|
|
|
|
|
rcutorture_shutdown_absorb("rcu_torture_stutter");
|
|
|
|
|
} while (!kthread_should_stop());
|
|
|
|
|
VERBOSE_PRINTK_STRING("rcu_torture_stutter task stopping");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -970,15 +992,16 @@ rcu_torture_cleanup(void)
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
mutex_lock(&fullstop_mutex);
|
|
|
|
|
if (!fullstop) {
|
|
|
|
|
/* If being signaled, let it happen, then exit. */
|
|
|
|
|
if (fullstop == FULLSTOP_SHUTDOWN) {
|
|
|
|
|
printk(KERN_WARNING /* but going down anyway, so... */
|
|
|
|
|
"Concurrent 'rmmod rcutorture' and shutdown illegal!\n");
|
|
|
|
|
mutex_unlock(&fullstop_mutex);
|
|
|
|
|
schedule_timeout_interruptible(10 * HZ);
|
|
|
|
|
schedule_timeout_uninterruptible(10);
|
|
|
|
|
if (cur_ops->cb_barrier != NULL)
|
|
|
|
|
cur_ops->cb_barrier();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fullstop = FULLSTOP_CLEANUP;
|
|
|
|
|
fullstop = FULLSTOP_RMMOD;
|
|
|
|
|
mutex_unlock(&fullstop_mutex);
|
|
|
|
|
unregister_reboot_notifier(&rcutorture_nb);
|
|
|
|
|
if (stutter_task) {
|
|
|
|
@ -1078,7 +1101,7 @@ rcu_torture_init(void)
|
|
|
|
|
else
|
|
|
|
|
nrealreaders = 2 * num_online_cpus();
|
|
|
|
|
rcu_torture_print_module_parms("Start of test");
|
|
|
|
|
fullstop = 0;
|
|
|
|
|
fullstop = FULLSTOP_DONTSTOP;
|
|
|
|
|
|
|
|
|
|
/* Set up the freelist. */
|
|
|
|
|
|
|
|
|
|