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]
|
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.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
Loading…
Reference in New Issue