[PATCH v4 02/23] perf threads: Avoid table->lock self-deadlock in threads__for_each_thread()
From: Ian Rogers
Date: Fri Nov 13 2026 - 13:45:07 EST
threads__for_each_thread() holds down_read(&table->lock) while invoking
its callback fn(). Callbacks such as perf_session__e_machine_cb() call
thread__e_machine_endian(), which calls machine__findnew_thread(machine,
pid, pid) when pid != tid. Both threads__find() (via
threads_table_entry__set_last_match()) and threads__findnew() acquire
down_write(&table->lock) on pid's shard, triggering a read-to-write
self-deadlock whenever pid and tid hash to the same shard.
Snapshot each shard's threads with thread__get() references (using a
stack buffer with a heap fallback for large shards) and release
table->lock before invoking fn(), matching maps__load_maps().
Fixes: d436f90a64f3 ("perf machine: Move machine's threads into its own abstraction")
Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/threads.c | 35 ++++++++++++++++++++++++++++-------
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c
index 6ca0b178fb6c..5db81b9debb5 100644
--- a/tools/perf/util/threads.c
+++ b/tools/perf/util/threads.c
@@ -1,4 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
+#include <errno.h>
+#include <stdlib.h>
+#include <linux/kernel.h>
#include "threads.h"
#include "machine.h"
#include "thread.h"
@@ -171,20 +174,38 @@ int threads__for_each_thread(struct threads *threads,
{
for (int i = 0; i < THREADS__TABLE_SIZE; i++) {
struct threads_table_entry *table = &threads->table[i];
+ struct thread *stack_threads[32];
+ struct thread **shard_threads = stack_threads;
struct hashmap_entry *cur;
- size_t bkt;
+ size_t bkt, cnt, idx = 0;
+ int rc = 0;
down_read(&table->lock);
- hashmap__for_each_entry(&table->shard, cur, bkt) {
- int rc = fn((struct thread *)cur->pvalue, data);
-
- if (rc != 0) {
+ cnt = hashmap__size(&table->shard);
+ if (cnt == 0) {
+ up_read(&table->lock);
+ continue;
+ }
+ if (cnt > ARRAY_SIZE(stack_threads)) {
+ shard_threads = malloc(cnt * sizeof(*shard_threads));
+ if (!shard_threads) {
up_read(&table->lock);
- return rc;
+ return -ENOMEM;
}
}
+ hashmap__for_each_entry(&table->shard, cur, bkt)
+ shard_threads[idx++] = thread__get((struct thread *)cur->pvalue);
up_read(&table->lock);
+
+ for (size_t j = 0; j < idx; j++) {
+ if (rc == 0)
+ rc = fn(shard_threads[j], data);
+ thread__put(shard_threads[j]);
+ }
+ if (shard_threads != stack_threads)
+ free(shard_threads);
+ if (rc != 0)
+ return rc;
}
return 0;
-
}
--
2.56.1.703.gf848ed807c-goog