mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 22:21:42 +00:00
rcu: Allow rcutorture to starve grace-period kthread
This commit provides an rcutorture.stall_gp_kthread module parameter to allow rcutorture to starve the grace-period kthread. This allows testing the code that detects such starvation. Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
This commit is contained in:
parent
df5916845d
commit
55b2dcf587
@ -4221,6 +4221,13 @@
|
||||
rcutorture.stall_cpu_irqsoff= [KNL]
|
||||
Disable interrupts while stalling if set.
|
||||
|
||||
rcutorture.stall_gp_kthread= [KNL]
|
||||
Duration (s) of forced sleep within RCU
|
||||
grace-period kthread to test RCU CPU stall
|
||||
warnings, zero to disable. If both stall_cpu
|
||||
and stall_gp_kthread are specified, the
|
||||
kthread is starved first, then the CPU.
|
||||
|
||||
rcutorture.stat_interval= [KNL]
|
||||
Time (s) between statistics printk()s.
|
||||
|
||||
|
@ -454,6 +454,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
|
||||
unsigned long secs,
|
||||
unsigned long c_old,
|
||||
unsigned long c);
|
||||
void rcu_gp_set_torture_wait(int duration);
|
||||
#else
|
||||
static inline void rcutorture_get_gp_data(enum rcutorture_type test_type,
|
||||
int *flags, unsigned long *gp_seq)
|
||||
@ -471,6 +472,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
|
||||
#define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
|
||||
do { } while (0)
|
||||
#endif
|
||||
static inline void rcu_gp_set_torture_wait(int duration) { }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST)
|
||||
|
@ -115,6 +115,8 @@ torture_param(int, stall_cpu_holdoff, 10,
|
||||
"Time to wait before starting stall (s).");
|
||||
torture_param(int, stall_cpu_irqsoff, 0, "Disable interrupts while stalling.");
|
||||
torture_param(int, stall_cpu_block, 0, "Sleep while stalling.");
|
||||
torture_param(int, stall_gp_kthread, 0,
|
||||
"Grace-period kthread stall duration (s).");
|
||||
torture_param(int, stat_interval, 60,
|
||||
"Number of seconds between stats printk()s");
|
||||
torture_param(int, stutter, 5, "Number of seconds to run/halt test");
|
||||
@ -1623,7 +1625,17 @@ static int rcu_torture_stall(void *args)
|
||||
schedule_timeout_interruptible(stall_cpu_holdoff * HZ);
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_stall end holdoff");
|
||||
}
|
||||
if (!kthread_should_stop()) {
|
||||
if (!kthread_should_stop() && stall_gp_kthread > 0) {
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_stall begin GP stall");
|
||||
rcu_gp_set_torture_wait(stall_gp_kthread * HZ);
|
||||
for (idx = 0; idx < stall_gp_kthread + 2; idx++) {
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
schedule_timeout_uninterruptible(HZ);
|
||||
}
|
||||
}
|
||||
if (!kthread_should_stop() && stall_cpu > 0) {
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_stall begin CPU stall");
|
||||
stop_at = ktime_get_seconds() + stall_cpu;
|
||||
/* RCU CPU stall is expected behavior in following code. */
|
||||
idx = cur_ops->readlock();
|
||||
@ -1642,8 +1654,8 @@ static int rcu_torture_stall(void *args)
|
||||
else if (!stall_cpu_block)
|
||||
preempt_enable();
|
||||
cur_ops->readunlock(idx);
|
||||
pr_alert("rcu_torture_stall end.\n");
|
||||
}
|
||||
pr_alert("rcu_torture_stall end.\n");
|
||||
torture_shutdown_absorb("rcu_torture_stall");
|
||||
while (!kthread_should_stop())
|
||||
schedule_timeout_interruptible(10 * HZ);
|
||||
@ -1653,7 +1665,7 @@ static int rcu_torture_stall(void *args)
|
||||
/* Spawn CPU-stall kthread, if stall_cpu specified. */
|
||||
static int __init rcu_torture_stall_init(void)
|
||||
{
|
||||
if (stall_cpu <= 0)
|
||||
if (stall_cpu <= 0 && stall_gp_kthread <= 0)
|
||||
return 0;
|
||||
return torture_create_kthread(rcu_torture_stall, NULL, stall_task);
|
||||
}
|
||||
|
@ -1486,6 +1486,31 @@ static void rcu_gp_slow(int delay)
|
||||
schedule_timeout_uninterruptible(delay);
|
||||
}
|
||||
|
||||
static unsigned long sleep_duration;
|
||||
|
||||
/* Allow rcutorture to stall the grace-period kthread. */
|
||||
void rcu_gp_set_torture_wait(int duration)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST) && duration > 0)
|
||||
WRITE_ONCE(sleep_duration, duration);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_gp_set_torture_wait);
|
||||
|
||||
/* Actually implement the aforementioned wait. */
|
||||
static void rcu_gp_torture_wait(void)
|
||||
{
|
||||
unsigned long duration;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_RCU_TORTURE_TEST))
|
||||
return;
|
||||
duration = xchg(&sleep_duration, 0UL);
|
||||
if (duration > 0) {
|
||||
pr_alert("%s: Waiting %lu jiffies\n", __func__, duration);
|
||||
schedule_timeout_uninterruptible(duration);
|
||||
pr_alert("%s: Wait complete\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a new grace period. Return false if no grace period required.
|
||||
*/
|
||||
@ -1686,6 +1711,7 @@ static void rcu_gp_fqs_loop(void)
|
||||
rcu_state.gp_state = RCU_GP_WAIT_FQS;
|
||||
ret = swait_event_idle_timeout_exclusive(
|
||||
rcu_state.gp_wq, rcu_gp_fqs_check_wake(&gf), j);
|
||||
rcu_gp_torture_wait();
|
||||
rcu_state.gp_state = RCU_GP_DOING_FQS;
|
||||
/* Locking provides needed memory barriers. */
|
||||
/* If grace period done, leave loop. */
|
||||
@ -1834,6 +1860,7 @@ static int __noreturn rcu_gp_kthread(void *unused)
|
||||
swait_event_idle_exclusive(rcu_state.gp_wq,
|
||||
READ_ONCE(rcu_state.gp_flags) &
|
||||
RCU_GP_FLAG_INIT);
|
||||
rcu_gp_torture_wait();
|
||||
rcu_state.gp_state = RCU_GP_DONE_GPS;
|
||||
/* Locking provides needed memory barrier. */
|
||||
if (rcu_gp_init())
|
||||
|
Loading…
Reference in New Issue
Block a user