diff options
| author | Breno Leitao <leitao@debian.org> | 2026-09-03 13:49:08 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-09-07 17:37:25 +0200 |
| commit | 59ebe7b0ff646071bbf6ef8e8208db9de209bc56 (patch) | |
| tree | 67e50f36cdf4df751726ec11dbd28a048f78628e | |
| parent | 3ee49d4dbd08a919f42016243b1d8bda4122981e (diff) | |
| download | linux-stable-59ebe7b0ff646071bbf6ef8e8208db9de209bc56.tar.gz linux-stable-59ebe7b0ff646071bbf6ef8e8208db9de209bc56.zip | |
mm/kmemleak: report RCU-tasks quiescent states during the scan
[ Upstream commit 3541a2b06ecd78ba333188df04368dcf97273d6a ]
kmemleak_scan() can run for ages on large debug kernels. It was
causing some soft-lockups which I got fixed with commit
3175fcfec8b16baeb ("mm/kmemleak: avoid soft lockup when scanning task
stacks") with our beloved cond_resched().
I've got the fix above deployed in the Meta fleet, and now I am seeing:
INFO: rcu_tasks detected stalls on tasks:
task:kmemleak state:R ... nvcsw: 274/274 holdout: 1 idle_cpu: -1/3
scan_block
scan_gray_list
kmemleak_scan
and, worse, blocks the callers waiting on that grace period. Here a BPF
struct_ops map free, which waits via synchronize_rcu_mult(call_rcu,
call_rcu_tasks), is stuck long enough to also trip the hung task check:
INFO: task kworker/...:bpf_map_free_deferred blocked for 122 seconds
__wait_rcu_gp
bpf_struct_ops_map_free
Then I've learned that cond_resched() is not an RCU-tasks quiescent
state, so, we need to use stronger primitives.
Use cond_resched_tasks_rcu_qs() at the scan reschedule points so the scan
reports an RCU-tasks quiescent state as it proceeds.
Inspired by commit b96285e10aad ("tracing: Have osnoise_main() add a
quiescent state for task rcu").
Link: https://lore.kernel.org/20260720-kmemleak_rcu_task-v1-1-5b460ade777d@debian.org
Fixes: c4b28963fd79 ("mm/kmemleak: rely on rcu for task stack scanning")
Signed-off-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Reviewed-by: SJ Park <sj@kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Breno Leitao <leitao@debian.org>
Cc: Puranjay Mohan <puranjay@kernel.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
[ adapted scan_large_block() to the branch’s existing void implementation without interruption propagation. ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | mm/kmemleak.c | 14 |
1 files changed, 7 insertions, 7 deletions
diff --git a/mm/kmemleak.c b/mm/kmemleak.c index bc79e293531b..e3c9dbde014b 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1569,7 +1569,7 @@ static void scan_large_block(void *start, void *end) next = min(start + MAX_SCAN_SIZE, end); scan_block(start, next, NULL); start = next; - cond_resched(); + cond_resched_tasks_rcu_qs(); } } #endif @@ -1604,7 +1604,7 @@ static void scan_object(struct kmemleak_object *object) scan_block(start, end, object); raw_spin_unlock_irqrestore(&object->lock, flags); - cond_resched(); + cond_resched_tasks_rcu_qs(); raw_spin_lock_irqsave(&object->lock, flags); if (!(object->flags & OBJECT_ALLOCATED)) break; @@ -1626,7 +1626,7 @@ static void scan_object(struct kmemleak_object *object) break; raw_spin_unlock_irqrestore(&object->lock, flags); - cond_resched(); + cond_resched_tasks_rcu_qs(); raw_spin_lock_irqsave(&object->lock, flags); } while (object->flags & OBJECT_ALLOCATED); } else { @@ -1654,7 +1654,7 @@ static void scan_gray_list(void) */ object = list_entry(gray_list.next, typeof(*object), gray_list); while (&object->gray_list != &gray_list) { - cond_resched(); + cond_resched_tasks_rcu_qs(); /* may add new objects to the list */ if (!scan_should_stop()) @@ -1689,7 +1689,7 @@ static void kmemleak_cond_resched(struct kmemleak_object *object) raw_spin_unlock_irq(&kmemleak_lock); rcu_read_unlock(); - cond_resched(); + cond_resched_tasks_rcu_qs(); rcu_read_lock(); raw_spin_lock_irq(&kmemleak_lock); @@ -1734,7 +1734,7 @@ static void kmemleak_scan_task_stacks(void) } put_task_struct(p); } - cond_resched(); + cond_resched_tasks_rcu_qs(); } while (pid && !stop); } @@ -1908,7 +1908,7 @@ static void kmemleak_scan(void) struct page *page = pfn_to_online_page(pfn); if (!(pfn & 63)) - cond_resched(); + cond_resched_tasks_rcu_qs(); if (!page) continue; |
