* [PATCH v9 01/19] compiler-context-analysis: Avoid unused parameter warning in _context_unsafe_alias()
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:04 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 02/19] tools headers compiler: Synchronize compiler_types.h with the kernel sources Ian Rogers
` (17 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
_context_unsafe_alias() does not use its parameter 'p', which triggers
-Wunused-parameter when building code that enables -Wextra (such as
tools/perf, tools/lib/perf, tools/lib/subcmd, and tools/lib/symbol).
Explicitly cast 'p' to void to silence the warning, matching
__assume_ctx_lock() and __assume_shared_ctx_lock().
Reviewed-by: Marco Elver <elver@google.com>
Signed-off-by: Ian Rogers <irogers@google.com>
---
include/linux/compiler-context-analysis.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/linux/compiler-context-analysis.h b/include/linux/compiler-context-analysis.h
index 8302ebc2ea8c..024e303188ad 100644
--- a/include/linux/compiler-context-analysis.h
+++ b/include/linux/compiler-context-analysis.h
@@ -243,7 +243,7 @@
* alias as a function argument).
*/
#define context_unsafe_alias(p) _context_unsafe_alias((void **)&(p))
-static inline void _context_unsafe_alias(void **p) { }
+static inline void _context_unsafe_alias(void **p) { (void)p; }
/**
* token_context_lock() - declare an abstract global context lock instance
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 02/19] tools headers compiler: Synchronize compiler_types.h with the kernel sources
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
2026-11-04 20:58 ` [PATCH v9 01/19] compiler-context-analysis: Avoid unused parameter warning in _context_unsafe_alias() Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:08 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 03/19] tools headers: Synchronize linux/compiler-context-analysis.h " Ian Rogers
` (16 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Synchronize the non-__KERNEL__ sections of
tools/include/linux/compiler_types.h and
tools/include/linux/compiler-gcc.h with the kernel sources (including
___PASTE/__PASTE, BTF_TYPE_TAG, object-size helpers, asm_goto_output,
ASM_INPUT_*, asm_inline, __typeof_unqual__/__unqual_scalar_typeof,
__signed_scalar_typeof, __native_word, compiletime_assert, and the
__diag pragma helpers required by compiler-context-analysis.h), and
remove the duplicated definitions from tools/include/linux/compiler.h.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/include/linux/compiler-gcc.h | 72 +++++++++-
tools/include/linux/compiler.h | 50 +------
tools/include/linux/compiler_types.h | 206 ++++++++++++++++++++++++++-
3 files changed, 275 insertions(+), 53 deletions(-)
diff --git a/tools/include/linux/compiler-gcc.h b/tools/include/linux/compiler-gcc.h
index e20f98e14e81..e005e8d5dfda 100644
--- a/tools/include/linux/compiler-gcc.h
+++ b/tools/include/linux/compiler-gcc.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _TOOLS_LINUX_COMPILER_H_
+#ifndef __LINUX_COMPILER_TYPES_H
#error "Please do not include <linux/compiler-gcc.h> directly, include <linux/compiler.h> instead."
-#endif
+#endif /* __LINUX_COMPILER_TYPES_H */
/*
* Common definitions for all gcc versions go here.
@@ -33,10 +33,76 @@
#define __packed __attribute__((packed))
#endif
#ifndef __noreturn
-#define __noreturn __attribute__((noreturn))
+#define __noreturn __attribute__((__noreturn__))
#endif
#ifndef __aligned
#define __aligned(x) __attribute__((aligned(x)))
#endif
#define __printf(a, b) __attribute__((format(printf, a, b)))
#define __scanf(a, b) __attribute__((format(scanf, a, b)))
+
+/*
+ * Turn individual warnings and errors on and off locally, depending
+ * on version.
+ */
+#define __diag_str1(s) #s
+#define __diag_str(s) __diag_str1(s)
+
+#ifdef __clang__
+#define __diag_clang(version, severity, s) \
+ __diag_clang_ ## version(__diag_clang_ ## severity s)
+
+/* Severity used in pragma directives */
+#define __diag_clang_ignore ignored
+#define __diag_clang_warn warning
+#define __diag_clang_error error
+
+#define __diag(s) _Pragma(__diag_str(clang diagnostic s))
+
+#if __clang_major__ >= 23
+#define __diag_clang_23(s) __diag(s)
+#else
+#define __diag_clang_23(s)
+#endif
+
+#define __diag_clang_all(s) __diag(s)
+
+#define __diag_ignore_all(option, comment) \
+ __diag_clang(all, ignore, option)
+
+/*
+ * clang has horrible behavior with "g" or "rm" constraints for asm
+ * inputs, turning them into something worse than "m". Avoid using
+ * constraints with multiple possible uses (but "ir" seems to be ok):
+ *
+ * https://github.com/llvm/llvm-project/issues/20571
+ */
+#define ASM_INPUT_G "ir"
+#define ASM_INPUT_RM "r"
+#define ASM_OUTPUT_RM "=r"
+
+#define CC_HAS_TYPEOF_UNQUAL (__clang_major__ > 19 || (__clang_major__ == 19 && __clang_minor__ > 0))
+#else /* !__clang__ */
+#define __diag_GCC(version, severity, s) \
+ __diag_GCC_ ## version(__diag_GCC_ ## severity s)
+
+/* Severity used in pragma directives */
+#define __diag_GCC_ignore ignored
+#define __diag_GCC_warn warning
+#define __diag_GCC_error error
+
+#define __diag(s) _Pragma(__diag_str(GCC diagnostic s))
+
+#if GCC_VERSION >= 80000
+#define __diag_GCC_8(s) __diag(s)
+#else
+#define __diag_GCC_8(s)
+#endif
+
+#define __diag_GCC_all(s) __diag(s)
+
+#define __diag_ignore_all(option, comment) \
+ __diag(__diag_GCC_ignore option)
+
+#define CC_HAS_TYPEOF_UNQUAL (__GNUC__ >= 14)
+#endif /* __clang__ */
diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h
index 03ecf9086643..505fbb0080d0 100644
--- a/tools/include/linux/compiler.h
+++ b/tools/include/linux/compiler.h
@@ -1,41 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _TOOLS_LINUX_COMPILER_H_
-#define _TOOLS_LINUX_COMPILER_H_
+#ifndef __LINUX_COMPILER_H
+#define __LINUX_COMPILER_H
#ifndef __ASSEMBLY__
#include <linux/compiler_types.h>
-#ifndef __compiletime_error
-# define __compiletime_error(message)
-#endif
-
-#ifdef __OPTIMIZE__
-# define __compiletime_assert(condition, msg, prefix, suffix) \
- do { \
- extern void prefix ## suffix(void) __compiletime_error(msg); \
- if (!(condition)) \
- prefix ## suffix(); \
- } while (0)
-#else
-# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
-#endif
-
-#define _compiletime_assert(condition, msg, prefix, suffix) \
- __compiletime_assert(condition, msg, prefix, suffix)
-
-/**
- * compiletime_assert - break build and emit msg if condition is false
- * @condition: a compile-time constant condition to check
- * @msg: a message to emit if condition is false
- *
- * In tradition of POSIX assert, this macro will break the build if the
- * supplied condition is *false*, emitting the supplied error message if the
- * compiler has support to do so.
- */
-#define compiletime_assert(condition, msg) \
- _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
-
/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")
@@ -48,10 +18,6 @@
#define __always_unused __attribute__((__unused__))
#endif
-#ifndef __noreturn
-#define __noreturn __attribute__((__noreturn__))
-#endif
-
#ifndef unreachable
#define unreachable() __builtin_unreachable()
#endif
@@ -68,11 +34,6 @@
#define __naked __attribute__((__naked__))
#endif
-/* Are two types/vars the same type (ignoring qualifiers)? */
-#ifndef __same_type
-# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
-#endif
-
/* Enforce static storage duration. */
#define ASSERT_STATIC_STORAGE(name) \
static typeof(name) * const __always_unused \
@@ -242,11 +203,6 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
__u.__val; \
})
-
-/* Indirect macros required for expanded argument pasting, eg. __LINE__. */
-#define ___PASTE(a, b) a##b
-#define __PASTE(a, b) ___PASTE(a, b)
-
#ifndef OPTIMIZER_HIDE_VAR
/* Make the optimizer believe the variable can be manipulated arbitrarily. */
#define OPTIMIZER_HIDE_VAR(var) \
@@ -263,4 +219,4 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
#endif /* __ASSEMBLY__ */
-#endif /* _TOOLS_LINUX_COMPILER_H */
+#endif /* __LINUX_COMPILER_H */
diff --git a/tools/include/linux/compiler_types.h b/tools/include/linux/compiler_types.h
index b3adbf5682f0..31e9f7d2a466 100644
--- a/tools/include/linux/compiler_types.h
+++ b/tools/include/linux/compiler_types.h
@@ -2,8 +2,6 @@
#ifndef __LINUX_COMPILER_TYPES_H
#define __LINUX_COMPILER_TYPES_H
-/* Builtins */
-
/*
* __has_builtin is supported on gcc >= 10, clang >= 3 and icc >= 21.
* In the meantime, to support gcc < 10, we implement __has_builtin
@@ -13,6 +11,23 @@
#define __has_builtin(x) (0)
#endif
+/* Indirect macros required for expanded argument pasting, eg. __LINE__. */
+#define ___PASTE(a, b) a##b
+#define __PASTE(a, b) ___PASTE(a, b)
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Skipped when running bindgen due to a libclang issue;
+ * see https://github.com/rust-lang/rust-bindgen/issues/2244.
+ */
+#if defined(CONFIG_DEBUG_INFO_BTF) && defined(CONFIG_PAHOLE_HAS_BTF_TAG) && \
+ __has_attribute(btf_type_tag) && !defined(__BINDGEN__)
+# define BTF_TYPE_TAG(value) __attribute__((btf_type_tag(#value)))
+#else
+# define BTF_TYPE_TAG(value) /* nothing */
+#endif
+
#include <linux/compiler-context-analysis.h>
/* Compiler specific macros. */
@@ -20,14 +35,101 @@
#include <linux/compiler-gcc.h>
#endif
+#ifndef __compiletime_error
+# define __compiletime_error(message)
+#endif
+
+#ifndef __noreturn
+# define __noreturn __attribute__((__noreturn__))
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * When the size of an allocated object is needed, use the best available
+ * mechanism to find it. (For cases where sizeof() cannot be used.)
+ *
+ * Optional: only supported since gcc >= 12
+ *
+ * gcc: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
+ * clang: https://clang.llvm.org/docs/LanguageExtensions.html#evaluating-object-size
+ */
+#if __has_builtin(__builtin_dynamic_object_size)
+#define __struct_size(p) __builtin_dynamic_object_size(p, 0)
+#define __member_size(p) __builtin_dynamic_object_size(p, 1)
+#else
+#define __struct_size(p) __builtin_object_size(p, 0)
+#define __member_size(p) __builtin_object_size(p, 1)
+#endif
+
+/*
+ * Determine if an attribute has been applied to a variable.
+ * Using __annotated needs to check for __annotated being available,
+ * or negative tests may fail when annotation cannot be checked. For
+ * example, see the definition of __is_cstr().
+ */
+#if __has_builtin(__builtin_has_attribute)
+#define __annotated(var, attr) __builtin_has_attribute(var, attr)
+#endif
+
+/*
+ * Optional: only supported since gcc >= 15, clang >= 19
+ *
+ * gcc: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fcounted_005fby_005fref
+ * clang: https://clang.llvm.org/docs/LanguageExtensions.html#builtin-counted-by-ref
+ */
+#if __has_builtin(__builtin_counted_by_ref) && \
+ !defined(CONFIG_CC_HAS_BROKEN_COUNTED_BY_REF)
+#define __flex_counter(FAM) __builtin_counted_by_ref(FAM)
+#else
+#define __flex_counter(FAM) ((void *)NULL)
+#endif
+
+/*
+ * Some versions of gcc do not mark 'asm goto' volatile:
+ *
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103979
+ *
+ * We do it here by hand, because it doesn't hurt.
+ */
#ifndef asm_goto_output
-#define asm_goto_output(x...) asm goto(x)
+#define asm_goto_output(x...) asm volatile goto(x)
+#endif
+
+/*
+ * Clang has trouble with constraints with multiple
+ * alternative behaviors ("g" , "rm" and "=rm").
+ */
+#ifndef ASM_INPUT_G
+ #define ASM_INPUT_G "g"
+ #define ASM_INPUT_RM "rm"
+ #define ASM_OUTPUT_RM "=rm"
+#endif
+
+#ifdef CONFIG_CC_HAS_ASM_INLINE
+#define asm_inline asm __inline
+#else
+#define asm_inline asm
+#endif
+
+#ifndef __ASSEMBLY__
+/*
+ * Use __typeof_unqual__() when available.
+ */
+#if (defined(CC_HAS_TYPEOF_UNQUAL) && CC_HAS_TYPEOF_UNQUAL) || defined(__CHECKER__)
+# define USE_TYPEOF_UNQUAL 1
+#endif
+
+/* Are two types/vars the same type (ignoring qualifiers)? */
+#ifndef __same_type
+# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#endif
/*
* __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving
* non-scalar types unchanged.
*/
+#ifndef USE_TYPEOF_UNQUAL
/*
* Prefer C11 _Generic for better compile-times and simpler code. Note: 'char'
* is not type-compatible with 'signed char', and we define a separate case.
@@ -45,5 +147,103 @@
__scalar_type_to_expr_cases(long), \
__scalar_type_to_expr_cases(long long), \
default: (x)))
+#else
+#define __unqual_scalar_typeof(x) __typeof_unqual__(x)
+#endif
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * __signed_scalar_typeof(x) - Declare a signed scalar type, leaving
+ * non-scalar types unchanged.
+ */
+
+#define __scalar_type_to_signed_cases(type) \
+ unsigned type: (signed type)0, \
+ signed type: (signed type)0
+
+#define __signed_scalar_typeof(x) typeof( \
+ _Generic((x), \
+ char: (signed char)0, \
+ __scalar_type_to_signed_cases(char), \
+ __scalar_type_to_signed_cases(short), \
+ __scalar_type_to_signed_cases(int), \
+ __scalar_type_to_signed_cases(long), \
+ __scalar_type_to_signed_cases(long long), \
+ default: (x)))
+
+/* Is this type a native word size -- useful for atomic operations */
+#define __native_word(t) \
+ (sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
+ sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long))
+
+#ifdef __OPTIMIZE__
+/*
+ * #ifdef __OPTIMIZE__ is only a good approximation; for instance "make
+ * CFLAGS_foo.o=-Og" defines __OPTIMIZE__, does not elide the conditional code
+ * and can break compilation with wrong error message(s). Combine with
+ * -U__OPTIMIZE__ when needed.
+ */
+# define __compiletime_assert(condition, msg, prefix, suffix) \
+ do { \
+ /* \
+ * __noreturn is needed to give the compiler enough \
+ * information to avoid certain possibly-uninitialized \
+ * warnings (regardless of the build failing). \
+ */ \
+ __noreturn extern void prefix ## suffix(void) \
+ __compiletime_error(msg); \
+ if (!(condition)) \
+ prefix ## suffix(); \
+ } while (0)
+#else
+# define __compiletime_assert(condition, msg, prefix, suffix) ((void)(condition))
+#endif
+
+#define _compiletime_assert(condition, msg, prefix, suffix) \
+ __compiletime_assert(condition, msg, prefix, suffix)
+
+/**
+ * compiletime_assert - break build and emit msg if condition is false
+ * @condition: a compile-time constant condition to check
+ * @msg: a message to emit if condition is false
+ *
+ * In tradition of POSIX assert, this macro will break the build if the
+ * supplied condition is *false*, emitting the supplied error message if the
+ * compiler has support to do so.
+ */
+#define compiletime_assert(condition, msg) \
+ _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
+
+#define compiletime_assert_atomic_type(t) \
+ compiletime_assert(__native_word(t), \
+ "Need native word sized stores/loads for atomicity.")
+
+/* Helpers for emitting diagnostics in pragmas. */
+#ifndef __diag
+#define __diag(string)
+#endif
+
+#ifndef __diag_GCC
+#define __diag_GCC(version, severity, string)
+#endif
+
+#ifndef __diag_clang
+#define __diag_clang(version, severity, string)
+#endif
+
+#define __diag_push() __diag(push)
+#define __diag_pop() __diag(pop)
+
+#define __diag_ignore(compiler, version, option, comment) \
+ __diag_ ## compiler(version, ignore, option)
+#define __diag_warn(compiler, version, option, comment) \
+ __diag_ ## compiler(version, warn, option)
+#define __diag_error(compiler, version, option, comment) \
+ __diag_ ## compiler(version, error, option)
+
+#ifndef __diag_ignore_all
+#define __diag_ignore_all(option, comment)
+#endif
#endif /* __LINUX_COMPILER_TYPES_H */
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [PATCH v9 02/19] tools headers compiler: Synchronize compiler_types.h with the kernel sources
2026-11-04 20:58 ` [PATCH v9 02/19] tools headers compiler: Synchronize compiler_types.h with the kernel sources Ian Rogers
@ 2026-11-04 21:08 ` sashiko-bot
0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-04 21:08 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
> Synchronize the non-__KERNEL__ sections of
> tools/include/linux/compiler_types.h and
> tools/include/linux/compiler-gcc.h with the kernel sources (including
> ___PASTE/__PASTE, BTF_TYPE_TAG, object-size helpers, asm_goto_output,
> ASM_INPUT_*, asm_inline, __typeof_unqual__/__unqual_scalar_typeof,
> __signed_scalar_typeof, __native_word, compiletime_assert, and the
> __diag pragma helpers required by compiler-context-analysis.h), and
> remove the duplicated definitions from tools/include/linux/compiler.h.
>
> Signed-off-by: Ian Rogers <irogers@google.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261104205909.2078411-1-irogers@google.com?part=2
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v9 03/19] tools headers: Synchronize linux/compiler-context-analysis.h with the kernel sources
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
2026-11-04 20:58 ` [PATCH v9 01/19] compiler-context-analysis: Avoid unused parameter warning in _context_unsafe_alias() Ian Rogers
2026-11-04 20:58 ` [PATCH v9 02/19] tools headers compiler: Synchronize compiler_types.h with the kernel sources Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:07 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 04/19] perf mutex: Use kernel style compiler-context-analysis.h annotations Ian Rogers
` (15 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Replace the no-op stub tools/include/linux/compiler-context-analysis.h
with the full kernel implementation from
include/linux/compiler-context-analysis.h and add it to
tools/perf/check-headers.sh so future changes are tracked.
Signed-off-by: Ian Rogers <irogers@google.com>
---
.../include/linux/compiler-context-analysis.h | 518 ++++++++++++++++--
| 1 +
2 files changed, 480 insertions(+), 39 deletions(-)
diff --git a/tools/include/linux/compiler-context-analysis.h b/tools/include/linux/compiler-context-analysis.h
index 13a9115e9e58..024e303188ad 100644
--- a/tools/include/linux/compiler-context-analysis.h
+++ b/tools/include/linux/compiler-context-analysis.h
@@ -1,42 +1,482 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _TOOLS_LINUX_COMPILER_CONTEXT_ANALYSIS_H
-#define _TOOLS_LINUX_COMPILER_CONTEXT_ANALYSIS_H
-
/*
* Macros and attributes for compiler-based static context analysis.
- * No-op stubs for tools.
- */
-
-#define __guarded_by(...)
-#define __pt_guarded_by(...)
-
-#define context_lock_struct(name, ...) struct __VA_ARGS__ name
-
-#define __no_context_analysis
-#define __context_unsafe(comment)
-#define context_unsafe(...) ({ __VA_ARGS__; })
-#define context_unsafe_alias(p)
-#define disable_context_analysis()
-#define enable_context_analysis()
-
-#define __must_hold(...)
-#define __must_not_hold(...)
-#define __acquires(...)
-#define __cond_acquires(ret, x)
-#define __releases(...)
-#define __acquire(x) (void)0
-#define __release(x) (void)0
-
-#define __must_hold_shared(...)
-#define __acquires_shared(...)
-#define __cond_acquires_shared(ret, x)
-#define __releases_shared(...)
-#define __acquire_shared(x) (void)0
-#define __release_shared(x) (void)0
-
-#define __acquire_ret(call, expr) (call)
-#define __acquire_shared_ret(call, expr) (call)
-#define __acquires_ret
-#define __acquires_shared_ret
-
-#endif /* _TOOLS_LINUX_COMPILER_CONTEXT_ANALYSIS_H */
+ */
+
+#ifndef _LINUX_COMPILER_CONTEXT_ANALYSIS_H
+#define _LINUX_COMPILER_CONTEXT_ANALYSIS_H
+
+#if defined(WARN_CONTEXT_ANALYSIS) && !defined(__CHECKER__) && !defined(__GENKSYMS__)
+
+/*
+ * These attributes define new context lock (Clang: capability) types.
+ * Internal only.
+ */
+# define __ctx_lock_type(name) __attribute__((capability(#name)))
+# define __reentrant_ctx_lock __attribute__((reentrant_capability))
+# define __acquires_ctx_lock(...) __attribute__((acquire_capability(__VA_ARGS__)))
+# define __acquires_shared_ctx_lock(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
+# define __try_acquires_ctx_lock(ret, var) __attribute__((try_acquire_capability(ret, var)))
+# define __try_acquires_shared_ctx_lock(ret, var) __attribute__((try_acquire_shared_capability(ret, var)))
+# define __releases_ctx_lock(...) __attribute__((release_capability(__VA_ARGS__)))
+# define __releases_shared_ctx_lock(...) __attribute__((release_shared_capability(__VA_ARGS__)))
+# define __returns_ctx_lock(var) __attribute__((lock_returned(var)))
+
+/*
+ * The below are used to annotate code being checked. Internal only.
+ */
+# define __excludes_ctx_lock(...) __attribute__((locks_excluded(__VA_ARGS__)))
+# define __requires_ctx_lock(...) __attribute__((requires_capability(__VA_ARGS__)))
+# define __requires_shared_ctx_lock(...) __attribute__((requires_shared_capability(__VA_ARGS__)))
+
+/*
+ * The "assert_capability" attribute is a bit confusingly named. It does not
+ * generate a check. Instead, it tells the analysis to *assume* the capability
+ * is held. This is used for augmenting runtime assertions, that can then help
+ * with patterns beyond the compiler's static reasoning abilities.
+ */
+# define __assumes_ctx_lock(...) __attribute__((assert_capability(__VA_ARGS__)))
+# define __assumes_shared_ctx_lock(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
+
+/**
+ * __guarded_by() - struct member and globals attribute, declares variable
+ * only accessible within active context
+ * @...: context lock instance pointer(s)
+ *
+ * Declares that the struct member or global variable is only accessible within
+ * the context entered by the given context lock(s). Read operations on the data
+ * require shared access to at least one of the context locks, while write
+ * operations require exclusive access to all listed context locks.
+ *
+ * .. code-block:: c
+ *
+ * struct some_state {
+ * spinlock_t lock;
+ * long counter __guarded_by(&lock);
+ * };
+ *
+ * struct some_state {
+ * spinlock_t lock1, lock2;
+ * long counter __guarded_by(&lock1, &lock2);
+ * };
+ */
+# define __guarded_by(...) __attribute__((guarded_by(__VA_ARGS__)))
+
+/**
+ * __pt_guarded_by() - struct member and globals attribute, declares pointed-to
+ * data only accessible within active context
+ * @...: context lock instance pointer(s)
+ *
+ * Declares that the data pointed to by the struct member pointer or global
+ * pointer is only accessible within the context entered by the given context
+ * lock(s). Read operations on the data require shared access to at least one
+ * of the context locks, while write operations require exclusive access to all
+ * listed context locks.
+ *
+ * .. code-block:: c
+ *
+ * struct some_state {
+ * spinlock_t lock;
+ * long *counter __pt_guarded_by(&lock);
+ * };
+ *
+ * struct some_state {
+ * spinlock_t lock1, lock2;
+ * long *counter __pt_guarded_by(&lock1, &lock2);
+ * };
+ */
+# define __pt_guarded_by(...) __attribute__((pt_guarded_by(__VA_ARGS__)))
+
+/**
+ * context_lock_struct() - declare or define a context lock struct
+ * @name: struct name
+ *
+ * Helper to declare or define a struct type that is also a context lock.
+ *
+ * .. code-block:: c
+ *
+ * context_lock_struct(my_handle) {
+ * int foo;
+ * long bar;
+ * };
+ *
+ * struct some_state {
+ * ...
+ * };
+ * // ... declared elsewhere ...
+ * context_lock_struct(some_state);
+ *
+ * Note: The implementation defines several helper functions that can acquire
+ * and release the context lock.
+ */
+# define context_lock_struct(name, ...) \
+ struct __ctx_lock_type(name) __VA_ARGS__ name; \
+ static __always_inline void __acquire_ctx_lock(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __acquires_ctx_lock(var) { } \
+ static __always_inline void __acquire_shared_ctx_lock(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __acquires_shared_ctx_lock(var) { } \
+ static __always_inline bool __try_acquire_ctx_lock(const struct name *var, bool ret) \
+ __attribute__((overloadable)) __no_context_analysis __try_acquires_ctx_lock(1, var) \
+ { return ret; } \
+ static __always_inline bool __try_acquire_shared_ctx_lock(const struct name *var, bool ret) \
+ __attribute__((overloadable)) __no_context_analysis __try_acquires_shared_ctx_lock(1, var) \
+ { return ret; } \
+ static __always_inline void __release_ctx_lock(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __releases_ctx_lock(var) { } \
+ static __always_inline void __release_shared_ctx_lock(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __releases_shared_ctx_lock(var) { } \
+ static __always_inline void __assume_ctx_lock(const struct name *var) \
+ __attribute__((overloadable)) __assumes_ctx_lock(var) { } \
+ static __always_inline void __assume_shared_ctx_lock(const struct name *var) \
+ __attribute__((overloadable)) __assumes_shared_ctx_lock(var) { } \
+ struct name
+
+/**
+ * disable_context_analysis() - disables context analysis
+ *
+ * Disables context analysis. Must be paired with a later
+ * enable_context_analysis().
+ */
+# define disable_context_analysis() \
+ __diag_push(); \
+ __diag_ignore_all("-Wunknown-warning-option", "") \
+ __diag_ignore_all("-Wthread-safety", "") \
+ __diag_ignore_all("-Wthread-safety-pointer", "")
+
+/**
+ * enable_context_analysis() - re-enables context analysis
+ *
+ * Re-enables context analysis. Must be paired with a prior
+ * disable_context_analysis().
+ */
+# define enable_context_analysis() __diag_pop()
+
+/**
+ * __no_context_analysis - function attribute, disables context analysis
+ *
+ * Function attribute denoting that context analysis is disabled for the
+ * whole function. Prefer use of `context_unsafe()` where possible.
+ */
+# define __no_context_analysis __attribute__((no_thread_safety_analysis))
+
+#else /* !WARN_CONTEXT_ANALYSIS */
+
+# define __ctx_lock_type(name)
+# define __reentrant_ctx_lock
+# define __acquires_ctx_lock(...)
+# define __acquires_shared_ctx_lock(...)
+# define __try_acquires_ctx_lock(ret, var)
+# define __try_acquires_shared_ctx_lock(ret, var)
+# define __releases_ctx_lock(...)
+# define __releases_shared_ctx_lock(...)
+# define __assumes_ctx_lock(...)
+# define __assumes_shared_ctx_lock(...)
+# define __returns_ctx_lock(var)
+# define __guarded_by(...)
+# define __pt_guarded_by(...)
+# define __excludes_ctx_lock(...)
+# define __requires_ctx_lock(...)
+# define __requires_shared_ctx_lock(...)
+# define __acquire_ctx_lock(var) do { } while (0)
+# define __acquire_shared_ctx_lock(var) do { } while (0)
+# define __try_acquire_ctx_lock(var, ret) (ret)
+# define __try_acquire_shared_ctx_lock(var, ret) (ret)
+# define __release_ctx_lock(var) do { } while (0)
+# define __release_shared_ctx_lock(var) do { } while (0)
+# define __assume_ctx_lock(var) do { (void)(var); } while (0)
+# define __assume_shared_ctx_lock(var) do { (void)(var); } while (0)
+# define context_lock_struct(name, ...) struct __VA_ARGS__ name
+# define disable_context_analysis()
+# define enable_context_analysis()
+# define __no_context_analysis
+
+#endif /* WARN_CONTEXT_ANALYSIS */
+
+/**
+ * context_unsafe() - disable context checking for contained code
+ *
+ * Disables context checking for contained statements or expression.
+ *
+ * .. code-block:: c
+ *
+ * struct some_data {
+ * spinlock_t lock;
+ * int counter __guarded_by(&lock);
+ * };
+ *
+ * int foo(struct some_data *d)
+ * {
+ * // ...
+ * // other code that is still checked ...
+ * // ...
+ * return context_unsafe(d->counter);
+ * }
+ */
+#define context_unsafe(...) \
+({ \
+ disable_context_analysis(); \
+ __VA_ARGS__; \
+ enable_context_analysis() \
+})
+
+/**
+ * __context_unsafe() - function attribute, disable context checking
+ * @comment: comment explaining why opt-out is safe
+ *
+ * Function attribute denoting that context analysis is disabled for the
+ * whole function. Forces adding an inline comment as argument.
+ */
+#define __context_unsafe(comment) __no_context_analysis
+
+/**
+ * context_unsafe_alias() - helper to insert a context lock "alias barrier"
+ * @p: pointer aliasing a context lock or object containing context locks
+ *
+ * No-op function that acts as a "context lock alias barrier", where the
+ * analysis rightfully detects that we're switching aliases, but the switch is
+ * considered safe but beyond the analysis reasoning abilities.
+ *
+ * This should be inserted before the first use of such an alias.
+ *
+ * Implementation Note: The compiler ignores aliases that may be reassigned but
+ * their value cannot be determined (e.g. when passing a non-const pointer to an
+ * alias as a function argument).
+ */
+#define context_unsafe_alias(p) _context_unsafe_alias((void **)&(p))
+static inline void _context_unsafe_alias(void **p) { (void)p; }
+
+/**
+ * token_context_lock() - declare an abstract global context lock instance
+ * @name: token context lock name
+ *
+ * Helper that declares an abstract global context lock instance @name, but not
+ * backed by a real data structure (linker error if accidentally referenced).
+ * The type name is `__ctx_lock_@name`.
+ */
+#define token_context_lock(name, ...) \
+ context_lock_struct(__ctx_lock_##name, ##__VA_ARGS__) {}; \
+ extern const struct __ctx_lock_##name *name
+
+/**
+ * token_context_lock_instance() - declare another instance of a global context lock
+ * @ctx: token context lock previously declared with token_context_lock()
+ * @name: name of additional global context lock instance
+ *
+ * Helper that declares an additional instance @name of the same token context
+ * lock class @ctx. This is helpful where multiple related token contexts are
+ * declared, to allow using the same underlying type (`__ctx_lock_@ctx`) as
+ * function arguments.
+ */
+#define token_context_lock_instance(ctx, name) \
+ extern const struct __ctx_lock_##ctx *name
+
+/*
+ * Common keywords for static context analysis.
+ */
+
+/**
+ * __must_hold() - function attribute, caller must hold exclusive context lock
+ *
+ * Function attribute declaring that the caller must hold the given context
+ * lock instance(s) exclusively.
+ */
+#define __must_hold(...) __requires_ctx_lock(__VA_ARGS__)
+
+/**
+ * __must_not_hold() - function attribute, caller must not hold context lock
+ *
+ * Function attribute declaring that the caller must not hold the given context
+ * lock instance(s).
+ */
+#define __must_not_hold(...) __excludes_ctx_lock(__VA_ARGS__)
+
+/**
+ * __acquires() - function attribute, function acquires context lock exclusively
+ *
+ * Function attribute declaring that the function acquires the given context
+ * lock instance(s) exclusively, but does not release them.
+ */
+#define __acquires(...) __acquires_ctx_lock(__VA_ARGS__)
+
+/*
+ * Clang's analysis does not care precisely about the value, only that it is
+ * either zero or non-zero. So the __cond_acquires() interface might be
+ * misleading if we say that @ret is the value returned if acquired. Instead,
+ * provide symbolic variants which we translate.
+ */
+#define __cond_acquires_impl_true(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(1, x)
+#define __cond_acquires_impl_false(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(0, x)
+#define __cond_acquires_impl_nonzero(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(1, x)
+#define __cond_acquires_impl_0(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(0, x)
+#define __cond_acquires_impl_nonnull(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(1, x)
+#define __cond_acquires_impl_NULL(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(0, x)
+
+/**
+ * __cond_acquires() - function attribute, function conditionally
+ * acquires a context lock exclusively
+ * @ret: abstract value returned by function if context lock acquired
+ * @x: context lock instance pointer
+ *
+ * Function attribute declaring that the function conditionally acquires the
+ * given context lock instance @x exclusively, but does not release it. The
+ * function return value @ret denotes when the context lock is acquired.
+ *
+ * @ret may be one of: true, false, nonzero, 0, nonnull, NULL.
+ */
+#define __cond_acquires(ret, x) __cond_acquires_impl_##ret(x)
+
+/**
+ * __releases() - function attribute, function releases a context lock exclusively
+ *
+ * Function attribute declaring that the function releases the given context
+ * lock instance(s) exclusively. The associated context(s) must be active on
+ * entry.
+ */
+#define __releases(...) __releases_ctx_lock(__VA_ARGS__)
+
+/*
+ * Clang's analysis does not care precisely about the value, only that it is
+ * either zero or non-zero. So the __cond_acquires() interface might be
+ * misleading if we say that @ret is the value returned if acquired. Instead,
+ * provide symbolic variants which we translate.
+ */
+#define __cond_acquires_impl_not_true(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(0, x)
+#define __cond_acquires_impl_not_false(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(1, x)
+#define __cond_acquires_impl_not_nonzero(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(0, x)
+#define __cond_acquires_impl_not_0(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(1, x)
+#define __cond_acquires_impl_not_nonnull(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(0, x)
+#define __cond_acquires_impl_not_NULL(x, ...) __try_acquires##__VA_ARGS__##_ctx_lock(1, x)
+
+/**
+ * __cond_releases() - function attribute, function conditionally
+ * releases a context lock exclusively
+ * @ret: abstract value returned by function if context lock releases
+ * @x: context lock instance pointer
+ *
+ * Function attribute declaring that the function conditionally releases the
+ * given context lock instance @x exclusively. The associated context(s) must
+ * be active on entry. The function return value @ret denotes when the context
+ * lock is released.
+ *
+ * @ret may be one of: true, false, nonzero, 0, nonnull, NULL.
+ *
+ * NOTE: clang does not have a native attribute for this; instead implement
+ * it as an unconditional release and a conditional acquire for the
+ * inverted condition -- which is semantically equivalent.
+ */
+#define __cond_releases(ret, x) __releases(x) __cond_acquires_impl_not_##ret(x)
+
+/**
+ * __acquire() - function to acquire context lock exclusively
+ * @x: context lock instance pointer
+ *
+ * No-op function that acquires the given context lock instance @x exclusively.
+ */
+#define __acquire(x) __acquire_ctx_lock(x)
+
+/**
+ * __release() - function to release context lock exclusively
+ * @x: context lock instance pointer
+ *
+ * No-op function that releases the given context lock instance @x.
+ */
+#define __release(x) __release_ctx_lock(x)
+
+/**
+ * __must_hold_shared() - function attribute, caller must hold shared context lock
+ *
+ * Function attribute declaring that the caller must hold the given context
+ * lock instance(s) with shared access.
+ */
+#define __must_hold_shared(...) __requires_shared_ctx_lock(__VA_ARGS__)
+
+/**
+ * __acquires_shared() - function attribute, function acquires context lock shared
+ *
+ * Function attribute declaring that the function acquires the given
+ * context lock instance(s) with shared access, but does not release them.
+ */
+#define __acquires_shared(...) __acquires_shared_ctx_lock(__VA_ARGS__)
+
+/**
+ * __cond_acquires_shared() - function attribute, function conditionally
+ * acquires a context lock shared
+ * @ret: abstract value returned by function if context lock acquired
+ * @x: context lock instance pointer
+ *
+ * Function attribute declaring that the function conditionally acquires the
+ * given context lock instance @x with shared access, but does not release it.
+ * The function return value @ret denotes when the context lock is acquired.
+ *
+ * @ret may be one of: true, false, nonzero, 0, nonnull, NULL.
+ */
+#define __cond_acquires_shared(ret, x) __cond_acquires_impl_##ret(x, _shared)
+
+/**
+ * __releases_shared() - function attribute, function releases a
+ * context lock shared
+ *
+ * Function attribute declaring that the function releases the given context
+ * lock instance(s) with shared access. The associated context(s) must be
+ * active on entry.
+ */
+#define __releases_shared(...) __releases_shared_ctx_lock(__VA_ARGS__)
+
+/**
+ * __acquire_shared() - function to acquire context lock shared
+ * @x: context lock instance pointer
+ *
+ * No-op function that acquires the given context lock instance @x with shared
+ * access.
+ */
+#define __acquire_shared(x) __acquire_shared_ctx_lock(x)
+
+/**
+ * __release_shared() - function to release context lock shared
+ * @x: context lock instance pointer
+ *
+ * No-op function that releases the given context lock instance @x with shared
+ * access.
+ */
+#define __release_shared(x) __release_shared_ctx_lock(x)
+
+/**
+ * __acquire_ret() - helper to acquire context lock of return value
+ * @call: call expression
+ * @ret_expr: acquire expression that uses __ret
+ */
+#define __acquire_ret(call, ret_expr) \
+ ({ \
+ __auto_type __ret = call; \
+ __acquire(ret_expr); \
+ __ret; \
+ })
+
+/**
+ * __acquire_shared_ret() - helper to acquire context lock shared of return value
+ * @call: call expression
+ * @ret_expr: acquire shared expression that uses __ret
+ */
+#define __acquire_shared_ret(call, ret_expr) \
+ ({ \
+ __auto_type __ret = call; \
+ __acquire_shared(ret_expr); \
+ __ret; \
+ })
+
+/*
+ * Attributes to mark functions returning acquired context locks.
+ *
+ * This is purely cosmetic to help readability, and should be used with the
+ * above macros as follows:
+ *
+ * struct foo { spinlock_t lock; ... };
+ * ...
+ * #define myfunc(...) __acquire_ret(_myfunc(__VA_ARGS__), &__ret->lock)
+ * struct foo *_myfunc(int bar) __acquires_ret;
+ * ...
+ */
+#define __acquires_ret __no_context_analysis
+#define __acquires_shared_ret __no_context_analysis
+
+#endif /* _LINUX_COMPILER_CONTEXT_ANALYSIS_H */
--git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
index 531c0e0e84df..c6b2ecadf9b1 100755
--- a/tools/perf/check-headers.sh
+++ b/tools/perf/check-headers.sh
@@ -23,6 +23,7 @@ declare -a FILES=(
"include/linux/bits.h"
"include/vdso/bits.h"
"include/linux/cfi_types.h"
+ "include/linux/compiler-context-analysis.h"
"include/linux/const.h"
"include/vdso/const.h"
"include/vdso/unaligned.h"
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 04/19] perf mutex: Use kernel style compiler-context-analysis.h annotations
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (2 preceding siblings ...)
2026-11-04 20:58 ` [PATCH v9 03/19] tools headers: Synchronize linux/compiler-context-analysis.h " Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:07 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 05/19] perf build: Enable additional Clang thread-safety flags Ian Rogers
` (14 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Enable -DWARN_CONTEXT_ANALYSIS alongside -Wthread-safety for Clang
builds in tools/perf/Makefile.config, replace the custom lock safety
macros in tools/perf with the kernel's compiler-context-analysis.h
annotations (context_lock_struct, __guarded_by, __acquires,
__acquires_shared, __releases, __releases_shared, __cond_acquires,
__must_hold, __must_hold_shared, __must_not_hold, __returns_ctx_lock,
__no_context_analysis, and __context_unsafe), and remove the redundant
macro definitions from tools/perf/util/mutex.h.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Makefile.config | 2 +-
tools/perf/builtin-sched.c | 16 +++----
tools/perf/builtin-top.c | 4 +-
tools/perf/util/annotate.c | 4 +-
tools/perf/util/annotate.h | 8 ++--
tools/perf/util/comm.c | 4 +-
tools/perf/util/dso.c | 30 ++++++------
tools/perf/util/dso.h | 11 +++--
tools/perf/util/dsos.c | 6 +--
tools/perf/util/intel-tpebs.c | 6 +--
tools/perf/util/libbfd.c | 4 +-
tools/perf/util/mutex.c | 4 +-
tools/perf/util/mutex.h | 90 +++--------------------------------
tools/perf/util/rwsem.c | 8 ++--
tools/perf/util/rwsem.h | 10 ++--
tools/perf/util/thread.c | 8 ++--
tools/perf/util/thread.h | 2 +-
17 files changed, 70 insertions(+), 147 deletions(-)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index b4764f69f47f..3eb8d095cda0 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -64,7 +64,7 @@ ifeq ($(CC_NO_CLANG), 0)
endif
# Enabled Wthread-safety analysis for clang builds.
- CFLAGS += -Wthread-safety
+ CFLAGS += -Wthread-safety -DWARN_CONTEXT_ANALYSIS
endif
include $(srctree)/tools/scripts/Makefile.arch
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 39571f03eda2..ee74a6aa5ea3 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -828,8 +828,8 @@ static void *thread_func(void *ctx)
}
static void create_tasks(struct perf_sched *sched)
- EXCLUSIVE_LOCK_FUNCTION(sched->start_work_mutex)
- EXCLUSIVE_LOCK_FUNCTION(sched->work_done_wait_mutex)
+ __acquires(&sched->start_work_mutex)
+ __acquires(&sched->work_done_wait_mutex)
{
struct task_desc *task;
pthread_attr_t attr;
@@ -858,8 +858,8 @@ static void create_tasks(struct perf_sched *sched)
}
static void destroy_tasks(struct perf_sched *sched)
- UNLOCK_FUNCTION(sched->start_work_mutex)
- UNLOCK_FUNCTION(sched->work_done_wait_mutex)
+ __releases(&sched->start_work_mutex)
+ __releases(&sched->work_done_wait_mutex)
{
struct task_desc *task;
unsigned long i;
@@ -878,8 +878,8 @@ static void destroy_tasks(struct perf_sched *sched)
}
static void wait_for_tasks(struct perf_sched *sched)
- EXCLUSIVE_LOCKS_REQUIRED(sched->work_done_wait_mutex)
- EXCLUSIVE_LOCKS_REQUIRED(sched->start_work_mutex)
+ __must_hold(&sched->work_done_wait_mutex)
+ __must_hold(&sched->start_work_mutex)
{
u64 cpu_usage_0, cpu_usage_1;
struct task_desc *task;
@@ -930,8 +930,8 @@ static void wait_for_tasks(struct perf_sched *sched)
}
static void run_one_test(struct perf_sched *sched)
- EXCLUSIVE_LOCKS_REQUIRED(sched->work_done_wait_mutex)
- EXCLUSIVE_LOCKS_REQUIRED(sched->start_work_mutex)
+ __must_hold(&sched->work_done_wait_mutex)
+ __must_hold(&sched->start_work_mutex)
{
u64 T0, T1, delta, avg_delta, fluct;
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index a3442d2d6edf..9049db154fa4 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -201,7 +201,7 @@ static void perf_top__record_precise_ip(struct perf_top *top,
struct hist_entry *he,
struct perf_sample *sample,
u64 ip)
- EXCLUSIVE_LOCKS_REQUIRED(he->hists->lock)
+ __must_hold(&he->hists->lock)
{
struct annotation *notes;
struct symbol *sym = he->ms.sym;
@@ -730,7 +730,7 @@ static void *display_thread(void *arg)
static int hist_iter__top_callback(struct hist_entry_iter *iter,
struct addr_location *al, bool single,
void *arg)
- EXCLUSIVE_LOCKS_REQUIRED(iter->he->hists->lock)
+ __must_hold(&iter->he->hists->lock)
{
struct perf_top *top = arg;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 86a4beec6528..c466757e8508 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -655,7 +655,7 @@ static struct mutex *annotation__get_mutex(const struct annotation *notes)
}
void annotation__lock(struct annotation *notes)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
struct mutex *mutex = annotation__get_mutex(notes);
@@ -664,7 +664,7 @@ void annotation__lock(struct annotation *notes)
}
void annotation__unlock(struct annotation *notes)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
struct mutex *mutex = annotation__get_mutex(notes);
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1b47c1156b9a..f5465c931721 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -369,7 +369,7 @@ struct annotated_branch {
u64 *br_cntr;
};
-struct LOCKABLE annotation {
+context_lock_struct(annotation) {
struct annotated_source *src;
struct annotated_branch *branch;
};
@@ -379,9 +379,9 @@ static inline void annotation__init(struct annotation *notes __maybe_unused)
}
void annotation__exit(struct annotation *notes);
-void annotation__lock(struct annotation *notes) EXCLUSIVE_LOCK_FUNCTION(*notes);
-void annotation__unlock(struct annotation *notes) UNLOCK_FUNCTION(*notes);
-bool annotation__trylock(struct annotation *notes) EXCLUSIVE_TRYLOCK_FUNCTION(true, *notes);
+void annotation__lock(struct annotation *notes) __acquires(notes);
+void annotation__unlock(struct annotation *notes) __releases(notes);
+bool annotation__trylock(struct annotation *notes) __cond_acquires(true, notes);
static inline int annotation__cycles_width(struct annotation *notes)
{
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c
index 9880247a2c33..6343a3c9debe 100644
--- a/tools/perf/util/comm.c
+++ b/tools/perf/util/comm.c
@@ -24,7 +24,7 @@ static struct comm_strs {
static void comm_strs__remove_if_last(struct comm_str *cs);
static void comm_strs__init(void)
- NO_THREAD_SAFETY_ANALYSIS /* Inherently single threaded due to pthread_once. */
+ __context_unsafe(/* Inherently single threaded due to pthread_once. */)
{
init_rwsem(&_comm_strs.lock);
_comm_strs.capacity = 16;
@@ -120,7 +120,7 @@ static void comm_strs__remove_if_last(struct comm_str *cs)
}
static struct comm_str *__comm_strs__find(struct comm_strs *comm_strs, const char *str)
- SHARED_LOCKS_REQUIRED(comm_strs->lock)
+ __must_hold_shared(&comm_strs->lock)
{
struct comm_str **result;
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index ce98613868e7..24ff066b9878 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -525,14 +525,14 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m,
*/
struct mutex _dso__data_open_lock;
static LIST_HEAD(dso__data_open);
-static long dso__data_open_cnt GUARDED_BY(_dso__data_open_lock);
+static long dso__data_open_cnt __guarded_by(&_dso__data_open_lock);
static void dso__data_open_lock_init(void)
{
mutex_init(&_dso__data_open_lock);
}
-static struct mutex *dso__data_open_lock(void) LOCK_RETURNED(_dso__data_open_lock)
+static struct mutex *dso__data_open_lock(void) __returns_ctx_lock(&_dso__data_open_lock)
{
static pthread_once_t data_open_lock_once = PTHREAD_ONCE_INIT;
@@ -541,7 +541,7 @@ static struct mutex *dso__data_open_lock(void) LOCK_RETURNED(_dso__data_open_loc
return &_dso__data_open_lock;
}
-static void dso__list_add(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static void dso__list_add(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
list_add_tail(&dso__data(dso)->open_entry, &dso__data_open);
#ifdef REFCNT_CHECKING
@@ -552,7 +552,7 @@ static void dso__list_add(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_o
dso__data_open_cnt++;
}
-static void dso__list_del(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static void dso__list_del(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
list_del_init(&dso__data(dso)->open_entry);
#ifdef REFCNT_CHECKING
@@ -567,7 +567,7 @@ static void dso__list_del(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_o
static void close_first_dso(void);
-static int do_open(char *name) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static int do_open(char *name) __must_hold(&_dso__data_open_lock)
{
do {
int fd = open(name, O_RDONLY|O_CLOEXEC);
@@ -670,7 +670,7 @@ void dso__put_filename(struct dso *dso __maybe_unused, char *filename, bool deco
}
static int __open_dso(struct dso *dso, struct machine *machine)
- EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+ __must_hold(&_dso__data_open_lock)
{
int fd = -EINVAL;
char *name;
@@ -703,7 +703,7 @@ static void check_data_close(void);
* list/count of open DSO objects.
*/
static int open_dso(struct dso *dso, struct machine *machine)
- EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+ __must_hold(&_dso__data_open_lock)
{
int fd;
struct nscookie nsc;
@@ -729,7 +729,7 @@ static int open_dso(struct dso *dso, struct machine *machine)
return fd;
}
-static void close_data_fd(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static void close_data_fd(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
if (dso__data(dso)->fd >= 0) {
close(dso__data(dso)->fd);
@@ -746,12 +746,12 @@ static void close_data_fd(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_o
* Close @dso's data file descriptor and updates
* list/count of open DSO objects.
*/
-static void close_dso(struct dso *dso) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static void close_dso(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
close_data_fd(dso);
}
-static void close_first_dso(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static void close_first_dso(void) __must_hold(&_dso__data_open_lock)
{
struct dso_data *dso_data;
struct dso *dso;
@@ -796,7 +796,7 @@ void reset_fd_limit(void)
fd_limit = 0;
}
-static bool may_cache_fd(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static bool may_cache_fd(void) __must_hold(&_dso__data_open_lock)
{
if (!fd_limit)
fd_limit = get_fd_limit();
@@ -812,7 +812,7 @@ static bool may_cache_fd(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
* for opened dso file descriptors. The limit is half
* of the RLIMIT_NOFILE files opened.
*/
-static void check_data_close(void) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+static void check_data_close(void) __must_hold(&_dso__data_open_lock)
{
bool cache_fd = may_cache_fd();
@@ -834,7 +834,7 @@ void dso__data_close(struct dso *dso)
}
static int dso__find_binary_type_locked(struct dso *dso)
- EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso))
+ __must_hold(dso__lock(dso))
{
enum dso_binary_type binary_type_data[] = {
DSO_BINARY_TYPE__BUILD_ID_CACHE,
@@ -926,7 +926,7 @@ static int dso__find_binary_type_locked(struct dso *dso)
#define FIND_MAX_RETRY 3
static int __dso__find_binary_type(struct dso *dso)
- EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+ __must_hold(&_dso__data_open_lock)
{
int ret;
int retry = 0;
@@ -966,7 +966,7 @@ enum dso_binary_type dso__find_binary_type(struct dso *dso)
}
static void try_to_open_dso(struct dso *dso, struct machine *machine)
- EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
+ __must_hold(&_dso__data_open_lock)
{
struct dso_data *dso_data = dso__data(dso);
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index d5f7382d4e13..f6c60bafcde1 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -629,6 +629,7 @@ static inline void dso__set_loaded(struct dso *dso)
}
static inline struct mutex *dso__lock(struct dso *dso)
+ __returns_ctx_lock(&RC_CHK_ACCESS(dso)->lock)
{
return &RC_CHK_ACCESS(dso)->lock;
}
@@ -760,7 +761,7 @@ static inline const char *dso__symsrc_filename(const struct dso *dso)
return RC_CHK_ACCESS(dso)->symsrc_filename;
}
-void dso__set_symsrc_filename(struct dso *dso, char *val) EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso));
+void dso__set_symsrc_filename(struct dso *dso, char *val) __must_hold(dso__lock(dso));
static inline void dso__free_symsrc_filename(struct dso *dso)
{
@@ -809,7 +810,7 @@ void __dso__improve_id(struct dso *dso, const struct dso_id *id);
int dso__name_len(const struct dso *dso);
struct dso *dso__get(struct dso *dso);
-void dso__put(struct dso *dso) LOCKS_EXCLUDED(_dso__data_open_lock);
+void dso__put(struct dso *dso) __must_not_hold(&_dso__data_open_lock);
static inline void __dso__zput(struct dso **dso)
{
@@ -919,9 +920,9 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m,
* TODO
*/
bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd)
- EXCLUSIVE_TRYLOCK_FUNCTION(true, _dso__data_open_lock);
-void dso__data_put_fd(struct dso *dso) UNLOCK_FUNCTION(_dso__data_open_lock);
-void dso__data_close(struct dso *dso) LOCKS_EXCLUDED(_dso__data_open_lock);
+ __cond_acquires(true, &_dso__data_open_lock);
+void dso__data_put_fd(struct dso *dso) __releases(&_dso__data_open_lock);
+void dso__data_close(struct dso *dso) __must_not_hold(&_dso__data_open_lock);
int dso__data_file_size(struct dso *dso, struct machine *machine);
off_t dso__data_size(struct dso *dso, struct machine *machine);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index e927e707abac..a2cd37bd1660 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -157,7 +157,7 @@ static struct dso *__dsos__find_by_longname_id(struct dsos *dsos,
const char *name,
const struct dso_id *id,
bool write_locked)
- SHARED_LOCKS_REQUIRED(dsos->lock)
+ __must_hold_shared(&dsos->lock)
{
struct dsos__key key = {
.long_name = name,
@@ -266,7 +266,7 @@ static int dsos__find_id_cb(struct dso *dso, void *data)
static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, const struct dso_id *id,
bool cmp_short, bool write_locked)
- SHARED_LOCKS_REQUIRED(dsos->lock)
+ __must_hold_shared(&dsos->lock)
{
struct dso *res;
@@ -330,7 +330,7 @@ static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const
}
static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)
- SHARED_LOCKS_REQUIRED(dsos->lock)
+ __must_hold_shared(&dsos->lock)
{
struct dso *dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/true);
diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c
index b41171b5df77..7c0b8b15bd10 100644
--- a/tools/perf/util/intel-tpebs.c
+++ b/tools/perf/util/intel-tpebs.c
@@ -69,7 +69,7 @@ static struct mutex *tpebs_mtx_get(void)
}
static struct tpebs_retire_lat *tpebs_retire_lat__find(struct evsel *evsel)
- EXCLUSIVE_LOCKS_REQUIRED(tpebs_mtx_get());
+ __must_hold(tpebs_mtx_get());
static int evsel__tpebs_start_perf_record(struct evsel *evsel)
{
@@ -244,7 +244,7 @@ static void *__sample_reader(void *arg __maybe_unused)
return NULL;
}
-static int tpebs_send_record_cmd(const char *msg) EXCLUSIVE_LOCKS_REQUIRED(tpebs_mtx_get())
+static int tpebs_send_record_cmd(const char *msg) __must_hold(tpebs_mtx_get())
{
struct pollfd pollfd = { .events = POLLIN, };
int ret, len, retries = 0;
@@ -321,7 +321,7 @@ static int tpebs_send_record_cmd(const char *msg) EXCLUSIVE_LOCKS_REQUIRED(tpebs
/*
* tpebs_stop - stop the sample data read thread and the perf record process.
*/
-static int tpebs_stop(void) EXCLUSIVE_LOCKS_REQUIRED(tpebs_mtx_get())
+static int tpebs_stop(void) __must_hold(tpebs_mtx_get())
{
int ret = 0;
diff --git a/tools/perf/util/libbfd.c b/tools/perf/util/libbfd.c
index 58cadc93e8b5..61bd4f49afba 100644
--- a/tools/perf/util/libbfd.c
+++ b/tools/perf/util/libbfd.c
@@ -51,13 +51,13 @@ struct a2l_data {
asymbol **syms;
};
-static bool perf_bfd_lock(void *bfd_mutex) NO_THREAD_SAFETY_ANALYSIS
+static bool perf_bfd_lock(void *bfd_mutex) __no_context_analysis
{
mutex_lock(bfd_mutex);
return true;
}
-static bool perf_bfd_unlock(void *bfd_mutex) NO_THREAD_SAFETY_ANALYSIS
+static bool perf_bfd_unlock(void *bfd_mutex) __no_context_analysis
{
mutex_unlock(bfd_mutex);
return true;
diff --git a/tools/perf/util/mutex.c b/tools/perf/util/mutex.c
index df3b7cce3d5a..f1b99409ea91 100644
--- a/tools/perf/util/mutex.c
+++ b/tools/perf/util/mutex.c
@@ -125,14 +125,14 @@ void mutex_destroy(struct mutex *mtx)
}
void mutex_lock(struct mutex *mtx)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
CHECK_ERR(mutex_take_over(mtx, pthread_mutex_lock(&mtx->lock),
/*give_up=*/false));
}
void mutex_unlock(struct mutex *mtx)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
CHECK_ERR(pthread_mutex_unlock(&mtx->lock));
}
diff --git a/tools/perf/util/mutex.h b/tools/perf/util/mutex.h
index 5de1065ea2b9..fc01a038aefe 100644
--- a/tools/perf/util/mutex.h
+++ b/tools/perf/util/mutex.h
@@ -4,91 +4,13 @@
#include <pthread.h>
#include <stdbool.h>
-
-/*
- * A function-like feature checking macro that is a wrapper around
- * `__has_attribute`, which is defined by GCC 5+ and Clang and evaluates to a
- * nonzero constant integer if the attribute is supported or 0 if not.
- */
-#ifdef __has_attribute
-#define HAVE_ATTRIBUTE(x) __has_attribute(x)
-#else
-#define HAVE_ATTRIBUTE(x) 0
-#endif
-
-#if HAVE_ATTRIBUTE(guarded_by) && HAVE_ATTRIBUTE(pt_guarded_by) && \
- HAVE_ATTRIBUTE(lockable) && HAVE_ATTRIBUTE(exclusive_lock_function) && \
- HAVE_ATTRIBUTE(exclusive_trylock_function) && HAVE_ATTRIBUTE(exclusive_locks_required) && \
- HAVE_ATTRIBUTE(no_thread_safety_analysis)
-
-/* Documents if a shared field or global variable needs to be protected by a mutex. */
-#define GUARDED_BY(x) __attribute__((guarded_by(x)))
-
-/*
- * Documents if the memory location pointed to by a pointer should be guarded by
- * a mutex when dereferencing the pointer.
- */
-#define PT_GUARDED_BY(x) __attribute__((pt_guarded_by(x)))
-
-/* Documents if a type is a lockable type. */
-#define LOCKABLE __attribute__((lockable))
-
-/* Documents a function that expects a lock not to be held prior to entry. */
-#define LOCKS_EXCLUDED(...) __attribute__((locks_excluded(__VA_ARGS__)))
-
-/* Documents a function that returns a lock. */
-#define LOCK_RETURNED(x) __attribute__((lock_returned(x)))
-
-/* Documents functions that acquire a lock in the body of a function, and do not release it. */
-#define EXCLUSIVE_LOCK_FUNCTION(...) __attribute__((exclusive_lock_function(__VA_ARGS__)))
-
-/*
- * Documents functions that acquire a shared (reader) lock in the body of a
- * function, and do not release it.
- */
-#define SHARED_LOCK_FUNCTION(...) __attribute__((shared_lock_function(__VA_ARGS__)))
-
-/*
- * Documents functions that expect a lock to be held on entry to the function,
- * and release it in the body of the function.
- */
-#define UNLOCK_FUNCTION(...) __attribute__((unlock_function(__VA_ARGS__)))
-
-/* Documents functions that try to acquire a lock, and return success or failure. */
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
- __attribute__((exclusive_trylock_function(__VA_ARGS__)))
-
-/* Documents a function that expects a mutex to be held prior to entry. */
-#define EXCLUSIVE_LOCKS_REQUIRED(...) __attribute__((exclusive_locks_required(__VA_ARGS__)))
-
-/* Documents a function that expects a shared (reader) lock to be held prior to entry. */
-#define SHARED_LOCKS_REQUIRED(...) __attribute__((shared_locks_required(__VA_ARGS__)))
-
-/* Turns off thread safety checking within the body of a particular function. */
-#define NO_THREAD_SAFETY_ANALYSIS __attribute__((no_thread_safety_analysis))
-
-#else
-
-#define GUARDED_BY(x)
-#define PT_GUARDED_BY(x)
-#define LOCKABLE
-#define LOCKS_EXCLUDED(...)
-#define LOCK_RETURNED(x)
-#define EXCLUSIVE_LOCK_FUNCTION(...)
-#define SHARED_LOCK_FUNCTION(...)
-#define UNLOCK_FUNCTION(...)
-#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
-#define EXCLUSIVE_LOCKS_REQUIRED(...)
-#define SHARED_LOCKS_REQUIRED(...)
-#define NO_THREAD_SAFETY_ANALYSIS
-
-#endif
+#include <linux/compiler.h>
/*
* A wrapper around the mutex implementation that allows perf to error check
* usage, etc.
*/
-struct LOCKABLE mutex {
+context_lock_struct(mutex) {
pthread_mutex_t lock;
};
@@ -150,10 +72,10 @@ void mutex_init_recursive(struct mutex *mtx);
bool mutex_init_pshared_robust(struct mutex *mtx);
void mutex_destroy(struct mutex *mtx);
-void mutex_lock(struct mutex *mtx) EXCLUSIVE_LOCK_FUNCTION(*mtx);
-void mutex_unlock(struct mutex *mtx) UNLOCK_FUNCTION(*mtx);
+void mutex_lock(struct mutex *mtx) __acquires(mtx);
+void mutex_unlock(struct mutex *mtx) __releases(mtx);
/* Tries to acquire the lock and returns true on success. */
-bool mutex_trylock(struct mutex *mtx) EXCLUSIVE_TRYLOCK_FUNCTION(true, *mtx);
+bool mutex_trylock(struct mutex *mtx) __cond_acquires(true, mtx);
/* Default initialize the cond struct. */
void cond_init(struct cond *cnd);
@@ -164,7 +86,7 @@ void cond_init(struct cond *cnd);
void cond_init_pshared(struct cond *cnd);
void cond_destroy(struct cond *cnd);
-void cond_wait(struct cond *cnd, struct mutex *mtx) EXCLUSIVE_LOCKS_REQUIRED(mtx);
+void cond_wait(struct cond *cnd, struct mutex *mtx) __must_hold(mtx);
void cond_signal(struct cond *cnd);
void cond_broadcast(struct cond *cnd);
diff --git a/tools/perf/util/rwsem.c b/tools/perf/util/rwsem.c
index 9d26832398db..cc720b482333 100644
--- a/tools/perf/util/rwsem.c
+++ b/tools/perf/util/rwsem.c
@@ -27,7 +27,7 @@ int exit_rwsem(struct rw_semaphore *sem)
}
int down_read(struct rw_semaphore *sem)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
#if RWS_ERRORCHECK
mutex_lock(&sem->mtx);
@@ -38,7 +38,7 @@ int down_read(struct rw_semaphore *sem)
}
int up_read(struct rw_semaphore *sem)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
#if RWS_ERRORCHECK
mutex_unlock(&sem->mtx);
@@ -49,7 +49,7 @@ int up_read(struct rw_semaphore *sem)
}
int down_write(struct rw_semaphore *sem)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
#if RWS_ERRORCHECK
mutex_lock(&sem->mtx);
@@ -60,7 +60,7 @@ int down_write(struct rw_semaphore *sem)
}
int up_write(struct rw_semaphore *sem)
- NO_THREAD_SAFETY_ANALYSIS
+ __no_context_analysis
{
#if RWS_ERRORCHECK
mutex_unlock(&sem->mtx);
diff --git a/tools/perf/util/rwsem.h b/tools/perf/util/rwsem.h
index b102d8143181..235bb63c92d0 100644
--- a/tools/perf/util/rwsem.h
+++ b/tools/perf/util/rwsem.h
@@ -10,7 +10,7 @@
*/
#define RWS_ERRORCHECK 0
-struct LOCKABLE rw_semaphore {
+context_lock_struct(rw_semaphore) {
#if RWS_ERRORCHECK
struct mutex mtx;
#else
@@ -21,10 +21,10 @@ struct LOCKABLE rw_semaphore {
int init_rwsem(struct rw_semaphore *sem);
int exit_rwsem(struct rw_semaphore *sem);
-int down_read(struct rw_semaphore *sem) SHARED_LOCK_FUNCTION(sem);
-int up_read(struct rw_semaphore *sem) UNLOCK_FUNCTION(sem);
+int down_read(struct rw_semaphore *sem) __acquires_shared(sem);
+int up_read(struct rw_semaphore *sem) __releases_shared(sem);
-int down_write(struct rw_semaphore *sem) EXCLUSIVE_LOCK_FUNCTION(sem);
-int up_write(struct rw_semaphore *sem) UNLOCK_FUNCTION(sem);
+int down_write(struct rw_semaphore *sem) __acquires(sem);
+int up_write(struct rw_semaphore *sem) __releases(sem);
#endif /* _PERF_RWSEM_H */
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 91118c301913..616d5bf87baf 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -41,7 +41,7 @@ int thread__init_maps(struct thread *thread, struct machine *machine)
}
struct thread *thread__new(pid_t pid, pid_t tid)
- NO_THREAD_SAFETY_ANALYSIS /* Allocation/creation is inherently single threaded. */
+ __context_unsafe(/* Allocation/creation is inherently single threaded. */)
{
RC_STRUCT(thread) *_thread = zalloc(sizeof(*_thread));
struct thread *thread;
@@ -203,7 +203,7 @@ int thread__set_namespaces(struct thread *thread, u64 timestamp,
}
static struct comm *__thread__comm(struct thread *thread)
- SHARED_LOCKS_REQUIRED(thread__comm_lock(thread))
+ __must_hold_shared(thread__comm_lock(thread))
{
if (list_empty(thread__comm_list(thread)))
return NULL;
@@ -250,7 +250,7 @@ struct comm *thread__exec_comm(struct thread *thread)
static int ____thread__set_comm(struct thread *thread, const char *str,
u64 timestamp, bool exec)
- EXCLUSIVE_LOCKS_REQUIRED(thread__comm_lock(thread))
+ __must_hold(thread__comm_lock(thread))
{
struct comm *new, *curr = __thread__comm(thread);
@@ -308,7 +308,7 @@ int thread__set_comm_from_proc(struct thread *thread)
}
static const char *__thread__comm_str(struct thread *thread)
- SHARED_LOCKS_REQUIRED(thread__comm_lock(thread))
+ __must_hold_shared(thread__comm_lock(thread))
{
const struct comm *comm = __thread__comm(thread);
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index d82fce8173ae..9d0ce7742c06 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -251,7 +251,7 @@ static inline struct rw_semaphore *thread__comm_lock(struct thread *thread)
}
static inline struct list_head *thread__comm_list(struct thread *thread)
- SHARED_LOCKS_REQUIRED(thread__comm_lock(thread))
+ __must_hold_shared(thread__comm_lock(thread))
{
return &RC_CHK_ACCESS(thread)->comm_list;
}
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [PATCH v9 04/19] perf mutex: Use kernel style compiler-context-analysis.h annotations
2026-11-04 20:58 ` [PATCH v9 04/19] perf mutex: Use kernel style compiler-context-analysis.h annotations Ian Rogers
@ 2026-11-04 21:07 ` sashiko-bot
0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-04 21:07 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
> Enable -DWARN_CONTEXT_ANALYSIS alongside -Wthread-safety for Clang
> builds in tools/perf/Makefile.config, replace the custom lock safety
> macros in tools/perf with the kernel's compiler-context-analysis.h
> annotations (context_lock_struct, __guarded_by, __acquires,
> __acquires_shared, __releases, __releases_shared, __cond_acquires,
> __must_hold, __must_hold_shared, __must_not_hold, __returns_ctx_lock,
> __no_context_analysis, and __context_unsafe), and remove the redundant
> macro definitions from tools/perf/util/mutex.h.
>
> Signed-off-by: Ian Rogers <irogers@google.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261104205909.2078411-1-irogers@google.com?part=4
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v9 05/19] perf build: Enable additional Clang thread-safety flags
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (3 preceding siblings ...)
2026-11-04 20:58 ` [PATCH v9 04/19] perf mutex: Use kernel style compiler-context-analysis.h annotations Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:15 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 06/19] perf comm: Add __guarded_by annotations to struct comm_strs Ian Rogers
` (13 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Align tools/perf's Clang thread-safety flags with the kernel's
scripts/Makefile.context-analysis by adding -Wthread-safety-pointer,
-Wthread-safety-beta, and (when supported by the compiler)
-fexperimental-late-parse-attributes.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Makefile.config | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 3eb8d095cda0..b50702bac53c 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -64,7 +64,13 @@ ifeq ($(CC_NO_CLANG), 0)
endif
# Enabled Wthread-safety analysis for clang builds.
- CFLAGS += -Wthread-safety -DWARN_CONTEXT_ANALYSIS
+ CFLAGS += -DWARN_CONTEXT_ANALYSIS -Wthread-safety
+ ifeq ($(shell $(CC) -Werror -Wthread-safety-pointer -Wthread-safety-beta -c -x c /dev/null -o /dev/null >/dev/null 2>&1 && echo 1), 1)
+ CFLAGS += -Wthread-safety-pointer -Wthread-safety-beta
+ endif
+ ifeq ($(shell $(CC) -Werror -fexperimental-late-parse-attributes -c -x c /dev/null -o /dev/null >/dev/null 2>&1 && echo 1), 1)
+ CFLAGS += -fexperimental-late-parse-attributes
+ endif
endif
include $(srctree)/tools/scripts/Makefile.arch
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 06/19] perf comm: Add __guarded_by annotations to struct comm_strs
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (4 preceding siblings ...)
2026-11-04 20:58 ` [PATCH v9 05/19] perf build: Enable additional Clang thread-safety flags Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:18 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 07/19] perf vdso: Fix unlocked __dsos__add() and race in __machine__addnew_vdso() Ian Rogers
` (12 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Annotate strs, num_strs, and capacity in struct comm_strs with
__guarded_by(&lock) and __pt_guarded_by(&lock) so Clang verifies all
accesses hold comm_strs->lock.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/comm.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c
index 6343a3c9debe..9fc4e91a36e9 100644
--- a/tools/perf/util/comm.c
+++ b/tools/perf/util/comm.c
@@ -16,9 +16,9 @@ DECLARE_RC_STRUCT(comm_str) {
static struct comm_strs {
struct rw_semaphore lock;
- struct comm_str **strs;
- int num_strs;
- int capacity;
+ struct comm_str **strs __pt_guarded_by(&lock) __guarded_by(&lock);
+ int num_strs __guarded_by(&lock);
+ int capacity __guarded_by(&lock);
} _comm_strs;
static void comm_strs__remove_if_last(struct comm_str *cs);
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 07/19] perf vdso: Fix unlocked __dsos__add() and race in __machine__addnew_vdso()
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (5 preceding siblings ...)
2026-11-04 20:58 ` [PATCH v9 06/19] perf comm: Add __guarded_by annotations to struct comm_strs Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:11 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 08/19] perf dsos: Skip BPF and OOL DSOs in dsos__find_kernel_dso() Ian Rogers
` (11 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
__machine__addnew_vdso() called __dsos__add(&machine->dsos, dso) without
holding machine->dsos.lock, and machine__findnew_vdso() and
__machine__findnew_compat() initialized machine->vdso_info and created
temporary VDSO files without synchronization. Acquire
machine->dsos.lock in write mode across machine->vdso_info
initialization, duplicate checking via __dsos__find(), temporary file
creation, dso__set_long_name(), and __dsos__add().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dsos.c | 8 ++++++-
tools/perf/util/dsos.h | 2 ++
tools/perf/util/vdso.c | 52 +++++++++++++++++++++++++++---------------
3 files changed, 42 insertions(+), 20 deletions(-)
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index a2cd37bd1660..1574c8885d78 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -284,12 +284,18 @@ static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, const st
return res;
}
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short, bool write_locked)
+ __must_hold_shared(&dsos->lock)
+{
+ return __dsos__find_id(dsos, name, &dso_id_empty, cmp_short, write_locked);
+}
+
struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
{
struct dso *res;
down_read(&dsos->lock);
- res = __dsos__find_id(dsos, name, &dso_id_empty, cmp_short, /*write_locked=*/false);
+ res = __dsos__find(dsos, name, cmp_short, /*write_locked=*/false);
up_read(&dsos->lock);
return res;
}
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index a26774950866..dbd06f420753 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -30,6 +30,8 @@ void dsos__exit(struct dsos *dsos);
int __dsos__add(struct dsos *dsos, struct dso *dso);
int dsos__add(struct dsos *dsos, struct dso *dso);
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short, bool write_locked)
+ __must_hold_shared(&dsos->lock);
struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id);
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index c12f5d8c4bf6..93828e660672 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -131,8 +131,13 @@ static struct dso *__machine__addnew_vdso(struct machine *machine, const char *s
dso = dso__new(short_name);
if (dso != NULL) {
- __dsos__add(&machine->dsos, dso);
+ /*
+ * Set the long name before adding to machine->dsos so that
+ * __dsos__add() inserts it in sorted order without needing to
+ * re-take machine->dsos.lock.
+ */
dso__set_long_name(dso, long_name, false);
+ __dsos__add(&machine->dsos, dso);
}
return dso;
@@ -250,15 +255,19 @@ static struct dso *__machine__findnew_compat(struct machine *machine,
const char *file_name;
struct dso *dso;
- dso = dsos__find(&machine->dsos, vdso_file->dso_name, true);
+ down_write(&machine->dsos.lock);
+ dso = __dsos__find(&machine->dsos, vdso_file->dso_name, true, /*write_locked=*/true);
if (dso)
- return dso;
+ goto out_unlock;
file_name = vdso__get_compat_file(vdso_file);
if (!file_name)
- return NULL;
+ goto out_unlock;
- return __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);
+ dso = __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);
+out_unlock:
+ up_write(&machine->dsos.lock);
+ return dso;
}
static int __machine__findnew_vdso_compat(struct machine *machine,
@@ -332,33 +341,38 @@ struct dso *machine__findnew_vdso(struct machine *machine,
{
struct vdso_info *vdso_info;
struct dso *dso = NULL;
- char *file;
+ const char *file;
+ dso = machine__find_vdso(machine, thread);
+ if (dso)
+ return dso;
+
+ down_write(&machine->dsos.lock);
if (!machine->vdso_info)
machine->vdso_info = vdso_info__new();
vdso_info = machine->vdso_info;
- if (!vdso_info)
+ if (!vdso_info) {
+ up_write(&machine->dsos.lock);
return NULL;
-
- dso = machine__find_vdso(machine, thread);
- if (dso)
- return dso;
+ }
#if BITS_PER_LONG == 64
+ up_write(&machine->dsos.lock);
if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso))
return dso;
+ down_write(&machine->dsos.lock);
#endif
- dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
- if (dso)
- return dso;
-
- file = get_file(&vdso_info->vdso);
- if (!file)
- return NULL;
+ dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true, /*write_locked=*/true);
+ if (!dso) {
+ file = get_file(&vdso_info->vdso);
+ if (file)
+ dso = __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
+ }
+ up_write(&machine->dsos.lock);
- return __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
+ return dso;
}
bool dso__is_vdso(struct dso *dso)
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 08/19] perf dsos: Skip BPF and OOL DSOs in dsos__find_kernel_dso()
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (6 preceding siblings ...)
2026-11-04 20:58 ` [PATCH v9 07/19] perf vdso: Fix unlocked __dsos__add() and race in __machine__addnew_vdso() Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:22 ` sashiko-bot
2026-11-04 20:58 ` [PATCH v9 09/19] perf dsos: Ensure DSOs in auxtrace, machine, and symbol are in dsos Ian Rogers
` (10 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
In machine__process_ksymbol_register(), set the binary type of non-OOL
ksymbol DSOs to DSO_BINARY_TYPE__BPF_IMAGE (which
machine__process_bpf_event_load() upgrades to
DSO_BINARY_TYPE__BPF_PROG_INFO on PERF_BPF_EVENT_PROG_LOAD), and update
dsos__find_kernel_dso_cb() to skip DSO_BINARY_TYPE__BPF_PROG_INFO,
DSO_BINARY_TYPE__BPF_IMAGE, and DSO_BINARY_TYPE__OOL so that
dsos__find_kernel_dso() does not match ksymbol DSOs in machine->dsos.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dsos.c | 3 +++
tools/perf/util/machine.c | 9 ++++-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 1574c8885d78..fbbf98c4ccae 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -475,6 +475,9 @@ static int dsos__find_kernel_dso_cb(struct dso *dso, void *data)
* is_kernel_module() treats it as a kernel cpumode.
*/
if (!dso__kernel(dso) ||
+ dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO ||
+ dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE ||
+ dso__binary_type(dso) == DSO_BINARY_TYPE__OOL ||
is_kernel_module(dso__long_name(dso), PERF_RECORD_MISC_CPUMODE_UNKNOWN))
return 0;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 076d167380f2..b958c6935a56 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -769,6 +769,10 @@ static int machine__process_ksymbol_register(struct machine *machine,
dso__set_binary_type(dso, DSO_BINARY_TYPE__OOL);
dso__data(dso)->file_size = event->ksymbol.len;
dso__set_loaded(dso);
+ } else {
+ dso__set_binary_type(dso, DSO_BINARY_TYPE__BPF_IMAGE);
+ if (is_bpf_image(event->ksymbol.name))
+ dso__set_long_name(dso, "", false);
}
map__set_start(map, event->ksymbol.addr);
@@ -780,11 +784,6 @@ static int machine__process_ksymbol_register(struct machine *machine,
}
dso__set_loaded(dso);
-
- if (is_bpf_image(event->ksymbol.name)) {
- dso__set_binary_type(dso, DSO_BINARY_TYPE__BPF_IMAGE);
- dso__set_long_name(dso, "", false);
- }
} else {
dso = dso__get(map__dso(map));
}
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 09/19] perf dsos: Ensure DSOs in auxtrace, machine, and symbol are in dsos
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (7 preceding siblings ...)
2026-11-04 20:58 ` [PATCH v9 08/19] perf dsos: Skip BPF and OOL DSOs in dsos__find_kernel_dso() Ian Rogers
@ 2026-11-04 20:58 ` Ian Rogers
2026-11-04 21:19 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 10/19] perf dso: Pass dso_data to close_data_fd() and dso__list_del() Ian Rogers
` (9 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:58 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Place the DSOs created in addr_filter__resolve_syms(),
machine__process_ksymbol_register(), and maps__split_kallsyms() into a
dsos collection so that all DSOs that may have data fds opened or be
referenced by maps belong to a dsos collection.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/auxtrace.c | 16 +++++++++++++---
tools/perf/util/machine.c | 4 ++++
tools/perf/util/symbol.c | 4 ++++
3 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 8bb491d389a2..1451de019df0 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -29,6 +29,7 @@
#include "config.h"
#include "evlist.h"
#include "dso.h"
+#include "dsos.h"
#include "map.h"
#include "pmu.h"
#include "evsel.h"
@@ -2684,7 +2685,7 @@ static int addr_filter__resolve_kernel_syms(struct addr_filter *filt)
return 0;
}
-static struct dso *load_dso(const char *name)
+static struct dso *load_dso(const char *name, struct dsos *dsos)
{
struct map *map;
struct dso *dso;
@@ -2693,6 +2694,11 @@ static struct dso *load_dso(const char *name)
if (!map)
return NULL;
+ if (dsos__add(dsos, map__dso(map))) {
+ map__put(map);
+ return NULL;
+ }
+
if (map__load(map) < 0)
pr_err("File '%s' not found or has no symbols.\n", name);
@@ -2794,6 +2800,7 @@ static int addr_filter__resolve_syms(struct addr_filter *filt)
{
u64 start, size;
struct dso *dso;
+ struct dsos dsos;
int err = 0;
if (!filt->sym_from && !filt->sym_to)
@@ -2802,9 +2809,11 @@ static int addr_filter__resolve_syms(struct addr_filter *filt)
if (!filt->filename)
return addr_filter__resolve_kernel_syms(filt);
- dso = load_dso(filt->filename);
+ dsos__init(&dsos);
+ dso = load_dso(filt->filename, &dsos);
if (!dso) {
pr_err("Failed to load symbols from: %s\n", filt->filename);
+ dsos__exit(&dsos);
return -EINVAL;
}
@@ -2831,13 +2840,14 @@ static int addr_filter__resolve_syms(struct addr_filter *filt)
err = check_end_after_start(filt, start, size);
if (err)
- return err;
+ goto put_dso;
filt->size = start + size - filt->addr;
}
put_dso:
dso__put(dso);
+ dsos__exit(&dsos);
return err;
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index b958c6935a56..0044ea046aca 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -775,6 +775,10 @@ static int machine__process_ksymbol_register(struct machine *machine,
dso__set_long_name(dso, "", false);
}
+ err = dsos__add(&machine->dsos, dso);
+ if (err)
+ goto out;
+
map__set_start(map, event->ksymbol.addr);
map__set_end(map, map__start(map) + event->ksymbol.len);
err = maps__fixup_overlap_and_insert(machine__kernel_maps(machine), map);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b61a574ce9a3..686fc48841c6 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1090,6 +1090,10 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
dso__set_symtab_type(ndso, DSO_BINARY_TYPE__KALLSYMS);
}
dso__set_loaded(ndso);
+ if (dsos__add(&machine->dsos, ndso)) {
+ dso__put(ndso);
+ return -1;
+ }
curr_map = map__new2(pos->start, ndso);
if (curr_map == NULL) {
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 10/19] perf dso: Pass dso_data to close_data_fd() and dso__list_del()
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (8 preceding siblings ...)
2026-11-04 20:58 ` [PATCH v9 09/19] perf dsos: Ensure DSOs in auxtrace, machine, and symbol are in dsos Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:19 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 11/19] perf dso: Use DEFINE_MUTEX for dso__data_open_lock Ian Rogers
` (8 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
close_data_fd() and dso__list_del() only operate on struct dso_data
fields, while close_first_dso() retrieves a struct dso_data * from
dso__data_open. Pass struct dso_data * directly to close_data_fd() and
dso__list_del() so close_first_dso() does not need to recover a
struct dso * from struct dso_data *.
This removes the REFCNT_CHECKING dso pointer from struct dso_data and
avoids dropping _dso__data_open_lock in dso__list_del() to call
dso__put(), preventing lock-order inversions and races during EMFILE
eviction.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 44 ++++++++++++-------------------------------
tools/perf/util/dso.h | 3 ---
2 files changed, 12 insertions(+), 35 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 24ff066b9878..cbc4f30e7f23 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "dso.h"
+#include <assert.h>
#include <errno.h>
#include <stdlib.h>
@@ -544,22 +545,14 @@ static struct mutex *dso__data_open_lock(void) __returns_ctx_lock(&_dso__data_op
static void dso__list_add(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
list_add_tail(&dso__data(dso)->open_entry, &dso__data_open);
-#ifdef REFCNT_CHECKING
- dso__data(dso)->dso = dso__get(dso);
-#endif
- /* Assume the dso is part of dsos, hence the optional reference count above. */
+ /* Assume the dso is part of dsos, holding it live while on dso__data_open. */
assert(dso__dsos(dso));
dso__data_open_cnt++;
}
-static void dso__list_del(struct dso *dso) __must_hold(&_dso__data_open_lock)
+static void dso__list_del(struct dso_data *dso_data) __must_hold(&_dso__data_open_lock)
{
- list_del_init(&dso__data(dso)->open_entry);
-#ifdef REFCNT_CHECKING
- mutex_unlock(dso__data_open_lock());
- dso__put(dso__data(dso)->dso);
- mutex_lock(dso__data_open_lock());
-#endif
+ list_del_init(&dso_data->open_entry);
WARN_ONCE(dso__data_open_cnt <= 0,
"DSO data fd counter out of bounds.");
dso__data_open_cnt--;
@@ -729,13 +722,13 @@ static int open_dso(struct dso *dso, struct machine *machine)
return fd;
}
-static void close_data_fd(struct dso *dso) __must_hold(&_dso__data_open_lock)
+static void close_data_fd(struct dso_data *dso_data) __must_hold(&_dso__data_open_lock)
{
- if (dso__data(dso)->fd >= 0) {
- close(dso__data(dso)->fd);
- dso__data(dso)->fd = -1;
- dso__data(dso)->file_size = 0;
- dso__list_del(dso);
+ if (dso_data->fd >= 0) {
+ close(dso_data->fd);
+ dso_data->fd = -1;
+ dso_data->file_size = 0;
+ dso__list_del(dso_data);
}
}
@@ -748,21 +741,15 @@ static void close_data_fd(struct dso *dso) __must_hold(&_dso__data_open_lock)
*/
static void close_dso(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
- close_data_fd(dso);
+ close_data_fd(dso__data(dso));
}
static void close_first_dso(void) __must_hold(&_dso__data_open_lock)
{
struct dso_data *dso_data;
- struct dso *dso;
dso_data = list_first_entry(&dso__data_open, struct dso_data, open_entry);
-#ifdef REFCNT_CHECKING
- dso = dso_data->dso;
-#else
- dso = container_of(dso_data, struct dso, data);
-#endif
- close_dso(dso);
+ close_data_fd(dso_data);
}
static rlim_t get_fd_limit(void)
@@ -1858,9 +1845,6 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
data->fd = -1;
data->status = DSO_DATA_STATUS_UNKNOWN;
INIT_LIST_HEAD(&data->open_entry);
-#ifdef REFCNT_CHECKING
- data->dso = NULL; /* Set when on the open_entry list. */
-#endif
}
return res;
}
@@ -1918,10 +1902,6 @@ struct dso *dso__get(struct dso *dso)
void dso__put(struct dso *dso)
{
-#ifdef REFCNT_CHECKING
- if (dso && dso__data(dso) && refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2)
- dso__data_close(dso);
-#endif
if (dso && refcount_dec_and_test(&RC_CHK_ACCESS(dso)->refcnt))
dso__delete(dso);
else
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index f6c60bafcde1..0c18c0f3ef88 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -261,9 +261,6 @@ struct dso_cache {
struct dso_data {
struct rb_root cache;
struct list_head open_entry;
-#ifdef REFCNT_CHECKING
- struct dso *dso;
-#endif
int fd;
int status;
u32 status_seen;
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 11/19] perf dso: Use DEFINE_MUTEX for dso__data_open_lock
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (9 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 10/19] perf dso: Pass dso_data to close_data_fd() and dso__list_del() Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:19 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 12/19] perf dsos: Use read lock fast path in dsos__findnew_id() Ian Rogers
` (7 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Replace _dso__data_open_lock and its pthread_once() initialization
wrapper dso__data_open_lock() with DEFINE_MUTEX(dso__data_open_lock).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 68 +++++++++++++++++--------------------------
tools/perf/util/dso.h | 10 +++----
2 files changed, 32 insertions(+), 46 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index cbc4f30e7f23..f0e02062c09c 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -524,25 +524,11 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m,
/*
* Global list of open DSOs and the counter.
*/
-struct mutex _dso__data_open_lock;
+DEFINE_MUTEX(dso__data_open_lock);
static LIST_HEAD(dso__data_open);
-static long dso__data_open_cnt __guarded_by(&_dso__data_open_lock);
+static long dso__data_open_cnt __guarded_by(&dso__data_open_lock);
-static void dso__data_open_lock_init(void)
-{
- mutex_init(&_dso__data_open_lock);
-}
-
-static struct mutex *dso__data_open_lock(void) __returns_ctx_lock(&_dso__data_open_lock)
-{
- static pthread_once_t data_open_lock_once = PTHREAD_ONCE_INIT;
-
- pthread_once(&data_open_lock_once, dso__data_open_lock_init);
-
- return &_dso__data_open_lock;
-}
-
-static void dso__list_add(struct dso *dso) __must_hold(&_dso__data_open_lock)
+static void dso__list_add(struct dso *dso) __must_hold(&dso__data_open_lock)
{
list_add_tail(&dso__data(dso)->open_entry, &dso__data_open);
/* Assume the dso is part of dsos, holding it live while on dso__data_open. */
@@ -550,7 +536,7 @@ static void dso__list_add(struct dso *dso) __must_hold(&_dso__data_open_lock)
dso__data_open_cnt++;
}
-static void dso__list_del(struct dso_data *dso_data) __must_hold(&_dso__data_open_lock)
+static void dso__list_del(struct dso_data *dso_data) __must_hold(&dso__data_open_lock)
{
list_del_init(&dso_data->open_entry);
WARN_ONCE(dso__data_open_cnt <= 0,
@@ -560,7 +546,7 @@ static void dso__list_del(struct dso_data *dso_data) __must_hold(&_dso__data_ope
static void close_first_dso(void);
-static int do_open(char *name) __must_hold(&_dso__data_open_lock)
+static int do_open(char *name) __must_hold(&dso__data_open_lock)
{
do {
int fd = open(name, O_RDONLY|O_CLOEXEC);
@@ -663,7 +649,7 @@ void dso__put_filename(struct dso *dso __maybe_unused, char *filename, bool deco
}
static int __open_dso(struct dso *dso, struct machine *machine)
- __must_hold(&_dso__data_open_lock)
+ __must_hold(&dso__data_open_lock)
{
int fd = -EINVAL;
char *name;
@@ -696,7 +682,7 @@ static void check_data_close(void);
* list/count of open DSO objects.
*/
static int open_dso(struct dso *dso, struct machine *machine)
- __must_hold(&_dso__data_open_lock)
+ __must_hold(&dso__data_open_lock)
{
int fd;
struct nscookie nsc;
@@ -722,7 +708,7 @@ static int open_dso(struct dso *dso, struct machine *machine)
return fd;
}
-static void close_data_fd(struct dso_data *dso_data) __must_hold(&_dso__data_open_lock)
+static void close_data_fd(struct dso_data *dso_data) __must_hold(&dso__data_open_lock)
{
if (dso_data->fd >= 0) {
close(dso_data->fd);
@@ -739,12 +725,12 @@ static void close_data_fd(struct dso_data *dso_data) __must_hold(&_dso__data_ope
* Close @dso's data file descriptor and updates
* list/count of open DSO objects.
*/
-static void close_dso(struct dso *dso) __must_hold(&_dso__data_open_lock)
+static void close_dso(struct dso *dso) __must_hold(&dso__data_open_lock)
{
close_data_fd(dso__data(dso));
}
-static void close_first_dso(void) __must_hold(&_dso__data_open_lock)
+static void close_first_dso(void) __must_hold(&dso__data_open_lock)
{
struct dso_data *dso_data;
@@ -783,7 +769,7 @@ void reset_fd_limit(void)
fd_limit = 0;
}
-static bool may_cache_fd(void) __must_hold(&_dso__data_open_lock)
+static bool may_cache_fd(void) __must_hold(&dso__data_open_lock)
{
if (!fd_limit)
fd_limit = get_fd_limit();
@@ -799,7 +785,7 @@ static bool may_cache_fd(void) __must_hold(&_dso__data_open_lock)
* for opened dso file descriptors. The limit is half
* of the RLIMIT_NOFILE files opened.
*/
-static void check_data_close(void) __must_hold(&_dso__data_open_lock)
+static void check_data_close(void) __must_hold(&dso__data_open_lock)
{
bool cache_fd = may_cache_fd();
@@ -815,9 +801,9 @@ static void check_data_close(void) __must_hold(&_dso__data_open_lock)
*/
void dso__data_close(struct dso *dso)
{
- mutex_lock(dso__data_open_lock());
+ mutex_lock(&dso__data_open_lock);
close_dso(dso);
- mutex_unlock(dso__data_open_lock());
+ mutex_unlock(&dso__data_open_lock);
}
static int dso__find_binary_type_locked(struct dso *dso)
@@ -913,7 +899,7 @@ static int dso__find_binary_type_locked(struct dso *dso)
#define FIND_MAX_RETRY 3
static int __dso__find_binary_type(struct dso *dso)
- __must_hold(&_dso__data_open_lock)
+ __must_hold(&dso__data_open_lock)
{
int ret;
int retry = 0;
@@ -945,15 +931,15 @@ enum dso_binary_type dso__find_binary_type(struct dso *dso)
{
int ret;
- mutex_lock(dso__data_open_lock());
+ mutex_lock(&dso__data_open_lock);
ret = __dso__find_binary_type(dso);
- mutex_unlock(dso__data_open_lock());
+ mutex_unlock(&dso__data_open_lock);
return ret;
}
static void try_to_open_dso(struct dso *dso, struct machine *machine)
- __must_hold(&_dso__data_open_lock)
+ __must_hold(&dso__data_open_lock)
{
struct dso_data *dso_data = dso__data(dso);
@@ -984,7 +970,7 @@ bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd)
if (dso__data(dso)->status == DSO_DATA_STATUS_ERROR)
return false;
- mutex_lock(dso__data_open_lock());
+ mutex_lock(&dso__data_open_lock);
try_to_open_dso(dso, machine);
@@ -992,13 +978,13 @@ bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd)
if (*fd >= 0)
return true;
- mutex_unlock(dso__data_open_lock());
+ mutex_unlock(&dso__data_open_lock);
return false;
}
void dso__data_put_fd(struct dso *dso __maybe_unused)
{
- mutex_unlock(dso__data_open_lock());
+ mutex_unlock(&dso__data_open_lock);
}
bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
@@ -1169,7 +1155,7 @@ static ssize_t file_read(struct dso *dso, struct machine *machine,
{
ssize_t ret;
- mutex_lock(dso__data_open_lock());
+ mutex_lock(&dso__data_open_lock);
/*
* dso__data(dso)->fd might be closed if other thread opened another
@@ -1185,7 +1171,7 @@ static ssize_t file_read(struct dso *dso, struct machine *machine,
ret = pread(dso__data(dso)->fd, data, DSO__DATA_CACHE_SIZE, offset);
out:
- mutex_unlock(dso__data_open_lock());
+ mutex_unlock(&dso__data_open_lock);
return ret;
}
@@ -1292,7 +1278,7 @@ static int file_size(struct dso *dso, struct machine *machine)
int ret = 0;
struct stat st;
- mutex_lock(dso__data_open_lock());
+ mutex_lock(&dso__data_open_lock);
/*
* dso__data(dso)->fd might be closed if other thread opened another
@@ -1315,7 +1301,7 @@ static int file_size(struct dso *dso, struct machine *machine)
dso__data(dso)->file_size = st.st_size;
out:
- mutex_unlock(dso__data_open_lock());
+ mutex_unlock(&dso__data_open_lock);
return ret;
}
@@ -1520,7 +1506,7 @@ uint16_t dso__e_machine_endian(struct dso *dso, struct machine *machine, uint32_
return EM_NONE;
}
- mutex_lock(dso__data_open_lock());
+ mutex_lock(&dso__data_open_lock);
/*
* dso__data(dso)->fd might be closed if other thread opened another
@@ -1533,7 +1519,7 @@ uint16_t dso__e_machine_endian(struct dso *dso, struct machine *machine, uint32_
else if (e_flags)
*e_flags = 0;
- mutex_unlock(dso__data_open_lock());
+ mutex_unlock(&dso__data_open_lock);
return e_machine;
}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 0c18c0f3ef88..9e38b3b69e9b 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -354,7 +354,7 @@ DECLARE_RC_STRUCT(dso) {
char name[];
};
-extern struct mutex _dso__data_open_lock;
+extern struct mutex dso__data_open_lock;
extern const struct dso_id dso_id_empty;
int dso_id__cmp(const struct dso_id *a, const struct dso_id *b);
@@ -807,7 +807,7 @@ void __dso__improve_id(struct dso *dso, const struct dso_id *id);
int dso__name_len(const struct dso *dso);
struct dso *dso__get(struct dso *dso);
-void dso__put(struct dso *dso) __must_not_hold(&_dso__data_open_lock);
+void dso__put(struct dso *dso) __must_not_hold(&dso__data_open_lock);
static inline void __dso__zput(struct dso **dso)
{
@@ -917,9 +917,9 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m,
* TODO
*/
bool dso__data_get_fd(struct dso *dso, struct machine *machine, int *fd)
- __cond_acquires(true, &_dso__data_open_lock);
-void dso__data_put_fd(struct dso *dso) __releases(&_dso__data_open_lock);
-void dso__data_close(struct dso *dso) __must_not_hold(&_dso__data_open_lock);
+ __cond_acquires(true, &dso__data_open_lock);
+void dso__data_put_fd(struct dso *dso) __releases(&dso__data_open_lock);
+void dso__data_close(struct dso *dso) __must_not_hold(&dso__data_open_lock);
int dso__data_file_size(struct dso *dso, struct machine *machine);
off_t dso__data_size(struct dso *dso, struct machine *machine);
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 12/19] perf dsos: Use read lock fast path in dsos__findnew_id()
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (10 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 11/19] perf dso: Use DEFINE_MUTEX for dso__data_open_lock Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:23 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 13/19] perf dso: Expand context analysis annotations Ian Rogers
` (6 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
In dsos__findnew_id(), try finding the DSO under the read lock first and
only acquire the write lock when a new DSO must be inserted or an
existing DSO needs its ID updated (checked via dso__needs_improve_id()).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 16 +++++++++++++++-
tools/perf/util/dso.h | 1 +
tools/perf/util/dsos.c | 10 ++++++++++
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index f0e02062c09c..4dc518f7cb7d 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1685,6 +1685,18 @@ const struct dso_id dso_id_empty = {
}
};
+bool dso__needs_improve_id(const struct dso *dso, const struct dso_id *id)
+{
+ const struct dso_id *dso_id = dso__id_const(dso);
+
+ if (!id)
+ return false;
+
+ return (id->mmap2_valid && !dso_id->mmap2_valid) ||
+ (id->mmap2_ino_generation_valid && !dso_id->mmap2_ino_generation_valid) ||
+ (build_id__is_defined(&id->build_id) && !build_id__is_defined(&dso_id->build_id));
+}
+
void __dso__improve_id(struct dso *dso, const struct dso_id *id)
{
struct dsos *dsos = dso__dsos(dso);
@@ -1692,6 +1704,8 @@ void __dso__improve_id(struct dso *dso, const struct dso_id *id)
bool changed = false;
/* dsos write lock held by caller. */
+ if (!id)
+ return;
if (id->mmap2_valid && !dso_id->mmap2_valid) {
dso_id->maj = id->maj;
@@ -1715,7 +1729,7 @@ void __dso__improve_id(struct dso *dso, const struct dso_id *id)
int dso_id__cmp(const struct dso_id *a, const struct dso_id *b)
{
- if (a == &dso_id_empty || b == &dso_id_empty) {
+ if (!a || !b || a == &dso_id_empty || b == &dso_id_empty) {
/* There is no valid data to compare so the comparison always returns identical. */
return 0;
}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 9e38b3b69e9b..df1748763d95 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -802,6 +802,7 @@ void dso__delete(struct dso *dso);
int dso__cmp_id(struct dso *a, struct dso *b);
void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated);
void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated);
+bool dso__needs_improve_id(const struct dso *dso, const struct dso_id *id);
void __dso__improve_id(struct dso *dso, const struct dso_id *id);
int dso__name_len(const struct dso *dso);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index fbbf98c4ccae..1b3f8b84ed30 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -349,6 +349,16 @@ static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, const
struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)
{
struct dso *dso;
+
+ down_read(&dsos->lock);
+ dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/false);
+ if (dso && !dso__needs_improve_id(dso, id)) {
+ up_read(&dsos->lock);
+ return dso;
+ }
+ up_read(&dsos->lock);
+ dso__put(dso);
+
down_write(&dsos->lock);
dso = __dsos__findnew_id(dsos, name, id);
up_write(&dsos->lock);
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 13/19] perf dso: Expand context analysis annotations
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (11 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 12/19] perf dsos: Use read lock fast path in dsos__findnew_id() Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:22 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 14/19] perf thread: Expand context analysis annotations to threads and namespaces_lock Ian Rogers
` (5 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Annotate struct dsos fields with __guarded_by(&lock), annotate dso__data_open and
fd_limit in util/dso.c with __guarded_by(&dso__data_open_lock), and
tighten __dsos__findnew_id() and __dsos__addnew_id() from shared to
exclusive lock requirements.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/dso-data.c | 4 ++++
tools/perf/util/dso.c | 14 +++++++++-----
tools/perf/util/dsos.c | 9 +++++++--
tools/perf/util/dsos.h | 10 +++++-----
tools/perf/util/vdso.c | 1 +
5 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c
index 18b7052a2dc5..6ef59929e6c0 100644
--- a/tools/perf/tests/dso-data.c
+++ b/tools/perf/tests/dso-data.c
@@ -116,12 +116,14 @@ static int dso__data_fd(struct dso *dso, struct machine *machine)
static void dsos__delete(struct dsos *dsos)
{
+ down_write(&dsos->lock);
for (unsigned int i = 0; i < dsos->cnt; i++) {
struct dso *dso = dsos->dsos[i];
dso__data_close(dso);
unlink(dso__name(dso));
}
+ up_write(&dsos->lock);
dsos__exit(dsos);
}
@@ -254,6 +256,7 @@ static int set_fd_limit(int n)
}
static int test__dso_data_cache(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
+ __context_unsafe(/* single-threaded test */)
{
struct machine machine;
long nr_end, nr = open_files_cnt();
@@ -324,6 +327,7 @@ static long new_limit(int count)
}
static int test__dso_data_reopen(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
+ __context_unsafe(/* single-threaded test */)
{
struct machine machine;
long nr_end, nr = open_files_cnt(), lim = new_limit(3);
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 4dc518f7cb7d..5f4b4cd9a089 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -525,7 +525,8 @@ void dso__set_module_info(struct dso *dso, struct kmod_path *m,
* Global list of open DSOs and the counter.
*/
DEFINE_MUTEX(dso__data_open_lock);
-static LIST_HEAD(dso__data_open);
+static struct list_head dso__data_open __guarded_by(&dso__data_open_lock) =
+ LIST_HEAD_INIT(dso__data_open);
static long dso__data_open_cnt __guarded_by(&dso__data_open_lock);
static void dso__list_add(struct dso *dso) __must_hold(&dso__data_open_lock)
@@ -757,7 +758,7 @@ static rlim_t get_fd_limit(void)
return limit;
}
-static rlim_t fd_limit;
+static rlim_t fd_limit __guarded_by(&dso__data_open_lock);
/*
* Used only by tests/dso-data.c to reset the environment
@@ -766,7 +767,8 @@ static rlim_t fd_limit;
*/
void reset_fd_limit(void)
{
- fd_limit = 0;
+ /* Only called by single-threaded test setup. */
+ context_unsafe(fd_limit = 0);
}
static bool may_cache_fd(void) __must_hold(&dso__data_open_lock)
@@ -1723,8 +1725,10 @@ void __dso__improve_id(struct dso *dso, const struct dso_id *id)
dso_id->build_id = id->build_id;
changed = true;
}
- if (changed && dsos)
- dsos->sorted = false;
+ if (changed && dsos) {
+ /* dsos write lock held by caller. */
+ context_unsafe(dsos->sorted = false);
+ }
}
int dso_id__cmp(const struct dso_id *a, const struct dso_id *b)
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 1b3f8b84ed30..7f3a6dabf8a7 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -12,6 +12,7 @@
#include <unistd.h>
void dsos__init(struct dsos *dsos)
+ __context_unsafe(/* initialization */)
{
init_rwsem(&dsos->lock);
@@ -50,6 +51,7 @@ void dsos__exit(struct dsos *dsos)
static int __dsos__for_each_dso(struct dsos *dsos,
int (*cb)(struct dso *dso, void *data),
void *data)
+ __must_hold_shared(&dsos->lock)
{
for (unsigned int i = 0; i < dsos->cnt; i++) {
struct dso *dso = dsos->dsos[i];
@@ -182,7 +184,8 @@ static struct dso *__dsos__find_by_longname_id(struct dsos *dsos,
}
qsort(dsos->dsos, dsos->cnt, sizeof(struct dso *),
dsos__cmp_long_name_id_short_name);
- dsos->sorted = true;
+ /* write_locked is true, so dsos->lock is held exclusively. */
+ context_unsafe(dsos->sorted = true);
}
res = bsearch(&key, dsos->dsos, dsos->cnt, sizeof(struct dso *),
@@ -194,6 +197,7 @@ static struct dso *__dsos__find_by_longname_id(struct dsos *dsos,
}
int __dsos__add(struct dsos *dsos, struct dso *dso)
+ __must_hold(&dsos->lock)
{
if (!dso)
return -EINVAL;
@@ -320,6 +324,7 @@ static void dso__set_basename(struct dso *dso)
}
static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)
+ __must_hold(&dsos->lock)
{
struct dso *dso = dso__new_id(name, id);
@@ -336,7 +341,7 @@ static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const
}
static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id)
- __must_hold_shared(&dsos->lock)
+ __must_hold(&dsos->lock)
{
struct dso *dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/true);
diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h
index dbd06f420753..cc7e3a42f9cb 100644
--- a/tools/perf/util/dsos.h
+++ b/tools/perf/util/dsos.h
@@ -19,16 +19,16 @@ struct machine;
*/
struct dsos {
struct rw_semaphore lock;
- struct dso **dsos;
- unsigned int cnt;
- unsigned int allocated;
- bool sorted;
+ struct dso **dsos __pt_guarded_by(&lock) __guarded_by(&lock);
+ unsigned int cnt __guarded_by(&lock);
+ unsigned int allocated __guarded_by(&lock);
+ bool sorted __guarded_by(&lock);
};
void dsos__init(struct dsos *dsos);
void dsos__exit(struct dsos *dsos);
-int __dsos__add(struct dsos *dsos, struct dso *dso);
+int __dsos__add(struct dsos *dsos, struct dso *dso) __must_hold(&dsos->lock);
int dsos__add(struct dsos *dsos, struct dso *dso);
struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short, bool write_locked)
__must_hold_shared(&dsos->lock);
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index 93828e660672..fc75c0cf372f 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -126,6 +126,7 @@ void machine__exit_vdso(struct machine *machine)
static struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name,
const char *long_name)
+ __must_hold(&machine->dsos.lock)
{
struct dso *dso;
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 14/19] perf thread: Expand context analysis annotations to threads and namespaces_lock
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (12 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 13/19] perf dso: Expand context analysis annotations Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:23 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 15/19] perf intel-tpebs: Fix race in tpebs_stop() and tpebs_cmd.out fd handling Ian Rogers
` (4 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
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@google.com>
---
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.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 15/19] perf intel-tpebs: Fix race in tpebs_stop() and tpebs_cmd.out fd handling
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (13 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 14/19] perf thread: Expand context analysis annotations to threads and namespaces_lock Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:36 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 16/19] perf intel-tpebs: Use DEFINE_MUTEX for tpebs_mtx Ian Rogers
` (3 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
In tpebs_stop(), read tpebs_reader_thread into a local variable before
releasing tpebs_mtx to call pthread_join().
Transfer ownership of tpebs_cmd.out to __sample_reader() by passing it as
the thread argument and setting tpebs_cmd.out = -1 in
evsel__tpebs_open() so tpebs_stop() does not double-close the fd closed
by perf_session__delete(). Also reset tpebs_cmd.pid = 0 and tpebs_cmd.out = -1 and return early if
start_command() fails in evsel__tpebs_start_perf_record(), as
start_command() closes the pipe internally and may leave cmd->pid = -1
on error, and join tpebs_reader_thread on error in evsel__tpebs_open()
if it was already started.
Include <pthread.h>, <signal.h>, <stdint.h>, <stdlib.h>, <string.h>, and
<unistd.h> explicitly.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/intel-tpebs.c | 51 ++++++++++++++++++++++++++++++-----
1 file changed, 44 insertions(+), 7 deletions(-)
diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c
index 7c0b8b15bd10..47c939fc2058 100644
--- a/tools/perf/util/intel-tpebs.c
+++ b/tools/perf/util/intel-tpebs.c
@@ -27,6 +27,12 @@
#include <errno.h>
#include <poll.h>
#include <math.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#define PERF_DATA "-"
@@ -120,10 +126,15 @@ static int evsel__tpebs_start_perf_record(struct evsel *evsel)
tpebs_cmd.out = -1;
ret = start_command(&tpebs_cmd);
zfree(&tpebs_cmd.argv);
+ if (ret) {
+ tpebs_cmd.pid = 0;
+ tpebs_cmd.out = -1;
+ return ret;
+ }
list_for_each_entry(t, &tpebs_results, nd)
t->started = true;
- return ret;
+ return 0;
}
static bool is_child_pid(pid_t parent, pid_t child)
@@ -220,13 +231,13 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
return 0;
}
-static void *__sample_reader(void *arg __maybe_unused)
+static void *__sample_reader(void *arg)
{
struct perf_session *session;
struct perf_data data = {
.mode = PERF_DATA_MODE_READ,
.path = PERF_DATA,
- .file.fd = tpebs_cmd.out,
+ .file.fd = (int)(intptr_t)arg,
};
struct perf_tool tool;
@@ -236,8 +247,10 @@ static void *__sample_reader(void *arg __maybe_unused)
tool.attr = perf_event__process_attr;
session = perf_session__new(&data, &tool);
- if (IS_ERR(session))
+ if (IS_ERR(session)) {
+ close(data.file.fd);
return NULL;
+ }
perf_session__process_events(session);
perf_session__delete(session);
@@ -331,12 +344,19 @@ static int tpebs_stop(void) __must_hold(tpebs_mtx_get())
/* Like tpebs_start, we should only run tpebs_end once. */
if (tpebs_cmd.pid != 0) {
pid_t actual_pid = tpebs_cmd.pid;
+ pthread_t reader_thread;
tpebs_stopping = true;
tpebs_send_record_cmd(EVLIST_CTL_CMD_STOP_TAG);
+ /*
+ * tpebs_results is empty when stopping; clear pid so
+ * process_sample_event() ignores any late samples while
+ * draining the pipe.
+ */
tpebs_cmd.pid = 0;
+ reader_thread = tpebs_reader_thread;
mutex_unlock(tpebs_mtx_get());
- pthread_join(tpebs_reader_thread, NULL);
+ pthread_join(reader_thread, NULL);
mutex_lock(tpebs_mtx_get());
if (control_fd[0] >= 0) {
close(control_fd[0]);
@@ -515,6 +535,7 @@ int evsel__tpebs_open(struct evsel *evsel)
int ret;
bool tpebs_empty;
bool started_process = false;
+ bool started_thread = false;
/* We should only run tpebs_start when tpebs_recording is enabled. */
if (!tpebs_recording)
@@ -567,12 +588,15 @@ int evsel__tpebs_open(struct evsel *evsel)
goto out;
if (pthread_create(&tpebs_reader_thread, /*attr=*/NULL, __sample_reader,
- /*arg=*/NULL)) {
+ (void *)(intptr_t)tpebs_cmd.out)) {
kill(tpebs_cmd.pid, SIGTERM);
pr_err("Could not create thread to process sample data.\n");
ret = -1;
goto out;
}
+ /* __sample_reader now owns and closes the output fd. */
+ tpebs_cmd.out = -1;
+ started_thread = true;
ret = tpebs_send_record_cmd(EVLIST_CTL_CMD_ENABLE_TAG);
}
out:
@@ -586,9 +610,22 @@ int evsel__tpebs_open(struct evsel *evsel)
if (started_process) {
if (tpebs_cmd.pid > 0) {
- kill(tpebs_cmd.pid, SIGTERM);
+ pid_t actual_pid = tpebs_cmd.pid;
+
+ tpebs_stopping = true;
+ kill(actual_pid, SIGTERM);
+ tpebs_cmd.pid = 0;
+ if (started_thread) {
+ pthread_t reader_thread = tpebs_reader_thread;
+
+ mutex_unlock(tpebs_mtx_get());
+ pthread_join(reader_thread, NULL);
+ mutex_lock(tpebs_mtx_get());
+ }
+ tpebs_cmd.pid = actual_pid;
finish_command(&tpebs_cmd);
tpebs_cmd.pid = 0;
+ tpebs_stopping = false;
}
if (tpebs_cmd.out >= 0) {
close(tpebs_cmd.out);
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 16/19] perf intel-tpebs: Use DEFINE_MUTEX for tpebs_mtx
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (14 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 15/19] perf intel-tpebs: Fix race in tpebs_stop() and tpebs_cmd.out fd handling Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:32 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 17/19] perf config, intel-tpebs, env: Add __guarded_by and __must_hold annotations Ian Rogers
` (2 subsequent siblings)
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Replace tpebs_mtx_get() and its pthread_once() initialization helper
tpebs_init() with DEFINE_MUTEX(tpebs_mtx) and static initializers for
control_fd and ack_fd.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/intel-tpebs.c | 79 ++++++++++++++---------------------
1 file changed, 32 insertions(+), 47 deletions(-)
diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c
index 47c939fc2058..1812917bfb85 100644
--- a/tools/perf/util/intel-tpebs.c
+++ b/tools/perf/util/intel-tpebs.c
@@ -38,11 +38,11 @@
bool tpebs_recording;
enum tpebs_mode tpebs_mode;
+static DEFINE_MUTEX(tpebs_mtx);
static LIST_HEAD(tpebs_results);
static pthread_t tpebs_reader_thread;
static struct child_process tpebs_cmd;
-static int control_fd[2], ack_fd[2];
-static struct mutex tpebs_mtx;
+static int control_fd[2] = { -1, -1 }, ack_fd[2] = { -1, -1 };
static bool tpebs_stopping;
struct tpebs_retire_lat {
@@ -59,23 +59,8 @@ struct tpebs_retire_lat {
bool started;
};
-static void tpebs_init(void)
-{
- mutex_init(&tpebs_mtx);
- control_fd[0] = control_fd[1] = -1;
- ack_fd[0] = ack_fd[1] = -1;
-}
-
-static struct mutex *tpebs_mtx_get(void)
-{
- static pthread_once_t tpebs_once = PTHREAD_ONCE_INIT;
-
- pthread_once(&tpebs_once, tpebs_init);
- return &tpebs_mtx;
-}
-
static struct tpebs_retire_lat *tpebs_retire_lat__find(struct evsel *evsel)
- __must_hold(tpebs_mtx_get());
+ __must_hold(&tpebs_mtx);
static int evsel__tpebs_start_perf_record(struct evsel *evsel)
{
@@ -205,19 +190,19 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
{
struct tpebs_retire_lat *t;
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
if (tpebs_cmd.pid == 0) {
/* Record has terminated. */
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return 0;
}
t = tpebs_retire_lat__find(sample->evsel);
if (!t) {
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return -EINVAL;
}
if (should_ignore_sample(sample, t)) {
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return 0;
}
/*
@@ -227,7 +212,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
*/
t->last = sample->weight3;
update_stats(&t->stats, sample->weight3);
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return 0;
}
@@ -257,7 +242,7 @@ static void *__sample_reader(void *arg)
return NULL;
}
-static int tpebs_send_record_cmd(const char *msg) __must_hold(tpebs_mtx_get())
+static int tpebs_send_record_cmd(const char *msg) __must_hold(&tpebs_mtx)
{
struct pollfd pollfd = { .events = POLLIN, };
int ret, len, retries = 0;
@@ -271,7 +256,7 @@ static int tpebs_send_record_cmd(const char *msg) __must_hold(tpebs_mtx_get())
* Let go of the lock while sending/receiving as blocking can starve the
* sample reading thread.
*/
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
/* Send perf record command.*/
len = strlen(msg);
@@ -327,14 +312,14 @@ static int tpebs_send_record_cmd(const char *msg) __must_hold(tpebs_mtx_get())
pr_err("tpebs: perf record control ack failed\n");
out:
/* Re-take lock as expected by caller. */
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
return ret;
}
/*
* tpebs_stop - stop the sample data read thread and the perf record process.
*/
-static int tpebs_stop(void) __must_hold(tpebs_mtx_get())
+static int tpebs_stop(void) __must_hold(&tpebs_mtx)
{
int ret = 0;
@@ -355,9 +340,9 @@ static int tpebs_stop(void) __must_hold(tpebs_mtx_get())
*/
tpebs_cmd.pid = 0;
reader_thread = tpebs_reader_thread;
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
pthread_join(reader_thread, NULL);
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
if (control_fd[0] >= 0) {
close(control_fd[0]);
control_fd[0] = -1;
@@ -493,20 +478,20 @@ static int evsel__tpebs_prepare(struct evsel *evsel)
struct evsel *pos;
struct tpebs_retire_lat *tpebs_event;
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
tpebs_event = tpebs_retire_lat__find(evsel);
if (tpebs_event) {
/* evsel, or an identically named one, was already prepared. */
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return 0;
}
tpebs_event = tpebs_retire_lat__new(evsel);
if (!tpebs_event) {
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return -ENOMEM;
}
list_add_tail(&tpebs_event->nd, &tpebs_results);
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
/*
* Eagerly prepare all other evsels on the list to try to ensure that by
@@ -541,9 +526,9 @@ int evsel__tpebs_open(struct evsel *evsel)
if (!tpebs_recording)
return 0;
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
if (tpebs_stopping) {
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return -EBUSY;
}
/* Only start the events once. */
@@ -553,17 +538,17 @@ int evsel__tpebs_open(struct evsel *evsel)
t = tpebs_retire_lat__find(evsel);
valid = t && t->started;
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
/* May fail as the event wasn't started. */
return valid ? 0 : -EBUSY;
}
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
ret = evsel__tpebs_prepare(evsel);
if (ret)
return ret;
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
if (tpebs_stopping || tpebs_cmd.pid != 0) {
ret = -EBUSY;
goto out;
@@ -618,9 +603,9 @@ int evsel__tpebs_open(struct evsel *evsel)
if (started_thread) {
pthread_t reader_thread = tpebs_reader_thread;
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
pthread_join(reader_thread, NULL);
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
}
tpebs_cmd.pid = actual_pid;
finish_command(&tpebs_cmd);
@@ -649,7 +634,7 @@ int evsel__tpebs_open(struct evsel *evsel)
}
}
}
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
return ret;
}
@@ -669,7 +654,7 @@ int evsel__tpebs_read(struct evsel *evsel, int cpu_map_idx, int thread)
count = perf_counts(evsel->counts, cpu_map_idx, thread);
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
t = tpebs_retire_lat__find(evsel);
/*
* If reading the first tpebs result, send a ping to the record
@@ -678,10 +663,10 @@ int evsel__tpebs_read(struct evsel *evsel, int cpu_map_idx, int thread)
*/
if (t && &t->nd == tpebs_results.next) {
ret = tpebs_send_record_cmd(EVLIST_CTL_CMD_PING_TAG);
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
if (ret)
return ret;
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
}
if (t == NULL || t->stats.n == 0) {
/* No sample data, use default. */
@@ -720,7 +705,7 @@ int evsel__tpebs_read(struct evsel *evsel, int cpu_map_idx, int thread)
break;
}
}
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
if (old_count) {
count->val = old_count->val + val;
@@ -745,7 +730,7 @@ void evsel__tpebs_close(struct evsel *evsel)
{
struct tpebs_retire_lat *t;
- mutex_lock(tpebs_mtx_get());
+ mutex_lock(&tpebs_mtx);
t = tpebs_retire_lat__find(evsel);
if (t) {
list_del_init(&t->nd);
@@ -754,5 +739,5 @@ void evsel__tpebs_close(struct evsel *evsel)
if (list_empty(&tpebs_results))
tpebs_stop();
}
- mutex_unlock(tpebs_mtx_get());
+ mutex_unlock(&tpebs_mtx);
}
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 17/19] perf config, intel-tpebs, env: Add __guarded_by and __must_hold annotations
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (15 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 16/19] perf intel-tpebs: Use DEFINE_MUTEX for tpebs_mtx Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:33 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 18/19] perf maps: Fix lock inversion in maps__load_first() and bounds check in __maps__remove() Ian Rogers
2026-11-04 20:59 ` [PATCH v9 19/19] perf maps: Add context analysis annotations Ian Rogers
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Add __guarded_by() and __must_hold() annotations to static parser and
config state in util/config.c, TPEBS state in util/intel-tpebs.c, and
bpf_progs and cgroups trees in struct perf_env.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/bpf-event.h | 5 ++--
tools/perf/util/config.c | 47 +++++++++++++++++++++--------------
tools/perf/util/env.c | 1 +
tools/perf/util/env.h | 19 ++++++++------
tools/perf/util/intel-tpebs.c | 7 +++---
5 files changed, 48 insertions(+), 31 deletions(-)
diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h
index da4eeb4a1a73..3f3323f98f49 100644
--- a/tools/perf/util/bpf-event.h
+++ b/tools/perf/util/bpf-event.h
@@ -6,11 +6,11 @@
#include <linux/rbtree.h>
#include <api/fd/array.h>
#include <stdio.h>
+#include "env.h"
struct bpf_prog_info;
struct machine;
union perf_event;
-struct perf_env;
struct perf_sample;
struct perf_session;
struct record_opts;
@@ -42,7 +42,8 @@ int machine__process_bpf(struct machine *machine, union perf_event *event,
int evlist__add_bpf_sb_event(struct evlist *evlist, struct perf_env *env);
void __bpf_event__print_bpf_prog_info(struct perf_bpil *info_linear,
struct perf_env *env,
- FILE *fp);
+ FILE *fp)
+ __must_hold_shared(&env->bpf_progs.lock);
void bpf_metadata_free(struct bpf_metadata *metadata);
#else
static inline int machine__process_bpf(struct machine *machine __maybe_unused,
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 287401f159e6..0e6b77a0a5e7 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -64,15 +64,29 @@ struct perf_stat_config stat_config = {
char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
-static FILE *config_file;
-static const char *config_file_name;
-static int config_linenr;
-static int config_file_eof;
-static struct perf_config_set *config_set;
+/* Parsing and rewriting share the static parser state. */
+static DEFINE_MUTEX(config_mutex);
+
+/* Serializes whole perf_config__set_variable() updates. */
+static DEFINE_MUTEX(config_update_mutex);
+
+/*
+ * Protects the global config_set. Separate from config_mutex because
+ * initializing config_set parses the config files, which acquires
+ * config_mutex.
+ */
+static DEFINE_MUTEX(config_set_mutex);
+
+static FILE *config_file __guarded_by(&config_mutex);
+static const char *config_file_name __guarded_by(&config_mutex);
+static int config_linenr __guarded_by(&config_mutex);
+static int config_file_eof __guarded_by(&config_mutex);
+static struct perf_config_set *config_set __guarded_by(&config_set_mutex);
const char *config_exclusive_filename;
static int get_next_char(void)
+ __must_hold(&config_mutex)
{
int c;
FILE *f;
@@ -99,8 +113,9 @@ static int get_next_char(void)
}
static char *parse_value(void)
+ __must_hold(&config_mutex)
{
- static char value[1024];
+ static char value[1024] __guarded_by(&config_mutex);
int quote = 0, comment = 0, space = 0;
size_t len = 0;
@@ -170,6 +185,7 @@ static inline int iskeychar(int c)
}
static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
+ __must_hold(&config_mutex)
{
int c;
char *value;
@@ -201,6 +217,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
}
static int get_extended_base_var(char *name, int baselen, int c)
+ __must_hold(&config_mutex)
{
do {
if (c == '\n')
@@ -237,6 +254,7 @@ static int get_extended_base_var(char *name, int baselen, int c)
}
static int get_base_var(char *name)
+ __must_hold(&config_mutex)
{
int baselen = 0;
@@ -257,10 +275,11 @@ static int get_base_var(char *name)
}
static int perf_parse_file(config_fn_t fn, void *data)
+ __must_hold(&config_mutex)
{
int comment = 0;
int baselen = 0;
- static char var[MAXNAME];
+ static char var[MAXNAME] __guarded_by(&config_mutex);
/* U+FEFF Byte Order Mark in UTF8 */
static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf";
@@ -549,12 +568,6 @@ int perf_default_config(const char *var, const char *value,
return 0;
}
-/* Parsing and rewriting share the static parser state. */
-static DEFINE_MUTEX(config_mutex);
-
-/* Serializes whole perf_config__set_variable() updates. */
-static DEFINE_MUTEX(config_update_mutex);
-
static int perf_config_from_file(config_fn_t fn, const char *filename, void *data)
{
int ret;
@@ -737,6 +750,7 @@ static int set_value(struct perf_config_item *item, const char *value)
static int collect_config(const char *var, const char *value,
void *perf_config_set)
+ __must_hold(&config_mutex)
{
int ret = -1;
char *ptr, *key;
@@ -853,11 +867,8 @@ struct perf_config_set *perf_config_set__load_file(const char *file)
return set;
}
-/* Not config_mutex: building the set parses the config files, which takes it. */
-static DEFINE_MUTEX(config_set_mutex);
-
-/* Called with config_set_mutex held. */
static int perf_config__init(void)
+ __must_hold(&config_set_mutex)
{
if (config_set == NULL)
config_set = perf_config_set__new();
@@ -975,7 +986,7 @@ int perf_config__set_variable(const char *var, const char *value)
/* Static: the parser publishes it as config_file_name. */
{
- static char path[PATH_MAX];
+ static char path[PATH_MAX] __guarded_by(&config_update_mutex);
char *user_config = mkpath(path, sizeof(path), "%s/.perfconfig", getenv("HOME"));
config_filename = config_exclusive_filename ?: user_config;
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 50993b0dedc5..9e8e5d1d822a 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -302,6 +302,7 @@ void perf_env__exit(struct perf_env *env)
}
void perf_env__init(struct perf_env *env)
+ __context_unsafe(/* initialization */)
{
memset(env, 0, sizeof(*env));
#ifdef HAVE_LIBBPF_SUPPORT
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 23d35425efe3..6b2083e3640c 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -139,16 +139,16 @@ struct perf_env {
*/
struct {
struct rw_semaphore lock;
- struct rb_root infos;
- u32 infos_cnt;
- struct rb_root btfs;
- u32 btfs_cnt;
+ struct rb_root infos __guarded_by(&lock);
+ u32 infos_cnt __guarded_by(&lock);
+ struct rb_root btfs __guarded_by(&lock);
+ u32 btfs_cnt __guarded_by(&lock);
} bpf_progs;
#endif // HAVE_LIBBPF_SUPPORT
/* same reason as above (for perf-top) */
struct {
struct rw_semaphore lock;
- struct rb_root tree;
+ struct rb_root tree __guarded_by(&lock);
} cgroups;
/* For fast cpu to numa node lookup via perf_env__numa_node */
@@ -223,7 +223,8 @@ int perf_env__nr_cpus_avail(struct perf_env *env);
void perf_env__init(struct perf_env *env);
#ifdef HAVE_LIBBPF_SUPPORT
bool __perf_env__insert_bpf_prog_info(struct perf_env *env,
- struct bpf_prog_info_node *info_node);
+ struct bpf_prog_info_node *info_node)
+ __must_hold(&env->bpf_progs.lock);
bool perf_env__insert_bpf_prog_info(struct perf_env *env,
struct bpf_prog_info_node *info_node);
struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env,
@@ -233,9 +234,11 @@ void perf_env__iterate_bpf_prog_info(struct perf_env *env,
void *data),
void *data);
bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node);
-bool __perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node);
+bool __perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node)
+ __must_hold(&env->bpf_progs.lock);
struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id);
-struct btf_node *__perf_env__find_btf(struct perf_env *env, __u32 btf_id);
+struct btf_node *__perf_env__find_btf(struct perf_env *env, __u32 btf_id)
+ __must_hold_shared(&env->bpf_progs.lock);
#endif // HAVE_LIBBPF_SUPPORT
int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu);
diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c
index 1812917bfb85..fb3127aa83ce 100644
--- a/tools/perf/util/intel-tpebs.c
+++ b/tools/perf/util/intel-tpebs.c
@@ -39,11 +39,11 @@
bool tpebs_recording;
enum tpebs_mode tpebs_mode;
static DEFINE_MUTEX(tpebs_mtx);
-static LIST_HEAD(tpebs_results);
-static pthread_t tpebs_reader_thread;
+static struct list_head tpebs_results __guarded_by(&tpebs_mtx) = LIST_HEAD_INIT(tpebs_results);
+static pthread_t tpebs_reader_thread __guarded_by(&tpebs_mtx);
static struct child_process tpebs_cmd;
static int control_fd[2] = { -1, -1 }, ack_fd[2] = { -1, -1 };
-static bool tpebs_stopping;
+static bool tpebs_stopping __guarded_by(&tpebs_mtx);
struct tpebs_retire_lat {
struct list_head nd;
@@ -63,6 +63,7 @@ static struct tpebs_retire_lat *tpebs_retire_lat__find(struct evsel *evsel)
__must_hold(&tpebs_mtx);
static int evsel__tpebs_start_perf_record(struct evsel *evsel)
+ __must_hold(&tpebs_mtx)
{
const char **record_argv;
int tpebs_event_size = 0, i = 0, ret;
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v9 18/19] perf maps: Fix lock inversion in maps__load_first() and bounds check in __maps__remove()
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (16 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 17/19] perf config, intel-tpebs, env: Add __guarded_by and __must_hold annotations Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:38 ` sashiko-bot
2026-11-04 20:59 ` [PATCH v9 19/19] perf maps: Add context analysis annotations Ian Rogers
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
In maps__load_first(), take a reference to the first map and drop
maps__lock(maps) before calling map__load() to avoid a read-to-write
self-deadlock when loading kernel maps inserts extra trampoline maps.
In __maps__remove(), check that the index returned by
maps__by_address_index() and maps__by_name_index() is within nr_maps
before indexing and removing the entry, and remove from maps_by_name
before calling map__put(maps_by_address[address_idx]) so that callers
such as maps__remove_maps() that pass maps_by_address[i] as map do not
use a freed handle in maps__by_name_index() under REFCNT_CHECKING.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/maps.c | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index f808df2fe77b..e947506c60a5 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <stdlib.h>
+#include <string.h>
#include <linux/zalloc.h>
#include "debug.h"
#include "dso.h"
@@ -548,20 +549,27 @@ static void __maps__remove(struct maps *maps, struct map *map)
/* Slide later mappings over the one to remove */
address_idx = maps__by_address_index(maps, map);
- map__put(maps_by_address[address_idx]);
- memmove(&maps_by_address[address_idx],
- &maps_by_address[address_idx + 1],
- (nr_maps - address_idx - 1) * sizeof(*maps_by_address));
+ if (address_idx >= nr_maps)
+ return;
if (maps_by_name) {
unsigned int name_idx = maps__by_name_index(maps, map);
- map__put(maps_by_name[name_idx]);
- memmove(&maps_by_name[name_idx],
- &maps_by_name[name_idx + 1],
- (nr_maps - name_idx - 1) * sizeof(*maps_by_name));
+ if (name_idx < nr_maps) {
+ map__put(maps_by_name[name_idx]);
+ memmove(&maps_by_name[name_idx],
+ &maps_by_name[name_idx + 1],
+ (nr_maps - name_idx - 1) * sizeof(*maps_by_name));
+ } else {
+ __maps__free_maps_by_name(maps);
+ }
}
+ map__put(maps_by_address[address_idx]);
+ memmove(&maps_by_address[address_idx],
+ &maps_by_address[address_idx + 1],
+ (nr_maps - address_idx - 1) * sizeof(*maps_by_address));
+
--RC_CHK_ACCESS(maps)->nr_maps;
}
@@ -1459,10 +1467,15 @@ int maps__merge_in(struct maps *kmaps, struct map *new_map)
void maps__load_first(struct maps *maps)
{
- down_read(maps__lock(maps));
+ struct map *first = NULL;
+ down_read(maps__lock(maps));
if (maps__nr_maps(maps) > 0)
- map__load(maps__maps_by_address(maps)[0]);
-
+ first = map__get(maps__maps_by_address(maps)[0]);
up_read(maps__lock(maps));
+
+ if (first) {
+ map__load(first);
+ map__put(first);
+ }
}
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [PATCH v9 18/19] perf maps: Fix lock inversion in maps__load_first() and bounds check in __maps__remove()
2026-11-04 20:59 ` [PATCH v9 18/19] perf maps: Fix lock inversion in maps__load_first() and bounds check in __maps__remove() Ian Rogers
@ 2026-11-04 21:38 ` sashiko-bot
0 siblings, 0 replies; 39+ messages in thread
From: sashiko-bot @ 2026-11-04 21:38 UTC (permalink / raw)
To: Ian Rogers; +Cc: linux-perf-users
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] perf maps: infinite loop in maps__remove_maps() on lookup failure
--
commit 0a54f1b25f8fdf637e12b3625e802bfd90f83a0e
Author: Ian Rogers <irogers@google.com>
perf maps: Fix lock inversion in maps__load_first() and bounds check in __maps__remove()
This commit fixes a lock inversion in maps__load_first() by taking a reference
to the first map and releasing the read lock before calling map__load(). It
also adds bounds checks and reorders reference releases in __maps__remove().
> diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
> index f808df2fe77b4..e947506c60a54 100644
> --- a/tools/perf/util/maps.c
> +++ b/tools/perf/util/maps.c
> @@ -1,6 +1,7 @@
> // SPDX-License-Identifier: GPL-2.0
> #include <errno.h>
> #include <stdlib.h>
> +#include <string.h>
> #include <linux/zalloc.h>
> #include "debug.h"
> #include "dso.h"
> @@ -548,20 +549,27 @@ static void __maps__remove(struct maps *maps, struct map *map)
>
> /* Slide later mappings over the one to remove */
> address_idx = maps__by_address_index(maps, map);
> - map__put(maps_by_address[address_idx]);
> - memmove(&maps_by_address[address_idx],
> - &maps_by_address[address_idx + 1],
> - (nr_maps - address_idx - 1) * sizeof(*maps_by_address));
> + if (address_idx >= nr_maps)
> + return;
[Severity: Medium]
Can returning early here cause an infinite loop in maps__remove_maps() while
holding the maps write lock?
In tools/perf/util/maps.c, maps__remove_maps() iterates over maps_by_address
and relies on __maps__remove() to shift subsequent entries down and decrement
nr_maps instead of incrementing i:
tools/perf/util/maps.c:maps__remove_maps() {
...
for (unsigned int i = 0; i < maps__nr_maps(maps);) {
if (cb(maps_by_address[i], data)) {
__maps__remove(maps, maps_by_address[i]);
removed = true;
} else {
i++;
}
}
...
}
However, in maps__by_address_index(), if maps_by_address_sorted is true, it
only checks bsearch() with map__start_cmp() and does not fall back to the
linear pointer scan if bsearch() returns NULL:
tools/perf/util/maps.c:maps__by_address_index() {
...
if (maps__maps_by_address_sorted(maps)) {
struct map **mapp =
bsearch(&map, maps__maps_by_address(maps), maps__nr_maps(maps),
sizeof(*mapp), map__start_cmp);
if (mapp)
return mapp - maps_by_address;
} else {
for (unsigned int i = 0; i < nr_maps; i++) {
if (RC_CHK_EQUAL(maps_by_address[i], map))
return i;
}
}
pr_err("Map missing from maps");
return -1;
}
When a map's start or end address is updated in place without clearing
maps_by_address_sorted (for instance in machine__create_kernel_maps() where
maps__find_next_entry() sorts kmaps while vmlinux_map has end == ~0ULL before
machine__set_kernel_mmap() and maps__fixup_end() adjust map end addresses, or
in machine__create_module() and machine__process_kernel_mmap_event()),
bsearch() can fail to find maps_by_address[i] and maps__by_address_index()
returns -1 (UINT_MAX).
When dso__load_kcore() then calls maps__remove_maps(kmaps, remove_old_maps,
map), __maps__remove() will return early on address_idx >= nr_maps without
removing maps_by_address[i] or decrementing nr_maps, causing
maps__remove_maps() to repeat the same iteration i indefinitely.
Should maps__by_address_index() (and maps__by_name_index()) fall back to the
linear scan when bsearch() fails?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261104205909.2078411-1-irogers@google.com?part=18
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v9 19/19] perf maps: Add context analysis annotations
2026-11-04 20:58 [PATCH v9 00/19] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
` (17 preceding siblings ...)
2026-11-04 20:59 ` [PATCH v9 18/19] perf maps: Fix lock inversion in maps__load_first() and bounds check in __maps__remove() Ian Rogers
@ 2026-11-04 20:59 ` Ian Rogers
2026-11-04 21:35 ` sashiko-bot
18 siblings, 1 reply; 39+ messages in thread
From: Ian Rogers @ 2026-11-04 20:59 UTC (permalink / raw)
To: irogers, acme, namhyung
Cc: adrian.hunter, bvanassche, elver, james.clark, jolsa, justinstitt,
linux-kernel, linux-perf-users, llvm, mingo, morbo, nathan,
ndesaulniers, peterz
Add __returns_ctx_lock() to maps__lock(), annotate DECLARE_RC_STRUCT(maps)
members protected by lock with __guarded_by(&lock), and annotate
internal maps helpers with __must_hold() and __must_hold_shared().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/maps.c | 122 ++++++++++++++++++++++++++---------------
1 file changed, 78 insertions(+), 44 deletions(-)
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index e947506c60a5..1c9e8e22793d 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -31,12 +31,12 @@ DECLARE_RC_STRUCT(maps) {
* @maps_by_address: array of maps sorted by their starting address if
* maps_by_address_sorted is true.
*/
- struct map **maps_by_address;
+ struct map **maps_by_address __guarded_by(&lock);
/**
* @maps_by_name: optional array of maps sorted by their dso name if
* maps_by_name_sorted is true.
*/
- struct map **maps_by_name;
+ struct map **maps_by_name __guarded_by(&lock);
struct machine *machine;
#ifdef HAVE_LIBUNWIND_SUPPORT
void *addr_space;
@@ -51,26 +51,33 @@ DECLARE_RC_STRUCT(maps) {
* @nr_maps: number of maps_by_address, and possibly maps_by_name,
* entries that contain maps.
*/
- unsigned int nr_maps;
+ unsigned int nr_maps __guarded_by(&lock);
/**
* @nr_maps_allocated: number of entries in maps_by_address and possibly
* maps_by_name.
*/
- unsigned int nr_maps_allocated;
+ unsigned int nr_maps_allocated __guarded_by(&lock);
/**
* @last_search_by_name_idx: cache of last found by name entry's index
* as frequent searches for the same dso name are common.
*/
unsigned int last_search_by_name_idx;
/** @maps_by_address_sorted: is maps_by_address sorted. */
- bool maps_by_address_sorted;
+ bool maps_by_address_sorted __guarded_by(&lock);
/** @maps_by_name_sorted: is maps_by_name sorted. */
- bool maps_by_name_sorted;
+ bool maps_by_name_sorted __guarded_by(&lock);
/** @ends_broken: does the map contain a map where end values are unset/unsorted? */
- bool ends_broken;
+ bool ends_broken __guarded_by(&lock);
};
+static struct rw_semaphore *maps__lock(const struct maps *maps)
+ __returns_ctx_lock(&RC_CHK_ACCESS(maps)->lock)
+{
+ return (struct rw_semaphore *)&RC_CHK_ACCESS(maps)->lock;
+}
+
static void check_invariants(const struct maps *maps __maybe_unused)
+ __must_hold_shared(maps__lock(maps))
{
#ifndef NDEBUG
assert(RC_CHK_ACCESS(maps)->nr_maps <= RC_CHK_ACCESS(maps)->nr_maps_allocated);
@@ -120,55 +127,65 @@ static void check_invariants(const struct maps *maps __maybe_unused)
}
static struct map **maps__maps_by_address(const struct maps *maps)
+ __must_hold_shared(maps__lock(maps))
{
return RC_CHK_ACCESS(maps)->maps_by_address;
}
static void maps__set_maps_by_address(struct maps *maps, struct map **new)
+ __must_hold(maps__lock(maps))
{
RC_CHK_ACCESS(maps)->maps_by_address = new;
}
static void maps__set_nr_maps_allocated(struct maps *maps, unsigned int nr_maps_allocated)
+ __must_hold(maps__lock(maps))
{
RC_CHK_ACCESS(maps)->nr_maps_allocated = nr_maps_allocated;
}
static void maps__set_nr_maps(struct maps *maps, unsigned int nr_maps)
+ __must_hold(maps__lock(maps))
{
RC_CHK_ACCESS(maps)->nr_maps = nr_maps;
}
/* Not in the header, to aid reference counting. */
static struct map **maps__maps_by_name(const struct maps *maps)
+ __must_hold_shared(maps__lock(maps))
{
return RC_CHK_ACCESS(maps)->maps_by_name;
}
static void maps__set_maps_by_name(struct maps *maps, struct map **new)
+ __must_hold(maps__lock(maps))
{
RC_CHK_ACCESS(maps)->maps_by_name = new;
}
static bool maps__maps_by_address_sorted(const struct maps *maps)
+ __must_hold_shared(maps__lock(maps))
{
return RC_CHK_ACCESS(maps)->maps_by_address_sorted;
}
static void maps__set_maps_by_address_sorted(struct maps *maps, bool value)
+ __must_hold(maps__lock(maps))
{
RC_CHK_ACCESS(maps)->maps_by_address_sorted = value;
}
static bool maps__maps_by_name_sorted(const struct maps *maps)
+ __must_hold_shared(maps__lock(maps))
{
return RC_CHK_ACCESS(maps)->maps_by_name_sorted;
}
static void maps__set_maps_by_name_sorted(struct maps *maps, bool value)
+ __must_hold(maps__lock(maps))
{
RC_CHK_ACCESS(maps)->maps_by_name_sorted = value;
}
@@ -178,11 +195,22 @@ struct machine *maps__machine(const struct maps *maps)
return RC_CHK_ACCESS(maps)->machine;
}
-unsigned int maps__nr_maps(const struct maps *maps)
+static unsigned int __maps__nr_maps(const struct maps *maps)
+ __must_hold_shared(maps__lock(maps))
{
return RC_CHK_ACCESS(maps)->nr_maps;
}
+unsigned int maps__nr_maps(const struct maps *maps)
+{
+ unsigned int nr;
+
+ down_read(maps__lock(maps));
+ nr = __maps__nr_maps(maps);
+ up_read(maps__lock(maps));
+ return nr;
+}
+
refcount_t *maps__refcnt(struct maps *maps)
{
return &RC_CHK_ACCESS(maps)->refcnt;
@@ -221,12 +249,8 @@ void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl)
}
#endif
-static struct rw_semaphore *maps__lock(struct maps *maps)
-{
- return &RC_CHK_ACCESS(maps)->lock;
-}
-
static void maps__init(struct maps *maps, struct machine *machine)
+ __context_unsafe(/* initialization */)
{
init_rwsem(maps__lock(maps));
RC_CHK_ACCESS(maps)->maps_by_address = NULL;
@@ -248,11 +272,12 @@ static void maps__init(struct maps *maps, struct machine *machine)
}
static void maps__exit(struct maps *maps)
+ __context_unsafe(/* teardown */)
{
struct map **maps_by_address = maps__maps_by_address(maps);
struct map **maps_by_name = maps__maps_by_name(maps);
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
+ for (unsigned int i = 0; i < __maps__nr_maps(maps); i++) {
map__zput(maps_by_address[i]);
if (maps_by_name)
map__zput(maps_by_name[i]);
@@ -301,6 +326,7 @@ void maps__put(struct maps *maps)
}
static void __maps__free_maps_by_name(struct maps *maps)
+ __must_hold(maps__lock(maps))
{
if (!maps__maps_by_name(maps))
return;
@@ -308,7 +334,7 @@ static void __maps__free_maps_by_name(struct maps *maps)
/*
* Free everything to try to do it from the rbtree in the next search
*/
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++)
+ for (unsigned int i = 0; i < __maps__nr_maps(maps); i++)
map__put(maps__maps_by_name(maps)[i]);
zfree(&RC_CHK_ACCESS(maps)->maps_by_name);
@@ -341,12 +367,13 @@ static int map__start_cmp(const void *a, const void *b)
}
static void __maps__sort_by_address(struct maps *maps)
+ __must_hold(maps__lock(maps))
{
if (maps__maps_by_address_sorted(maps))
return;
qsort(maps__maps_by_address(maps),
- maps__nr_maps(maps),
+ __maps__nr_maps(maps),
sizeof(struct map *),
map__start_cmp);
maps__set_maps_by_address_sorted(maps, true);
@@ -389,7 +416,7 @@ static int maps__sort_by_name(struct maps *maps)
err = -ENOMEM;
else {
struct map **maps_by_address = maps__maps_by_address(maps);
- unsigned int n = maps__nr_maps(maps);
+ unsigned int n = __maps__nr_maps(maps);
maps__set_maps_by_name(maps, maps_by_name);
for (unsigned int i = 0; i < n; i++)
@@ -398,7 +425,7 @@ static int maps__sort_by_name(struct maps *maps)
}
if (!err) {
qsort(maps_by_name,
- maps__nr_maps(maps),
+ __maps__nr_maps(maps),
sizeof(struct map *),
map__strcmp);
maps__set_maps_by_name_sorted(maps, true);
@@ -410,18 +437,19 @@ static int maps__sort_by_name(struct maps *maps)
}
static unsigned int maps__by_address_index(const struct maps *maps, const struct map *map)
+ __must_hold_shared(maps__lock(maps))
{
struct map **maps_by_address = maps__maps_by_address(maps);
if (maps__maps_by_address_sorted(maps)) {
struct map **mapp =
- bsearch(&map, maps__maps_by_address(maps), maps__nr_maps(maps),
+ bsearch(&map, maps__maps_by_address(maps), __maps__nr_maps(maps),
sizeof(*mapp), map__start_cmp);
if (mapp)
return mapp - maps_by_address;
} else {
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
+ for (unsigned int i = 0; i < __maps__nr_maps(maps); i++) {
if (RC_CHK_ACCESS(maps_by_address[i]) == RC_CHK_ACCESS(map))
return i;
}
@@ -431,18 +459,19 @@ static unsigned int maps__by_address_index(const struct maps *maps, const struct
}
static unsigned int maps__by_name_index(const struct maps *maps, const struct map *map)
+ __must_hold_shared(maps__lock(maps))
{
struct map **maps_by_name = maps__maps_by_name(maps);
if (maps__maps_by_name_sorted(maps)) {
struct map **mapp =
- bsearch(&map, maps_by_name, maps__nr_maps(maps),
+ bsearch(&map, maps_by_name, __maps__nr_maps(maps),
sizeof(*mapp), map__strcmp);
if (mapp)
return mapp - maps_by_name;
} else {
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
+ for (unsigned int i = 0; i < __maps__nr_maps(maps); i++) {
if (RC_CHK_ACCESS(maps_by_name[i]) == RC_CHK_ACCESS(map))
return i;
}
@@ -471,10 +500,11 @@ static void map__set_kmap_maps(struct map *map, struct maps *maps)
}
static int __maps__insert(struct maps *maps, struct map *new)
+ __must_hold(maps__lock(maps))
{
struct map **maps_by_address = maps__maps_by_address(maps);
struct map **maps_by_name = maps__maps_by_name(maps);
- unsigned int nr_maps = maps__nr_maps(maps);
+ unsigned int nr_maps = __maps__nr_maps(maps);
unsigned int nr_allocate = RC_CHK_ACCESS(maps)->nr_maps_allocated;
if (nr_maps + 1 > nr_allocate) {
@@ -541,10 +571,11 @@ int maps__insert(struct maps *maps, struct map *map)
}
static void __maps__remove(struct maps *maps, struct map *map)
+ __must_hold(maps__lock(maps))
{
struct map **maps_by_address = maps__maps_by_address(maps);
struct map **maps_by_name = maps__maps_by_name(maps);
- unsigned int nr_maps = maps__nr_maps(maps);
+ unsigned int nr_maps = __maps__nr_maps(maps);
unsigned int address_idx;
/* Slide later mappings over the one to remove */
@@ -631,7 +662,7 @@ bool maps__empty(struct maps *maps)
bool res;
down_read(maps__lock(maps));
- res = maps__nr_maps(maps) == 0;
+ res = __maps__nr_maps(maps) == 0;
up_read(maps__lock(maps));
return res;
@@ -659,7 +690,7 @@ int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data)
* the array - this may cause maps to be skipped or
* repeated.
*/
- for (unsigned int i = 0; i < maps__nr_maps(maps); i++) {
+ for (unsigned int i = 0; i < __maps__nr_maps(maps); i++) {
struct map **maps_by_address = maps__maps_by_address(maps);
struct map *map = maps_by_address[i];
@@ -686,7 +717,7 @@ int maps__load_maps(struct maps *maps)
return 0;
down_read(maps__lock(maps));
- nr_maps = maps__nr_maps(maps);
+ nr_maps = __maps__nr_maps(maps);
if (nr_maps == 0) {
up_read(maps__lock(maps));
return 0;
@@ -719,7 +750,7 @@ void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data
down_write(maps__lock(maps));
maps_by_address = maps__maps_by_address(maps);
- for (unsigned int i = 0; i < maps__nr_maps(maps);) {
+ for (unsigned int i = 0; i < __maps__nr_maps(maps);) {
if (cb(maps_by_address[i], data)) {
__maps__remove(maps, maps_by_address[i]);
removed = true;
@@ -773,7 +804,7 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, st
* This prevents deadlocking if iteration triggers further map insertions.
*/
down_read(maps__lock(maps));
- nr_maps = maps__nr_maps(maps);
+ nr_maps = __maps__nr_maps(maps);
maps_copy = calloc(nr_maps, sizeof(*maps_copy));
if (maps_copy) {
for (unsigned int i = 0; i < nr_maps; i++) {
@@ -858,9 +889,10 @@ size_t maps__fprintf(struct maps *maps, FILE *fp)
* Same as find_vma() in kernel.
*/
static unsigned int first_ending_after(struct maps *maps, const struct map *map)
+ __must_hold_shared(maps__lock(maps))
{
struct map **maps_by_address = maps__maps_by_address(maps);
- int low = 0, high = (int)maps__nr_maps(maps) - 1, first = high + 1;
+ int low = 0, high = (int)__maps__nr_maps(maps) - 1, first = high + 1;
assert(maps__maps_by_address_sorted(maps));
if (low <= high && map__end(maps_by_address[0]) > map__start(map))
@@ -885,10 +917,11 @@ static unsigned int first_ending_after(struct maps *maps, const struct map *map)
static int __maps__insert_sorted(struct maps *maps, unsigned int first_after_index,
struct map *new1, struct map *new2)
+ __must_hold(maps__lock(maps))
{
struct map **maps_by_address = maps__maps_by_address(maps);
struct map **maps_by_name = maps__maps_by_name(maps);
- unsigned int nr_maps = maps__nr_maps(maps);
+ unsigned int nr_maps = __maps__nr_maps(maps);
unsigned int nr_allocate = RC_CHK_ACCESS(maps)->nr_maps_allocated;
unsigned int to_add = new2 ? 2 : 1;
@@ -945,6 +978,7 @@ static int __maps__insert_sorted(struct maps *maps, unsigned int first_after_ind
* adjusted or removed so that new fits without overlapping any entries.
*/
static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
+ __must_hold(maps__lock(maps))
{
int err = 0;
unsigned int i, ni = INT_MAX; // Some gcc complain, but depends on maps_by_name...
@@ -956,7 +990,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
* Iterate through entries where the end of the existing entry is
* greater-than the new map's start.
*/
- for (i = first_ending_after(maps, new); i < maps__nr_maps(maps); ) {
+ for (i = first_ending_after(maps, new); i < __maps__nr_maps(maps); ) {
struct map **maps_by_address = maps__maps_by_address(maps);
struct map **maps_by_name = maps__maps_by_name(maps);
struct map *pos = maps_by_address[i];
@@ -1067,7 +1101,7 @@ static int __maps__fixup_overlap_and_insert(struct maps *maps, struct map *new)
return err;
} else {
struct map *next = NULL;
- unsigned int nr_maps = maps__nr_maps(maps);
+ unsigned int nr_maps = __maps__nr_maps(maps);
if (i + 1 < nr_maps)
next = maps_by_address[i + 1];
@@ -1151,8 +1185,8 @@ int maps__copy_from(struct maps *dest, struct maps *parent)
}
#endif
parent_maps_by_address = maps__maps_by_address(parent);
- n = maps__nr_maps(parent);
- if (maps__nr_maps(dest) == 0) {
+ n = __maps__nr_maps(parent);
+ if (__maps__nr_maps(dest) == 0) {
/* No existing mappings so just copy from parent to avoid reallocs in insert. */
unsigned int nr_maps_allocated = RC_CHK_ACCESS(parent)->nr_maps_allocated;
struct map **dest_maps_by_address =
@@ -1236,7 +1270,7 @@ struct map *maps__find(struct maps *maps, u64 ip)
if (maps__maps_by_address_sorted(maps)) {
struct map **mapp = NULL;
struct map **maps_by_address = maps__maps_by_address(maps);
- unsigned int nr_maps = maps__nr_maps(maps);
+ unsigned int nr_maps = __maps__nr_maps(maps);
if (maps_by_address && nr_maps)
mapp = bsearch(&ip, maps_by_address, nr_maps, sizeof(*mapp),
@@ -1272,7 +1306,7 @@ struct map *maps__find_by_name(struct maps *maps, const char *name)
/* First check last found entry. */
i = RC_CHK_ACCESS(maps)->last_search_by_name_idx;
- if (i < maps__nr_maps(maps) && maps__maps_by_name(maps)) {
+ if (i < __maps__nr_maps(maps) && maps__maps_by_name(maps)) {
struct dso *dso = map__dso(maps__maps_by_name(maps)[i]);
if (dso && strcmp(dso__short_name(dso), name) == 0) {
@@ -1284,7 +1318,7 @@ struct map *maps__find_by_name(struct maps *maps, const char *name)
/* Second search sorted array. */
if (!done && maps__maps_by_name_sorted(maps)) {
struct map **mapp =
- bsearch(name, maps__maps_by_name(maps), maps__nr_maps(maps),
+ bsearch(name, maps__maps_by_name(maps), __maps__nr_maps(maps),
sizeof(*mapp), map__strcmp_name);
if (mapp) {
@@ -1307,7 +1341,7 @@ struct map *maps__find_by_name(struct maps *maps, const char *name)
down_read(maps__lock(maps));
maps_by_address = maps__maps_by_address(maps);
- n = maps__nr_maps(maps);
+ n = __maps__nr_maps(maps);
for (i = 0; i < n; i++) {
struct map *pos = maps_by_address[i];
struct dso *dso = map__dso(pos);
@@ -1337,7 +1371,7 @@ struct map *maps__find_next_entry(struct maps *maps, struct map *map)
down_read(maps__lock(maps));
}
i = maps__by_address_index(maps, map);
- if (++i < maps__nr_maps(maps))
+ if (++i < __maps__nr_maps(maps))
result = map__get(maps__maps_by_address(maps)[i]);
up_read(maps__lock(maps));
@@ -1354,7 +1388,7 @@ void maps__fixup_end(struct maps *maps)
__maps__sort_by_address(maps);
maps_by_address = maps__maps_by_address(maps);
- n = maps__nr_maps(maps);
+ n = __maps__nr_maps(maps);
for (unsigned int i = 1; i < n; i++) {
struct map *prev = maps_by_address[i - 1];
struct map *curr = maps_by_address[i];
@@ -1401,7 +1435,7 @@ int maps__merge_in(struct maps *kmaps, struct map *new_map)
first_after_ = first_ending_after(kmaps, new_map);
kmaps_maps_by_address = maps__maps_by_address(kmaps);
- if (first_after_ >= maps__nr_maps(kmaps) ||
+ if (first_after_ >= __maps__nr_maps(kmaps) ||
map__start(kmaps_maps_by_address[first_after_]) >= map__end(new_map)) {
/* No overlap so regular insert suffices. */
up_read(maps__lock(kmaps));
@@ -1416,7 +1450,7 @@ int maps__merge_in(struct maps *kmaps, struct map *new_map)
first_after_ = first_ending_after(kmaps, new_map);
kmaps_maps_by_address = maps__maps_by_address(kmaps);
- kmaps__nr_maps = maps__nr_maps(kmaps);
+ kmaps__nr_maps = __maps__nr_maps(kmaps);
if (first_after_ >= kmaps__nr_maps ||
map__start(kmaps_maps_by_address[first_after_]) >= map__end(new_map)) {
@@ -1470,7 +1504,7 @@ void maps__load_first(struct maps *maps)
struct map *first = NULL;
down_read(maps__lock(maps));
- if (maps__nr_maps(maps) > 0)
+ if (__maps__nr_maps(maps) > 0)
first = map__get(maps__maps_by_address(maps)[0]);
up_read(maps__lock(maps));
--
2.56.0.455.g6c5a503b38-goog
^ permalink raw reply related [flat|nested] 39+ messages in thread