Skip to content

Commit 82e3100

Browse files
committed
rcutorture: Enable multiple concurrent callback-flood kthreads
This commit converts the rcutorture.fwd_progress module parameter from bool to int, so that it specifies the number of callback-flood kthreads. Values less than zero specify one kthread per CPU, however, the number of kthreads executing concurrently is limited to the number of online CPUs. This commit also reverse the order of the need-resched and callback-flood operations to cause the callback flooding to happen more nearly at the same time. Cc: Neeraj Upadhyay <neeraj.iitr10@gmail.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
1 parent 12e8854 commit 82e3100

2 files changed

Lines changed: 91 additions & 29 deletions

File tree

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4603,8 +4603,12 @@
46034603
in seconds.
46044604

46054605
rcutorture.fwd_progress= [KNL]
4606-
Enable RCU grace-period forward-progress testing
4606+
Specifies the number of kthreads to be used
4607+
for RCU grace-period forward-progress testing
46074608
for the types of RCU supporting this notion.
4609+
Defaults to 1 kthread, values less than zero or
4610+
greater than the number of CPUs cause the number
4611+
of CPUs to be used.
46084612

46094613
rcutorture.fwd_progress_div= [KNL]
46104614
Specify the fraction of a CPU-stall-warning

kernel/rcu/rcutorture.c

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ torture_param(int, fqs_duration, 0,
7979
"Duration of fqs bursts (us), 0 to disable");
8080
torture_param(int, fqs_holdoff, 0, "Holdoff time within fqs bursts (us)");
8181
torture_param(int, fqs_stutter, 3, "Wait time between fqs bursts (s)");
82-
torture_param(bool, fwd_progress, 1, "Test grace-period forward progress");
82+
torture_param(int, fwd_progress, 1, "Test grace-period forward progress");
8383
torture_param(int, fwd_progress_div, 4, "Fraction of CPU stall to wait");
8484
torture_param(int, fwd_progress_holdoff, 60,
8585
"Time between forward-progress tests (s)");
@@ -146,7 +146,7 @@ static struct task_struct *stats_task;
146146
static struct task_struct *fqs_task;
147147
static struct task_struct *boost_tasks[NR_CPUS];
148148
static struct task_struct *stall_task;
149-
static struct task_struct *fwd_prog_task;
149+
static struct task_struct **fwd_prog_tasks;
150150
static struct task_struct **barrier_cbs_tasks;
151151
static struct task_struct *barrier_task;
152152
static struct task_struct *read_exit_task;
@@ -2161,10 +2161,12 @@ struct rcu_fwd {
21612161
unsigned long rcu_fwd_startat;
21622162
struct rcu_launder_hist n_launders_hist[N_LAUNDERS_HIST];
21632163
unsigned long rcu_launder_gp_seq_start;
2164+
int rcu_fwd_id;
21642165
};
21652166

21662167
static DEFINE_MUTEX(rcu_fwd_mutex);
21672168
static struct rcu_fwd *rcu_fwds;
2169+
static unsigned long rcu_fwd_seq;
21682170
static bool rcu_fwd_emergency_stop;
21692171

21702172
static void rcu_torture_fwd_cb_hist(struct rcu_fwd *rfp)
@@ -2177,8 +2179,9 @@ static void rcu_torture_fwd_cb_hist(struct rcu_fwd *rfp)
21772179
for (i = ARRAY_SIZE(rfp->n_launders_hist) - 1; i > 0; i--)
21782180
if (rfp->n_launders_hist[i].n_launders > 0)
21792181
break;
2180-
pr_alert("%s: Callback-invocation histogram (duration %lu jiffies):",
2181-
__func__, jiffies - rfp->rcu_fwd_startat);
2182+
mutex_lock(&rcu_fwd_mutex); // Serialize histograms.
2183+
pr_alert("%s: Callback-invocation histogram %d (duration %lu jiffies):",
2184+
__func__, rfp->rcu_fwd_id, jiffies - rfp->rcu_fwd_startat);
21822185
gps_old = rfp->rcu_launder_gp_seq_start;
21832186
for (j = 0; j <= i; j++) {
21842187
gps = rfp->n_launders_hist[j].launder_gp_seq;
@@ -2189,6 +2192,7 @@ static void rcu_torture_fwd_cb_hist(struct rcu_fwd *rfp)
21892192
gps_old = gps;
21902193
}
21912194
pr_cont("\n");
2195+
mutex_unlock(&rcu_fwd_mutex);
21922196
}
21932197

