[PATCH tip/locking/core] compiler-context-analysis: Add support for multi-argument guarded_by

Marco Elver posted 1 patch 2 hours ago
include/linux/compiler-context-analysis.h | 66 ++++++++++++++++++++---
init/Kconfig                              |  5 ++
lib/test_context-analysis.c               | 24 +++++++++
3 files changed, 87 insertions(+), 8 deletions(-)
[PATCH tip/locking/core] compiler-context-analysis: Add support for multi-argument guarded_by
Posted by Marco Elver 2 hours ago
Clang 23 introduces support for multiple arguments in the `guarded_by`
and `pt_guarded_by` attributes [1]. This allows defining variables
protected by multiple context locks, where read access requires holding
at least one lock (shared or exclusive), and write access requires
holding all of them exclusively.

To use the feature while maintaining compatibility with Clang 22, add
the `__guarded_by_any()` and `__pt_guarded_by_any()` macros. On Clang 23
and newer, these expand to the underlying attributes; with older Clang
versions, they fall back to a no-op (false negatives possible).

Link: https://github.com/llvm/llvm-project/pull/186838 [1]
Requested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Marco Elver <elver@google.com>
---
 include/linux/compiler-context-analysis.h | 66 ++++++++++++++++++++---
 init/Kconfig                              |  5 ++
 lib/test_context-analysis.c               | 24 +++++++++
 3 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/include/linux/compiler-context-analysis.h b/include/linux/compiler-context-analysis.h
index 00c074a2ccb0..a0d135e500dd 100644
--- a/include/linux/compiler-context-analysis.h
+++ b/include/linux/compiler-context-analysis.h
@@ -39,8 +39,9 @@
 # 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
+ * __guarded_by() - struct member and globals attribute, declares variable
+ *                  only accessible within active context
+ * @x: context lock instance pointer
  *
  * Declares that the struct member or global variable is only accessible within
  * the context entered by the given context lock. Read operations on the data
@@ -53,11 +54,12 @@
  *		long counter __guarded_by(&lock);
  *	};
  */
-# define __guarded_by(...)		__attribute__((guarded_by(__VA_ARGS__)))
+# define __guarded_by(x)		__attribute__((guarded_by(x)))
 
 /**
- * __pt_guarded_by - struct member and globals attribute, declares pointed-to
- *                   data only accessible within active context
+ * __pt_guarded_by() - struct member and globals attribute, declares pointed-to
+ *                     data only accessible within active context
+ * @x: context lock instance pointer
  *
  * 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
@@ -71,7 +73,53 @@
  *		long *counter __pt_guarded_by(&lock);
  *	};
  */
