Re: [PATCH tip/locking/core] compiler-context-analysis: Add support for multi-argument guarded_by
From: Marco Elver
Date: Mon Mar 23 2026 - 12:21:49 EST
On Mon, 23 Mar 2026 at 16:33, Marco Elver <elver@xxxxxxxxxx> 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@xxxxxxxxxxxxx>
> Signed-off-by: Marco Elver <elver@xxxxxxxxxx>
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
>