21942198
/* Callback function for continuous-flood RCU callbacks. */
@@ -2314,7 +2318,8 @@ static void rcu_torture_fwd_prog_nr(struct rcu_fwd *rfp,
23142318
cver = READ_ONCE(rcu_torture_current_version) - cver;
23152319
gps = rcutorture_seq_diff(cur_ops->get_gp_seq(), gps);
23162320
WARN_ON(!cver && gps < 2);
2317-
pr_alert("%s: Duration %ld cver %ld gps %ld\n", __func__, dur, cver, gps);
2321+
pr_alert("%s: %d Duration %ld cver %ld gps %ld\n", __func__,
2322+
rfp->rcu_fwd_id, dur, cver, gps);
23182323
}
23192324
if (selfpropcb) {
23202325
WRITE_ONCE(fcs.stop, 1);
@@ -2432,6 +2437,8 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp)
24322437
static int rcutorture_oom_notify(struct notifier_block *self,
24332438
unsigned long notused, void *nfreed)
24342439
{
2440+
int i;
2441+
long ncbs;
24352442
struct rcu_fwd *rfp;
24362443

24372444
mutex_lock(&rcu_fwd_mutex);
@@ -2442,18 +2449,26 @@ static int rcutorture_oom_notify(struct notifier_block *self,
24422449
}
24432450
WARN(1, "%s invoked upon OOM during forward-progress testing.\n",
24442451
__func__);
2445-
rcu_torture_fwd_cb_hist(rfp);
2446-
rcu_fwd_progress_check(1 + (jiffies - READ_ONCE(rfp->rcu_fwd_startat)) / 2);
2452+
for (i = 0; i < fwd_progress; i++) {
2453+
rcu_torture_fwd_cb_hist(&rfp[i]);
2454+
rcu_fwd_progress_check(1 + (jiffies - READ_ONCE(rfp[i].rcu_fwd_startat)) / 2);
2455+
}
24472456
WRITE_ONCE(rcu_fwd_emergency_stop, true);
24482457
smp_mb(); /* Emergency stop before free and wait to avoid hangs. */
2449-
pr_info("%s: Freed %lu RCU callbacks.\n",
2450-
__func__, rcu_torture_fwd_prog_cbfree(rfp));
2458+
ncbs = 0;
2459+
for (i = 0; i < fwd_progress; i++)
2460+
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
2461+
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
24512462
rcu_barrier();
2452-
pr_info("%s: Freed %lu RCU callbacks.\n",
2453-
__func__, rcu_torture_fwd_prog_cbfree(rfp));
2463+
ncbs = 0;
2464+
for (i = 0; i < fwd_progress; i++)
2465+
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
2466+
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
24542467
rcu_barrier();
2455-
pr_info("%s: Freed %lu RCU callbacks.\n",
2456-
__func__, rcu_torture_fwd_prog_cbfree(rfp));
2468+
ncbs = 0;
2469+
for (i = 0; i < fwd_progress; i++)
2470+
ncbs += rcu_torture_fwd_prog_cbfree(&rfp[i]);
2471+
pr_info("%s: Freed %lu RCU callbacks.\n", __func__, ncbs);
24572472
smp_mb(); /* Frees before return to avoid redoing OOM. */
24582473
(*(unsigned long *)nfreed)++; /* Forward progress CBs freed! */
24592474
pr_info("%s returning after OOM processing.\n", __func__);
@@ -2469,6 +2484,7 @@ static struct notifier_block rcutorture_oom_nb = {
24692484
static int rcu_torture_fwd_prog(void *args)
24702485
{
24712486
int oldnice = task_nice(current);
2487+
unsigned long oldseq = READ_ONCE(rcu_fwd_seq);
24722488
struct rcu_fwd *rfp = args;
24732489
int tested = 0;
24742490
int tested_tries = 0;
@@ -2478,39 +2494,59 @@ static int rcu_torture_fwd_prog(void *args)
24782494
if (!IS_ENABLED(CONFIG_SMP) || !IS_ENABLED(CONFIG_RCU_BOOST))
24792495
set_user_nice(current, MAX_NICE);
24802496
do {
2481-
schedule_timeout_interruptible(fwd_progress_holdoff * HZ);
2482-
WRITE_ONCE(rcu_fwd_emergency_stop, false);
2497+
if (!rfp->rcu_fwd_id) {
2498+
schedule_timeout_interruptible(fwd_progress_holdoff * HZ);
2499+
WRITE_ONCE(rcu_fwd_emergency_stop, false);
2500+
WRITE_ONCE(rcu_fwd_seq, rcu_fwd_seq + 1);
2501+
} else {
2502+
while (READ_ONCE(rcu_fwd_seq) == oldseq)
2503+
schedule_timeout_interruptible(1);
2504+
oldseq = READ_ONCE(rcu_fwd_seq);
2505+
}
2506+
pr_alert("%s: Starting forward-progress test %d\n", __func__, rfp->rcu_fwd_id);
2507+
if (rcu_inkernel_boot_has_ended() && torture_num_online_cpus() > rfp->rcu_fwd_id)
2508+
rcu_torture_fwd_prog_cr(rfp);
24832509
if (!IS_ENABLED(CONFIG_TINY_RCU) ||
2484-
rcu_inkernel_boot_has_ended())
2510+
(rcu_inkernel_boot_has_ended() && torture_num_online_cpus() > rfp->rcu_fwd_id))
24852511
rcu_torture_fwd_prog_nr(rfp, &tested, &tested_tries);
2486-
if (rcu_inkernel_boot_has_ended())
2487-
rcu_torture_fwd_prog_cr(rfp);
24882512

24892513
/* Avoid slow periods, better to test when busy. */
24902514
if (stutter_wait("rcu_torture_fwd_prog"))
24912515
sched_set_normal(current, oldnice);
24922516
} while (!torture_must_stop());
24932517
/* Short runs might not contain a valid forward-progress attempt. */
2494-
WARN_ON(!tested && tested_tries >= 5);
2495-
pr_alert("%s: tested %d tested_tries %d\n", __func__, tested, tested_tries);
2518+
if (!rfp->rcu_fwd_id) {
2519+
WARN_ON(!tested && tested_tries >= 5);
2520+
pr_alert("%s: tested %d tested_tries %d\n", __func__, tested, tested_tries);
2521+
}
24962522
torture_kthread_stopping("rcu_torture_fwd_prog");
24972523
return 0;
24982524
}
24992525

25002526
/* If forward-progress checking is requested and feasible, spawn the thread. */
25012527
static int __init rcu_torture_fwd_prog_init(void)
25022528
{
2529+
int i;
2530+
int ret = 0;
25032531
struct rcu_fwd *rfp;
25042532

25052533
if (!fwd_progress)
25062534
return 0; /* Not requested, so don't do it. */
2535+
if (fwd_progress >= nr_cpu_ids) {
2536+
VERBOSE_TOROUT_STRING("rcu_torture_fwd_prog_init: Limiting fwd_progress to # CPUs.\n");
2537+
fwd_progress = nr_cpu_ids;
2538+
} else if (fwd_progress < 0) {
2539+
fwd_progress = nr_cpu_ids;
2540+
}
25072541
if ((!cur_ops->sync && !cur_ops->call) ||
25082542
!cur_ops->stall_dur || cur_ops->stall_dur() <= 0 || cur_ops == &rcu_busted_ops) {
25092543
VERBOSE_TOROUT_STRING("rcu_torture_fwd_prog_init: Disabled, unsupported by RCU flavor under test");
2544+
fwd_progress = 0;
25102545
return 0;
25112546
}
25122547
if (stall_cpu > 0) {
25132548
VERBOSE_TOROUT_STRING("rcu_torture_fwd_prog_init: Disabled, conflicts with CPU-stall testing");
2549+
fwd_progress = 0;
25142550
if (IS_MODULE(CONFIG_RCU_TORTURE_TEST))
25152551
return -EINVAL; /* In module, can fail back to user. */
25162552
WARN_ON(1); /* Make sure rcutorture notices conflict. */
@@ -2520,29 +2556,51 @@ static int __init rcu_torture_fwd_prog_init(void)
25202556
fwd_progress_holdoff = 1;
25212557
if (fwd_progress_div <= 0)
25222558
fwd_progress_div = 4;
2523-
rfp = kzalloc(sizeof(*rfp), GFP_KERNEL);
2524-
if (!rfp)
2559+
rfp = kcalloc(fwd_progress, sizeof(*rfp), GFP_KERNEL);
2560+
fwd_prog_tasks = kcalloc(fwd_progress, sizeof(*fwd_prog_tasks), GFP_KERNEL);
2561+
if (!rfp || !fwd_prog_tasks) {
2562+
kfree(rfp);
2563+
kfree(fwd_prog_tasks);
2564+
fwd_prog_tasks = NULL;
2565+
fwd_progress = 0;
25252566
return -ENOMEM;
2526-
spin_lock_init(&rfp->rcu_fwd_lock);
2527-
rfp->rcu_fwd_cb_tail = &rfp->rcu_fwd_cb_head;
2567+
}
2568+
for (i = 0; i < fwd_progress; i++) {
2569+
spin_lock_init(&rfp[i].rcu_fwd_lock);
2570+
rfp[i].rcu_fwd_cb_tail = &rfp[i].rcu_fwd_cb_head;
2571+
rfp[i].rcu_fwd_id = i;
2572+
}
25282573
mutex_lock(&rcu_fwd_mutex);
25292574
rcu_fwds = rfp;
25302575
mutex_unlock(&rcu_fwd_mutex);
25312576
register_oom_notifier(&rcutorture_oom_nb);
2532-
return torture_create_kthread(rcu_torture_fwd_prog, rfp, fwd_prog_task);
2577+
for (i = 0; i < fwd_progress; i++) {
2578+
ret = torture_create_kthread(rcu_torture_fwd_prog, &rcu_fwds[i], fwd_prog_tasks[i]);
2579+
if (ret) {
2580+
fwd_progress = i;
2581+
return ret;
2582+
}
2583+
}
2584+
return 0;
25332585
}
25342586

25352587
static void rcu_torture_fwd_prog_cleanup(void)
25362588
{
2589+
int i;
25372590
struct rcu_fwd *rfp;
25382591

2539-
torture_stop_kthread(rcu_torture_fwd_prog, fwd_prog_task);
2540-
rfp = rcu_fwds;
2592+
if (!rcu_fwds || !fwd_prog_tasks)
2593+
return;
2594+
for (i = 0; i < fwd_progress; i++)
2595+
torture_stop_kthread(rcu_torture_fwd_prog, fwd_prog_tasks[i]);
2596+
unregister_oom_notifier(&rcutorture_oom_nb);
25412597
mutex_lock(&rcu_fwd_mutex);
2598+
rfp = rcu_fwds;
25422599
rcu_fwds = NULL;
25432600
mutex_unlock(&rcu_fwd_mutex);
2544-
unregister_oom_notifier(&rcutorture_oom_nb);
25452601
kfree(rfp);
2602+
kfree(fwd_prog_tasks);
2603+
fwd_prog_tasks = NULL;
25462604
}
25472605

25482606
/* Callback function for RCU barrier testing. */

0 commit comments

Comments
 (0)