[PATCH RFC] mm/kmemleak: avoid soft lockup when scanning task stacks
From: Breno Leitao
Date: Thu Jun 11 2026 - 08:49:10 EST
kmemleak_scan() walks every thread and scans its kernel stack under a
single rcu_read_lock() with no reschedule point. On a host with very
many threads -- amplified by KASAN/lockdep in debug builds -- this loop
can hog a CPU long enough to trip the soft lockup watchdog:
watchdog: BUG: soft lockup - CPU#35 stuck for 22s! [kmemleak:537]
scan_block
kmemleak_scan
kmemleak_scan_thread
kthread
A cond_resched() cannot be added directly: the loop runs inside an RCU
read-side critical section.
Split the scan in two parts:
1) get the list of tasks (with RCU read lock) in an array
2) run scan_block() for the tasks (with cond_reschd()).
Is it a sane approach?
Signed-off-by: Breno Leitao <leitao@xxxxxxxxxx>
---
mm/kmemleak.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 7c7ba17ce7af0..9f8a35ecbb50c 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -62,6 +62,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/sched/signal.h>
+#include <linux/sched/stat.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/jiffies.h>
@@ -1885,17 +1886,34 @@ static void kmemleak_scan(void)
* Scanning the task stacks (may introduce false negatives).
*/
if (kmemleak_stack_scan) {
- struct task_struct *p, *g;
+ struct task_struct **tasks, *p, *g;
+ unsigned int nr = 0, max, i;
+ max = nr_threads + 64;
+ tasks = kvmalloc_array(max, sizeof(*tasks), GFP_KERNEL);
+
+ /* Snapshot the threads under RCU */
rcu_read_lock();
for_each_process_thread(g, p) {
- void *stack = try_get_task_stack(p);
+ if (!tasks || nr >= max)
+ break;
+ get_task_struct(p);
+ tasks[nr++] = p;
+ }
+ rcu_read_unlock();
+
+ /* now scan_block for the tasks above with cond_resched() */
+ for (i = 0; i < nr; i++) {
+ void *stack = try_get_task_stack(tasks[i]);
+
if (stack) {
scan_block(stack, stack + THREAD_SIZE, NULL);
- put_task_stack(p);
+ put_task_stack(tasks[i]);
}
+ put_task_struct(tasks[i]);
+ cond_resched();
}
- rcu_read_unlock();
+ kvfree(tasks);
}
/*
---
base-commit: abe651837cb394f76d738a7a747322fca3bf17ba
change-id: 20260611-kmemleak-stack-resched-01ed72858a7f
Best regards,
--
Breno Leitao <leitao@xxxxxxxxxx>