Skip to content

Commit 4788c86

Browse files
Sebastian Andrzej Siewiorpaulmckrcu
authored andcommitted
scftorture: Use a lock-less list to free memory.
scf_handler() is used as a SMP function call. This function is always invoked in IRQ-context even with forced-threading enabled. This function frees memory which not allowed on PREEMPT_RT because the locking underneath is using sleeping locks. Add a per-CPU scf_free_pool where each SMP functions adds its memory to be freed. This memory is then freed by scftorture_invoker() on each iteration. On the majority of invocations the number of items is less than five. If the thread sleeps/ gets delayed the number exceed 350 but did not reach 400 in testing. These were the spikes during testing. The bulk free of 64 pointers at once should improve the give-back if the list grows. The list size is ~1.3 items per invocations. Having one global scf_free_pool with one cleaning thread let the list grow to over 10.000 items with 32 CPUs (again, spikes not the average) especially if the CPU went to sleep. The per-CPU part looks like a good compromise. Reported-by: "Paul E. McKenney" <paulmck@kernel.org> Closes: https://lore.kernel.org/lkml/41619255-cdc2-4573-a360-7794fc3614f7@paulmck-laptop/ Tested-by: Paul E. McKenney <paulmck@kernel.org> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Reviewed-by: Boqun Feng <boqun.feng@gmail.com> Tested-by: Boqun Feng <boqun.feng@gmail.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
1 parent 64bdaf9 commit 4788c86

File tree

1 file changed

+36
-4
lines changed

1 file changed

+36
-4
lines changed

kernel/scftorture.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ struct scf_statistics {
9797
static struct scf_statistics *scf_stats_p;
9898
static struct task_struct *scf_torture_stats_task;
9999
static DEFINE_PER_CPU(long long, scf_invoked_count);
100+
static DEFINE_PER_CPU(struct llist_head, scf_free_pool);
100101

101102
// Data for random primitive selection
102103
#define SCF_PRIM_RESCHED 0
@@ -133,6 +134,7 @@ struct scf_check {
133134
bool scfc_wait;
134135
bool scfc_rpc;
135136
struct completion scfc_completion;
137+
struct llist_node scf_node;
136138
};
137139

138140
// Use to wait for all threads to start.
@@ -148,6 +150,31 @@ static DEFINE_TORTURE_RANDOM_PERCPU(scf_torture_rand);
148150

149151
extern void resched_cpu(int cpu); // An alternative IPI vector.
150152

153+
static void scf_add_to_free_list(struct scf_check *scfcp)
154+
{
155+
struct llist_head *pool;
156+
unsigned int cpu;
157+
158+
cpu = raw_smp_processor_id() % nthreads;
159+
pool = &per_cpu(scf_free_pool, cpu);
160+
llist_add(&scfcp->scf_node, pool);
161+
}
162+
163+
static void scf_cleanup_free_list(unsigned int cpu)
164+
{
165+
struct llist_head *pool;
166+
struct llist_node *node;
167+
struct scf_check *scfcp;
168+
169+
pool = &per_cpu(scf_free_pool, cpu);
170+
node = llist_del_all(pool);
171+
while (node) {
172+
scfcp = llist_entry(node, struct scf_check, scf_node);
173+
node = node->next;
174+
kfree(scfcp);
175+
}
176+
}
177+
151178
// Print torture statistics. Caller must ensure serialization.
152179
static void scf_torture_stats_print(void)
153180
{
@@ -296,7 +323,7 @@ static void scf_handler(void *scfc_in)
296323
if (scfcp->scfc_rpc)
297324
complete(&scfcp->scfc_completion);
298325
} else {
299-
kfree(scfcp);
326+
scf_add_to_free_list(scfcp);
300327
}
301328
}
302329

@@ -363,7 +390,7 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
363390
scfp->n_single_wait_ofl++;
364391
else
365392
scfp->n_single_ofl++;
366-
kfree(scfcp);
393+
scf_add_to_free_list(scfcp);
367394
scfcp = NULL;
368395
}
369396
break;
@@ -391,7 +418,7 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
391418
preempt_disable();
392419
} else {
393420
scfp->n_single_rpc_ofl++;
394-
kfree(scfcp);
421+
scf_add_to_free_list(scfcp);
395422
scfcp = NULL;
396423
}
397424
break;
@@ -428,7 +455,7 @@ static void scftorture_invoke_one(struct scf_statistics *scfp, struct torture_ra
428455
pr_warn("%s: Memory-ordering failure, scfs_prim: %d.\n", __func__, scfsp->scfs_prim);
429456
atomic_inc(&n_mb_out_errs); // Leak rather than trash!
430457
} else {
431-
kfree(scfcp);
458+
scf_add_to_free_list(scfcp);
432459
}
433460
barrier(); // Prevent race-reduction compiler optimizations.
434461
}
@@ -479,6 +506,8 @@ static int scftorture_invoker(void *arg)
479506
VERBOSE_SCFTORTOUT("scftorture_invoker %d started", scfp->cpu);
480507

481508
do {
509+
scf_cleanup_free_list(cpu);
510+
482511
scftorture_invoke_one(scfp, &rand);
483512
while (cpu_is_offline(cpu) && !torture_must_stop()) {
484513
schedule_timeout_interruptible(HZ / 5);
@@ -529,6 +558,9 @@ static void scf_torture_cleanup(void)
529558
kfree(scf_stats_p); // -After- the last stats print has completed!
530559
scf_stats_p = NULL;
531560

561+
for (i = 0; i < nr_cpu_ids; i++)
562+
scf_cleanup_free_list(i);
563+
532564
if (atomic_read(&n_errs) || atomic_read(&n_mb_in_errs) || atomic_read(&n_mb_out_errs))
533565
scftorture_print_module_parms("End of test: FAILURE");
534566
else if (torture_onoff_failures())

0 commit comments

Comments
 (0)