-# define __pt_guarded_by(...)		__attribute__((pt_guarded_by(__VA_ARGS__)))
+# define __pt_guarded_by(x)		__attribute__((pt_guarded_by(x)))
+
+/**
+ * __guarded_by_any() - struct member and globals attribute, declares variable
+ *                      only accessible within active contexts
+ * @...: context lock instance pointers
+ *
+ * Declares that the struct member or global variable is protected by multiple
+ * context locks. Write access requires all listed context locks to be held
+ * exclusively; read access requires at least one of them to be held (shared or
+ * exclusive).
+ *
+ * .. code-block:: c
+ *
+ *	struct some_state {
+ *		spinlock_t lock1, lock2;
+ *		long counter __guarded_by_any(&lock1, &lock2);
+ *	};
+ */
+# ifdef CONFIG_CC_HAS_MULTI_ARG_GUARDED_BY_ATTR
+#  define __guarded_by_any(...)		__attribute__((guarded_by(__VA_ARGS__)))
+# else
+#  define __guarded_by_any(...)
+# endif
+
+/**
+ * __pt_guarded_by_any() - struct member and globals attribute, declares pointed-to
+ *                         data only accessible within active contexts
+ * @...: context lock instance pointers
+ *
+ * Declares that the data pointed to by the struct member pointer or global
+ * pointer is protected by multiple context locks. Write access requires all
+ * listed context locks to be held exclusively; read access requires at least
+ * one of them to be held (shared or exclusive).
+ *
+ * .. code-block:: c
+ *
+ *	struct some_state {
+ *		spinlock_t lock1, lock2;
+ *		long *counter __pt_guarded_by_any(&lock1, &lock2);
+ *	};
+ */
+# ifdef CONFIG_CC_HAS_MULTI_ARG_GUARDED_BY_ATTR
+#  define __pt_guarded_by_any(...)		__attribute__((pt_guarded_by(__VA_ARGS__)))
+# else
+#  define __pt_guarded_by_any(...)
+# endif
 
 /**
  * context_lock_struct() - declare or define a context lock struct
@@ -158,8 +206,10 @@
 # define __assumes_ctx_lock(...)
 # define __assumes_shared_ctx_lock(...)
 # define __returns_ctx_lock(var)
-# define __guarded_by(...)
-# define __pt_guarded_by(...)
+# define __guarded_by(x)
+# define __pt_guarded_by(x)
+# define __guarded_by_any(...)
+# define __pt_guarded_by_any(...)
 # define __excludes_ctx_lock(...)
 # define __requires_ctx_lock(...)
 # define __requires_shared_ctx_lock(...)
diff --git a/init/Kconfig b/init/Kconfig
index 444ce811ea67..9f9a800822ff 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -158,6 +158,11 @@ config CC_HAS_BROKEN_COUNTED_BY_REF
 config CC_HAS_MULTIDIMENSIONAL_NONSTRING
 	def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
 
+config CC_HAS_MULTI_ARG_GUARDED_BY_ATTR
+	# supported since clang 23
+	depends on CC_IS_CLANG
+	def_bool $(success,echo 'typedef int __attribute__((capability("l"))) L; L l1; L l2; int __attribute__((guarded_by(&l1, &l2))) x;' | $(CC) -x c - -c -o /dev/null -Werror)
+
 config LD_CAN_USE_KEEP_IN_OVERLAY
 	# ld.lld prior to 21.0.0 did not support KEEP within an overlay description
 	# https://github.com/llvm/llvm-project/pull/130661
diff --git a/lib/test_context-analysis.c b/lib/test_context-analysis.c
index 06b4a6a028e0..691fb2d6fc45 100644
--- a/lib/test_context-analysis.c
+++ b/lib/test_context-analysis.c
@@ -159,6 +159,10 @@ TEST_SPINLOCK_COMMON(read_lock,
 struct test_mutex_data {
 	struct mutex mtx;
 	int counter __guarded_by(&mtx);
+
+	struct mutex mtx2;
+	int anyread __guarded_by_any(&mtx, &mtx2);
+	int *anyptr __pt_guarded_by_any(&mtx, &mtx2);
 };
 
 static void __used test_mutex_init(struct test_mutex_data *d)
@@ -219,6 +223,26 @@ static void __used test_mutex_cond_guard(struct test_mutex_data *d)
 	}
 }
 
+static void __used test_mutex_multiguard(struct test_mutex_data *d)
+{
+	mutex_lock(&d->mtx);
+	(void)d->anyread;
+	(void)*d->anyptr;
+	mutex_unlock(&d->mtx);
+
+	mutex_lock(&d->mtx2);
+	(void)d->anyread;
+	(void)*d->anyptr;
+	mutex_unlock(&d->mtx2);
+
+	mutex_lock(&d->mtx);
+	mutex_lock(&d->mtx2);
+	d->anyread++;
+	(*d->anyptr)++;
+	mutex_unlock(&d->mtx2);
+	mutex_unlock(&d->mtx);
+}
+
 struct test_seqlock_data {
 	seqlock_t sl;
 	int counter __guarded_by(&sl);
-- 
2.53.0.1018.g2bb0e51243-goog
Re: [PATCH tip/locking/core] compiler-context-analysis: Add support for multi-argument guarded_by
Posted by Marco Elver 2 hours ago
On Mon, 23 Mar 2026 at 16:33, Marco Elver <elver@google.com> wrote:
>
> Clang 23 introduces support for multiple arguments in the `guarded_by`
> and `pt_guarded_by` attributes [1]. This allows defining variables
> protected by multiple context locks, where read access requires holding
> at least one lock (shared or exclusive), and write access requires
> holding all of them exclusively.
>
> To use the feature while maintaining compatibility with Clang 22, add
> the `__guarded_by_any()` and `__pt_guarded_by_any()` macros. On Clang 23
> and newer, these expand to the underlying attributes; with older Clang
> versions, they fall back to a no-op (false negatives possible).
>
> Link: https://github.com/llvm/llvm-project/pull/186838 [1]
> Requested-by: Peter Zijlstra <peterz@infradead.org>
> Signed-off-by: Marco Elver <elver@google.com>

If we want to retain compatibility with Clang 22, we need
__guarded_by_any, which ultimately maps to 'guarded_by' as well if we
have Clang 23+.

The alternative would be to wait a few weeks and then require Context
Analysis to have a Clang 23 compiler. That'd avoid adding the new
__guarded_by_any variant and we can just use __guarded_by as-is.

We likely want to do that when Clang 23.1 is released anyway, because
there were some other fixes (arrays of lock fix:
https://github.com/llvm/llvm-project/pull/148551).

Preferences?

> ---
>  include/linux/compiler-context-analysis.h | 66 ++++++++++++++++++++---
>  init/Kconfig                              |  5 ++
>  lib/test_context-analysis.c               | 24 +++++++++
>  3 files changed, 87 insertions(+), 8 deletions(-)
>
> diff --git a/include/linux/compiler-context-analysis.h b/include/linux/compiler-context-analysis.h
> index 00c074a2ccb0..a0d135e500dd 100644
> --- a/include/linux/compiler-context-analysis.h
> +++ b/include/linux/compiler-context-analysis.h
> @@ -39,8 +39,9 @@
>  # 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
> + * __guarded_by() - struct member and globals attribute, declares variable
> + *                  only accessible within active context
> + * @x: context lock instance pointer
>   *
>   * Declares that the struct member or global variable is only accessible within
>   * the context entered by the given context lock. Read operations on the data
> @@ -53,11 +54,12 @@
>   *             long counter __guarded_by(&lock);
>   *     };
>   */
> -# define __guarded_by(...)             __attribute__((guarded_by(__VA_ARGS__)))
> +# define __guarded_by(x)               __attribute__((guarded_by(x)))
>
>  /**
> - * __pt_guarded_by - struct member and globals attribute, declares pointed-to
> - *                   data only accessible within active context
> + * __pt_guarded_by() - struct member and globals attribute, declares pointed-to
> + *                     data only accessible within active context
> + * @x: context lock instance pointer
>   *
>   * 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
> @@ -71,7 +73,53 @@
>   *             long *counter __pt_guarded_by(&lock);
>   *     };
>   */
> -# define __pt_guarded_by(...)          __attribute__((pt_guarded_by(__VA_ARGS__)))
> +# define __pt_guarded_by(x)            __attribute__((pt_guarded_by(x)))
> +
> +/**
> + * __guarded_by_any() - struct member and globals attribute, declares variable
> + *                      only accessible within active contexts
> + * @...: context lock instance pointers
> + *
> + * Declares that the struct member or global variable is protected by multiple
> + * context locks. Write access requires all listed context locks to be held
> + * exclusively; read access requires at least one of them to be held (shared or
> + * exclusive).
> + *
> + * .. code-block:: c
> + *
> + *     struct some_state {
> + *             spinlock_t lock1, lock2;
> + *             long counter __guarded_by_any(&lock1, &lock2);
> + *     };
> + */
> +# ifdef CONFIG_CC_HAS_MULTI_ARG_GUARDED_BY_ATTR
> +#  define __guarded_by_any(...)                __attribute__((guarded_by(__VA_ARGS__)))
> +# else
> +#  define __guarded_by_any(...)
> +# endif
> +
> +/**
> + * __pt_guarded_by_any() - struct member and globals attribute, declares pointed-to
> + *                         data only accessible within active contexts
> + * @...: context lock instance pointers
> + *
> + * Declares that the data pointed to by the struct member pointer or global
> + * pointer is protected by multiple context locks. Write access requires all
> + * listed context locks to be held exclusively; read access requires at least
> + * one of them to be held (shared or exclusive).
> + *
> + * .. code-block:: c
> + *
> + *     struct some_state {
> + *             spinlock_t lock1, lock2;
> + *             long *counter __pt_guarded_by_any(&lock1, &lock2);
> + *     };
> + */
> +# ifdef CONFIG_CC_HAS_MULTI_ARG_GUARDED_BY_ATTR
> +#  define __pt_guarded_by_any(...)             __attribute__((pt_guarded_by(__VA_ARGS__)))
> +# else
> +#  define __pt_guarded_by_any(...)
> +# endif
>
>  /**
>   * context_lock_struct() - declare or define a context lock struct
> @@ -158,8 +206,10 @@
>  # define __assumes_ctx_lock(...)
>  # define __assumes_shared_ctx_lock(...)
>  # define __returns_ctx_lock(var)
> -# define __guarded_by(...)
> -# define __pt_guarded_by(...)
> +# define __guarded_by(x)
> +# define __pt_guarded_by(x)
> +# define __guarded_by_any(...)
> +# define __pt_guarded_by_any(...)
>  # define __excludes_ctx_lock(...)
>  # define __requires_ctx_lock(...)
>  # define __requires_shared_ctx_lock(...)
> diff --git a/init/Kconfig b/init/Kconfig
> index 444ce811ea67..9f9a800822ff 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -158,6 +158,11 @@ config CC_HAS_BROKEN_COUNTED_BY_REF
>  config CC_HAS_MULTIDIMENSIONAL_NONSTRING
>         def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
>
> +config CC_HAS_MULTI_ARG_GUARDED_BY_ATTR
> +       # supported since clang 23
> +       depends on CC_IS_CLANG
> +       def_bool $(success,echo 'typedef int __attribute__((capability("l"))) L; L l1; L l2; int __attribute__((guarded_by(&l1, &l2))) x;' | $(CC) -x c - -c -o /dev/null -Werror)
> +
>  config LD_CAN_USE_KEEP_IN_OVERLAY
>         # ld.lld prior to 21.0.0 did not support KEEP within an overlay description
>         # https://github.com/llvm/llvm-project/pull/130661
> diff --git a/lib/test_context-analysis.c b/lib/test_context-analysis.c
> index 06b4a6a028e0..691fb2d6fc45 100644
> --- a/lib/test_context-analysis.c
> +++ b/lib/test_context-analysis.c
> @@ -159,6 +159,10 @@ TEST_SPINLOCK_COMMON(read_lock,
>  struct test_mutex_data {
>         struct mutex mtx;
>         int counter __guarded_by(&mtx);
> +
> +       struct mutex mtx2;
> +       int anyread __guarded_by_any(&mtx, &mtx2);
> +       int *anyptr __pt_guarded_by_any(&mtx, &mtx2);
>  };
>
>  static void __used test_mutex_init(struct test_mutex_data *d)
> @@ -219,6 +223,26 @@ static void __used test_mutex_cond_guard(struct test_mutex_data *d)
>         }
>  }
>
> +static void __used test_mutex_multiguard(struct test_mutex_data *d)
> +{
> +       mutex_lock(&d->mtx);
> +       (void)d->anyread;
> +       (void)*d->anyptr;
> +       mutex_unlock(&d->mtx);
> +
> +       mutex_lock(&d->mtx2);
> +       (void)d->anyread;
> +       (void)*d->anyptr;
> +       mutex_unlock(&d->mtx2);
> +
> +       mutex_lock(&d->mtx);
> +       mutex_lock(&d->mtx2);
> +       d->anyread++;
> +       (*d->anyptr)++;
> +       mutex_unlock(&d->mtx2);
> +       mutex_unlock(&d->mtx);
> +}
> +
>  struct test_seqlock_data {
>         seqlock_t sl;
>         int counter __guarded_by(&sl);
> --
> 2.53.0.1018.g2bb0e51243-goog
>