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:
Paul E. McKenney 2020-04-01 19:57:52 -07:00
parent df5916845d
commit 55b2dcf587
4 changed files with 51 additions and 3 deletions

View File

@ -4221,6 +4221,13 @@
rcutorture.stall_cpu_irqsoff= [KNL] rcutorture.stall_cpu_irqsoff= [KNL]
Disable interrupts while stalling if set. 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] rcutorture.stat_interval= [KNL]
Time (s) between statistics printk()s. Time (s) between statistics printk()s.

View File

@ -454,6 +454,7 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
unsigned long secs, unsigned long secs,
unsigned long c_old, unsigned long c_old,
unsigned long c); unsigned long c);
void rcu_gp_set_torture_wait(int duration);
#else #else
static inline void rcutorture_get_gp_data(enum rcutorture_type test_type, static inline void rcutorture_get_gp_data(enum rcutorture_type test_type,
int *flags, unsigned long *gp_seq) 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) \ #define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
do { } while (0) do { } while (0)
#endif #endif
static inline void rcu_gp_set_torture_wait(int duration) { }
#endif #endif
#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST) #if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST)

View File

@ -115,6 +115,8 @@ torture_param(int, stall_cpu_holdoff, 10,
"Time to wait before starting stall (s)."); "Time to wait before starting stall (s).");
torture_param(int, stall_cpu_irqsoff, 0, "Disable interrupts while stalling."); 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_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, torture_param(int, stat_interval, 60,
"Number of seconds between stats printk()s"); "Number of seconds between stats printk()s");
torture_param(int, stutter, 5, "Number of seconds to run/halt test"); 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); schedule_timeout_interruptible(stall_cpu_holdoff * HZ);
VERBOSE_TOROUT_STRING("rcu_torture_stall end holdoff"); 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; stop_at = ktime_get_seconds() + stall_cpu;
/* RCU CPU stall is expected behavior in following code. */ /* RCU CPU stall is expected behavior in following code. */
idx = cur_ops->readlock(); idx = cur_ops->readlock();
@ -1642,8 +1654,8 @@ static int rcu_torture_stall(void *args)
else if (!stall_cpu_block) else if (!stall_cpu_block)
preempt_enable(); preempt_enable();
cur_ops->readunlock(idx); cur_ops->readunlock(idx);
pr_alert("rcu_torture_stall end.\n");
} }
pr_alert("rcu_torture_stall end.\n");
torture_shutdown_absorb("rcu_torture_stall"); torture_shutdown_absorb("rcu_torture_stall");
while (!kthread_should_stop()) while (!kthread_should_stop())
schedule_timeout_interruptible(10 * HZ); schedule_timeout_interruptible(10 * HZ);
@ -1653,7 +1665,7 @@ static int rcu_torture_stall(void *args)
/* Spawn CPU-stall kthread, if stall_cpu specified. */ /* Spawn CPU-stall kthread, if stall_cpu specified. */
static int __init rcu_torture_stall_init(void) static int __init rcu_torture_stall_init(void)
{ {
if (stall_cpu <= 0) if (stall_cpu <= 0 && stall_gp_kthread <= 0)
return 0; return 0;
return torture_create_kthread(rcu_torture_stall, NULL, stall_task); return torture_create_kthread(rcu_torture_stall, NULL, stall_task);
} }

View File

@ -1486,6 +1486,31 @@ static void rcu_gp_slow(int delay)
schedule_timeout_uninterruptible(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. * 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; rcu_state.gp_state = RCU_GP_WAIT_FQS;
ret = swait_event_idle_timeout_exclusive( ret = swait_event_idle_timeout_exclusive(
rcu_state.gp_wq, rcu_gp_fqs_check_wake(&gf), j); rcu_state.gp_wq, rcu_gp_fqs_check_wake(&gf), j);
rcu_gp_torture_wait();
rcu_state.gp_state = RCU_GP_DOING_FQS; rcu_state.gp_state = RCU_GP_DOING_FQS;
/* Locking provides needed memory barriers. */ /* Locking provides needed memory barriers. */
/* If grace period done, leave loop. */ /* 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, swait_event_idle_exclusive(rcu_state.gp_wq,
READ_ONCE(rcu_state.gp_flags) & READ_ONCE(rcu_state.gp_flags) &
RCU_GP_FLAG_INIT); RCU_GP_FLAG_INIT);
rcu_gp_torture_wait();
rcu_state.gp_state = RCU_GP_DONE_GPS; rcu_state.gp_state = RCU_GP_DONE_GPS;
/* Locking provides needed memory barrier. */ /* Locking provides needed memory barrier. */
if (rcu_gp_init()) if (rcu_gp_init())