[PATCH v4 12/17] perf thread: Expand context analysis annotations to threads and namespaces_lock

From: Ian Rogers

Date: Fri Oct 16 2026 - 18:20:48 EST


Annotate shard and last_match in struct threads_table_entry with
__guarded_by(&lock) and add __must_hold_shared()/__must_hold() to the
last_match helpers in util/threads.c.

In util/thread.h and util/thread.c, add __returns_ctx_lock() to
thread__namespaces_lock() and thread__comm_lock(), annotate
namespaces_list and comm_list with __guarded_by(), and add
__must_hold_shared()/__must_hold() to the namespaces_list helpers.

Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/thread.c | 2 ++
tools/perf/util/thread.h | 28 +++++++++++++++++-----------
tools/perf/util/threads.c | 4 ++++
tools/perf/util/threads.h | 6 +++---
4 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 616d5bf87baf..af761b1fc5d5 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -149,6 +149,7 @@ void thread__put(struct thread *thread)
}

static struct namespaces *__thread__namespaces(struct thread *thread)
+ __must_hold_shared(thread__namespaces_lock(thread))
{
if (list_empty(thread__namespaces_list(thread)))
return NULL;
@@ -169,6 +170,7 @@ struct namespaces *thread__namespaces(struct thread *thread)

static int __thread__set_namespaces(struct thread *thread, u64 timestamp,
struct perf_record_namespaces *event)
+ __must_hold(thread__namespaces_lock(thread))
{
struct namespaces *new, *curr = __thread__namespaces(thread);

diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 9d0ce7742c06..a7850eebce4e 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -48,10 +48,10 @@ DECLARE_RC_STRUCT(thread) {
bool exited;
bool comm_set;
int comm_len;
- struct list_head namespaces_list;
struct rw_semaphore namespaces_lock;
- struct list_head comm_list;
+ struct list_head namespaces_list __guarded_by(&namespaces_lock);
struct rw_semaphore comm_lock;
+ struct list_head comm_list __guarded_by(&comm_lock);
u64 db_id;

void *priv;
@@ -230,30 +230,36 @@ static inline void thread__set_comm_len(struct thread *thread, int len)
RC_CHK_ACCESS(thread)->comm_len = len;
}

-static inline struct list_head *thread__namespaces_list(struct thread *thread)
+static inline struct rw_semaphore *thread__namespaces_lock(const struct thread *thread)
+ __returns_ctx_lock(&RC_CHK_ACCESS(thread)->namespaces_lock)
{
- return &RC_CHK_ACCESS(thread)->namespaces_list;
+ return (struct rw_semaphore *)&RC_CHK_ACCESS(thread)->namespaces_lock;
}

-static inline int thread__namespaces_list_empty(const struct thread *thread)
+static inline struct list_head *thread__namespaces_list(struct thread *thread)
+ __must_hold_shared(thread__namespaces_lock(thread))
{
- return list_empty(&RC_CHK_ACCESS(thread)->namespaces_list);
+ /* Readers hold the shared lock; writers hold the exclusive lock. */
+ return context_unsafe(&RC_CHK_ACCESS(thread)->namespaces_list);
}

-static inline struct rw_semaphore *thread__namespaces_lock(struct thread *thread)
+static inline int thread__namespaces_list_empty(const struct thread *thread)
+ __must_hold_shared(thread__namespaces_lock(thread))
{
- return &RC_CHK_ACCESS(thread)->namespaces_lock;
+ return list_empty(&RC_CHK_ACCESS(thread)->namespaces_list);
}

-static inline struct rw_semaphore *thread__comm_lock(struct thread *thread)
+static inline struct rw_semaphore *thread__comm_lock(const struct thread *thread)
+ __returns_ctx_lock(&RC_CHK_ACCESS(thread)->comm_lock)
{
- return &RC_CHK_ACCESS(thread)->comm_lock;
+ return (struct rw_semaphore *)&RC_CHK_ACCESS(thread)->comm_lock;
}

static inline struct list_head *thread__comm_list(struct thread *thread)
__must_hold_shared(thread__comm_lock(thread))
{
- return &RC_CHK_ACCESS(thread)->comm_list;
+ /* Readers hold the shared lock; writers hold the exclusive lock. */
+ return context_unsafe(&RC_CHK_ACCESS(thread)->comm_list);
}

static inline u64 thread__db_id(const struct thread *thread)
diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c
index 6ca0b178fb6c..39b3c420635a 100644
--- a/tools/perf/util/threads.c
+++ b/tools/perf/util/threads.c
@@ -21,6 +21,7 @@ static bool key_equal(long key1, long key2, void *ctx __maybe_unused)
}

void threads__init(struct threads *threads)
+ __context_unsafe(/* initialization */)
{
for (int i = 0; i < THREADS__TABLE_SIZE; i++) {
struct threads_table_entry *table = &threads->table[i];
@@ -32,6 +33,7 @@ void threads__init(struct threads *threads)
}

void threads__exit(struct threads *threads)
+ __context_unsafe(/* teardown */)
{
threads__remove_all_threads(threads);
for (int i = 0; i < THREADS__TABLE_SIZE; i++) {
@@ -63,6 +65,7 @@ size_t threads__nr(struct threads *threads)
*/
static struct thread *__threads_table_entry__get_last_match(struct threads_table_entry *table,
pid_t tid)
+ __must_hold_shared(&table->lock)
{
struct thread *th, *res = NULL;

@@ -76,6 +79,7 @@ static struct thread *__threads_table_entry__get_last_match(struct threads_table

static void __threads_table_entry__set_last_match(struct threads_table_entry *table,
struct thread *th)
+ __must_hold(&table->lock)
{
thread__put(table->last_match);
table->last_match = thread__get(th);
diff --git a/tools/perf/util/threads.h b/tools/perf/util/threads.h
index da68d2223f18..54fba16657db 100644
--- a/tools/perf/util/threads.h
+++ b/tools/perf/util/threads.h
@@ -11,10 +11,10 @@ struct thread;
#define THREADS__TABLE_SIZE (1 << THREADS__TABLE_BITS)

struct threads_table_entry {
- /* Key is tid, value is struct thread. */
- struct hashmap shard;
struct rw_semaphore lock;
- struct thread *last_match;
+ /* Key is tid, value is struct thread. */
+ struct hashmap shard __guarded_by(&lock);
+ struct thread *last_match __guarded_by(&lock);
};

struct threads {
--
2.56.0.212.g129ede20d9-goog