Hai
This is on top of tip/locking/core with these patches on:
https://lkml.kernel.org/r/20260119094029.1344361-1-elver@google.com
and converts mutex, rtmutex, ww_mutex and futex to use the new context analysis
bits.
There is one snafu:
ww_mutex_set_context_fastpath()'s data_race() usage doesn't stop the compiler
from complaining when building a defconfig+PREEMPT_RT+LOCKDEP build:
../kernel/locking/ww_mutex.h:439:24: error: calling function '__ww_mutex_has_waiters' requires holding raw_spinlock 'lock->base.rtmutex.wait_lock' exclusively [-Werror,-Wthread-safety-analysis]
439 | if (likely(!data_race(__ww_mutex_has_waiters(&lock->base))))
| ^
1 error generated.
On Wed, Jan 21, 2026 at 12:07:04PM +0100, Peter Zijlstra wrote: > Hai > > This is on top of tip/locking/core with these patches on: > > https://lkml.kernel.org/r/20260119094029.1344361-1-elver@google.com > > and converts mutex, rtmutex, ww_mutex and futex to use the new context analysis > bits. > Pushed out an updated/fixed series to: git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git locking/core
On 2026-01-21 20:23:49 [+0100], Peter Zijlstra wrote: > On Wed, Jan 21, 2026 at 12:07:04PM +0100, Peter Zijlstra wrote: > > Hai > > > > This is on top of tip/locking/core with these patches on: > > > > https://lkml.kernel.org/r/20260119094029.1344361-1-elver@google.com > > > > and converts mutex, rtmutex, ww_mutex and futex to use the new context analysis > > bits. > > > > Pushed out an updated/fixed series to: > > git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git locking/core I went through what you have there now and it looks good and I did not find anything while testing. Full ACK. Sebastian
On 1/21/26 11:23 AM, Peter Zijlstra wrote:
> Pushed out an updated/fixed series to:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git locking/core
With CONFIG_WARN_CONTEXT_ANALYSIS=y, CONFIG_WARN_CONTEXT_ANALYSIS_ALL=n
and "+src:*include/*=emit" in scripts/context-analysis-suppression.txt
I see the following error messages for that tree:
In file included from kernel/locking/mutex.c:22:
In file included from ./include/linux/ww_mutex.h:21:
./include/linux/rtmutex.h:44:25: error: reading variable 'owner'
requires holding raw_spinlock '&rt_mutex_base::wait_lock'
[-Werror,-Wthread-safety-analysis]
44 | return READ_ONCE(lock->owner) != NULL;
| ^
./include/linux/rtmutex.h:52:56: error: reading variable 'owner'
requires holding raw_spinlock '&rt_mutex_base::wait_lock'
[-Werror,-Wthread-safety-analysis]
52 | unsigned long owner = (unsigned long)
READ_ONCE(lock->owner);
| ^
2 errors generated.
Should this series perhaps include changes for the file
scripts/context-analysis-suppression.txt?
Thanks,
Bart.
On Wed, Jan 21, 2026 at 12:37:21PM -0800, Bart Van Assche wrote: > On 1/21/26 11:23 AM, Peter Zijlstra wrote: > > Pushed out an updated/fixed series to: > > > > git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git locking/core > > With CONFIG_WARN_CONTEXT_ANALYSIS=y, CONFIG_WARN_CONTEXT_ANALYSIS_ALL=n > and "+src:*include/*=emit" in scripts/context-analysis-suppression.txt > I see the following error messages for that tree: > > In file included from kernel/locking/mutex.c:22: > In file included from ./include/linux/ww_mutex.h:21: > ./include/linux/rtmutex.h:44:25: error: reading variable 'owner' requires > holding raw_spinlock '&rt_mutex_base::wait_lock' > [-Werror,-Wthread-safety-analysis] > 44 | return READ_ONCE(lock->owner) != NULL; > | ^ > ./include/linux/rtmutex.h:52:56: error: reading variable 'owner' requires > holding raw_spinlock '&rt_mutex_base::wait_lock' > [-Werror,-Wthread-safety-analysis] > 52 | unsigned long owner = (unsigned long) > READ_ONCE(lock->owner); > | ^ > 2 errors generated. > > Should this series perhaps include changes for the file > scripts/context-analysis-suppression.txt? I'm having trouble reproducing :-( You're speaking of something like the below, on a defconfig build, right? --- diff --git a/scripts/context-analysis-suppression.txt b/scripts/context-analysis-suppression.txt index fd8951d06706..6c31eadd0244 100644 --- a/scripts/context-analysis-suppression.txt +++ b/scripts/context-analysis-suppression.txt @@ -14,6 +14,7 @@ src:*include/linux/* src:*include/net/* # Opt-in headers: +src:*include/*=emit src:*include/linux/bit_spinlock.h=emit src:*include/linux/cleanup.h=emit src:*include/linux/kref.h=emit
On 1/22/26 1:04 AM, Peter Zijlstra wrote:
> I'm having trouble reproducing :-(
Hi Peter,
With this patch applied:
diff --git a/scripts/context-analysis-suppression.txt
b/scripts/context-analysis-suppression.txt
index fd8951d06706..1c51b6153f08 100644
--- a/scripts/context-analysis-suppression.txt
+++ b/scripts/context-analysis-suppression.txt
@@ -24,6 +24,7 @@ src:*include/linux/mutex*.h=emit
src:*include/linux/rcupdate.h=emit
src:*include/linux/refcount.h=emit
src:*include/linux/rhashtable.h=emit
+src:*include/linux/rtmutex*.h=emit
src:*include/linux/rwlock*.h=emit
src:*include/linux/rwsem.h=emit
src:*include/linux/sched*=emit
and if I run the following commands:
build-kernel-with-clang allmodconfig
build-kernel-with-clang kernel/locking/mutex.o
then the following output appears:
CALL scripts/checksyscalls.sh
DESCEND objtool
INSTALL libsubcmd_headers
CC kernel/locking/mutex.o
In file included from kernel/locking/mutex.c:22:
In file included from ./include/linux/ww_mutex.h:21:
./include/linux/rtmutex.h:44:25: error: reading variable 'owner' requires
holding raw_spinlock '&rt_mutex_base::wait_lock'
[-Werror,-Wthread-safety-analysis]
44 | return READ_ONCE(lock->owner) != NULL;
| ^
./include/linux/rtmutex.h:52:56: error: reading variable 'owner' requires
holding raw_spinlock '&rt_mutex_base::wait_lock'
[-Werror,-Wthread-safety-analysis]
52 | unsigned long owner = (unsigned long)
READ_ONCE(lock->owner);
| ^
2 errors generated.
The build-kernel-with-clang script is as follows (this may not be the
recommended way to build the kernel with clang):
#!/bin/bash
export CC=clang
export LD=ld.lld # Use LLVM's linker (optional but recommended)
export AR=llvm-ar
export NM=llvm-nm
export OBJCOPY=llvm-objcopy
export OBJDUMP=llvm-objdump
export STRIP=llvm-strip
export READELF=llvm-readelf
make LLVM=1 CC=clang "$@"
Please let me know if you need more information.
Bart.
On Thu, Jan 22, 2026 at 08:28:44AM -0800, Bart Van Assche wrote:
> then the following output appears:
>
> CALL scripts/checksyscalls.sh
> DESCEND objtool
> INSTALL libsubcmd_headers
> CC kernel/locking/mutex.o
> In file included from kernel/locking/mutex.c:22:
> In file included from ./include/linux/ww_mutex.h:21:
> ./include/linux/rtmutex.h:44:25: error: reading variable 'owner' requires
> holding raw_spinlock '&rt_mutex_base::wait_lock'
> [-Werror,-Wthread-safety-analysis]
> 44 | return READ_ONCE(lock->owner) != NULL;
> | ^
> ./include/linux/rtmutex.h:52:56: error: reading variable 'owner' requires
> holding raw_spinlock '&rt_mutex_base::wait_lock'
> [-Werror,-Wthread-safety-analysis]
> 52 | unsigned long owner = (unsigned long)
> READ_ONCE(lock->owner);
> | ^
> 2 errors generated.
>
Indeed; I shall fold the below into the rtmutex patch. Thanks!
---
--- a/include/linux/rtmutex.h
+++ b/include/linux/rtmutex.h
@@ -41,7 +41,7 @@ struct rt_mutex_base {
*/
static inline bool rt_mutex_base_is_locked(struct rt_mutex_base *lock)
{
- return READ_ONCE(lock->owner) != NULL;
+ return data_race(READ_ONCE(lock->owner) != NULL);
}
#ifdef CONFIG_RT_MUTEXES
@@ -49,7 +49,7 @@ static inline bool rt_mutex_base_is_lock
static inline struct task_struct *rt_mutex_owner(struct rt_mutex_base *lock)
{
- unsigned long owner = (unsigned long) READ_ONCE(lock->owner);
+ unsigned long owner = (unsigned long) data_race(READ_ONCE(lock->owner));
return (struct task_struct *) (owner & ~RT_MUTEX_HAS_WAITERS);
}
--- a/scripts/context-analysis-suppression.txt
+++ b/scripts/context-analysis-suppression.txt
@@ -24,6 +24,7 @@ src:*include/linux/mutex*.h=emit
src:*include/linux/rcupdate.h=emit
src:*include/linux/refcount.h=emit
src:*include/linux/rhashtable.h=emit
+src:*include/linux/rtmutex*.h=emit
src:*include/linux/rwlock*.h=emit
src:*include/linux/rwsem.h=emit
src:*include/linux/sched*=emit
On 1/23/26 3:15 AM, Peter Zijlstra wrote:
> --- a/include/linux/rtmutex.h
> +++ b/include/linux/rtmutex.h
> @@ -41,7 +41,7 @@ struct rt_mutex_base {
> */
> static inline bool rt_mutex_base_is_locked(struct rt_mutex_base *lock)
> {
> - return READ_ONCE(lock->owner) != NULL;
> + return data_race(READ_ONCE(lock->owner) != NULL);
> }
>
> #ifdef CONFIG_RT_MUTEXES
> @@ -49,7 +49,7 @@ static inline bool rt_mutex_base_is_lock
>
> static inline struct task_struct *rt_mutex_owner(struct rt_mutex_base *lock)
> {
> - unsigned long owner = (unsigned long) READ_ONCE(lock->owner);
> + unsigned long owner = (unsigned long) data_race(READ_ONCE(lock->owner));
>
> return (struct task_struct *) (owner & ~RT_MUTEX_HAS_WAITERS);
> }
Marco, shouldn't lock context analysis be disabled for all READ_ONCE()
invocations? I expect that all code that uses READ_ONCE() to read a
structure member annotated with __guarded_by() will have to be annotated
with data_race(). Shouldn't the data_race() invocation be moved into the
READ_ONCE() definition?
Thanks,
Bart.
On Fri, 23 Jan 2026 at 19:58, Bart Van Assche <bvanassche@acm.org> wrote:
>
> On 1/23/26 3:15 AM, Peter Zijlstra wrote:
> > --- a/include/linux/rtmutex.h
> > +++ b/include/linux/rtmutex.h
> > @@ -41,7 +41,7 @@ struct rt_mutex_base {
> > */
> > static inline bool rt_mutex_base_is_locked(struct rt_mutex_base *lock)
> > {
> > - return READ_ONCE(lock->owner) != NULL;
> > + return data_race(READ_ONCE(lock->owner) != NULL);
> > }
> >
> > #ifdef CONFIG_RT_MUTEXES
> > @@ -49,7 +49,7 @@ static inline bool rt_mutex_base_is_lock
> >
> > static inline struct task_struct *rt_mutex_owner(struct rt_mutex_base *lock)
> > {
> > - unsigned long owner = (unsigned long) READ_ONCE(lock->owner);
> > + unsigned long owner = (unsigned long) data_race(READ_ONCE(lock->owner));
> >
> > return (struct task_struct *) (owner & ~RT_MUTEX_HAS_WAITERS);
> > }
> Marco, shouldn't lock context analysis be disabled for all READ_ONCE()
> invocations? I expect that all code that uses READ_ONCE() to read a
> structure member annotated with __guarded_by() will have to be annotated
> with data_race(). Shouldn't the data_race() invocation be moved into the
> READ_ONCE() definition?
Unclear, guarded variables accessed with READ_ONCE() do not
necessarily imply access without said lock is safe.
Second, a READ_ONCE() paired with another marked primitive never
results in a data race, and using data_race() actually prohibits KCSAN
from detecting real data races if any would be introduced, but we're
not expecting any.
In the above case, if there aren't actually any data races, the
data_race() addition does look odd.
Perhaps context_unsafe(READ_ONCE(..)) would be more appropriate if
there's no actual data race. That will signal context analysis this is
safe, and if there are any real data races introduced here (e.g. by a
concurrent plain write or such), we'd learn about it if KCSAN detects
it.
On Thu, Jan 22, 2026 at 08:28:44AM -0800, Bart Van Assche wrote:
> The build-kernel-with-clang script is as follows (this may not be the
> recommended way to build the kernel with clang):
>
> #!/bin/bash
> export CC=clang
> export LD=ld.lld # Use LLVM's linker (optional but recommended)
> export AR=llvm-ar
> export NM=llvm-nm
> export OBJCOPY=llvm-objcopy
> export OBJDUMP=llvm-objdump
> export STRIP=llvm-strip
> export READELF=llvm-readelf
> make LLVM=1 CC=clang "$@"
FWIW, this could ultimately simplify to
make LLVM=1 "$@"
as LLVM=1 is the shorthand for
make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \
OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \
HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld
as noted in Documentation/kbuild/llvm.rst /
https://docs.kernel.org/kbuild/llvm.html.
Cheers,
Nathan
On Thu, Jan 22, 2026 at 11:58:12AM -0700, Nathan Chancellor wrote: > On Thu, Jan 22, 2026 at 08:28:44AM -0800, Bart Van Assche wrote: > > The build-kernel-with-clang script is as follows (this may not be the > > recommended way to build the kernel with clang): > > > > #!/bin/bash > > export CC=clang > > export LD=ld.lld # Use LLVM's linker (optional but recommended) > > export AR=llvm-ar > > export NM=llvm-nm > > export OBJCOPY=llvm-objcopy > > export OBJDUMP=llvm-objdump > > export STRIP=llvm-strip > > export READELF=llvm-readelf > > make LLVM=1 CC=clang "$@" > > FWIW, this could ultimately simplify to > > make LLVM=1 "$@" > > as LLVM=1 is the shorthand for > > make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \ > OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \ > HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld > Right, for all of us on Debian, you can use: make LLVM=-22 ...
On Wed, Jan 21, 2026 at 12:07PM +0100, Peter Zijlstra wrote:
> Hai
>
> This is on top of tip/locking/core with these patches on:
>
> https://lkml.kernel.org/r/20260119094029.1344361-1-elver@google.com
>
> and converts mutex, rtmutex, ww_mutex and futex to use the new context analysis
> bits.
>
> There is one snafu:
>
> ww_mutex_set_context_fastpath()'s data_race() usage doesn't stop the compiler
> from complaining when building a defconfig+PREEMPT_RT+LOCKDEP build:
>
> ../kernel/locking/ww_mutex.h:439:24: error: calling function '__ww_mutex_has_waiters' requires holding raw_spinlock 'lock->base.rtmutex.wait_lock' exclusively [-Werror,-Wthread-safety-analysis]
> 439 | if (likely(!data_race(__ww_mutex_has_waiters(&lock->base))))
> | ^
> 1 error generated.
This works:
diff --git a/kernel/locking/ww_mutex.h b/kernel/locking/ww_mutex.h
index 73eed6b7f24e..561e2475954d 100644
--- a/kernel/locking/ww_mutex.h
+++ b/kernel/locking/ww_mutex.h
@@ -436,7 +436,8 @@ ww_mutex_set_context_fastpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
* __ww_mutex_add_waiter() and makes sure we either observe ww->ctx
* and/or !empty list.
*/
- if (likely(!data_race(__ww_mutex_has_waiters(&lock->base))))
+ bool has_waiters = data_race(__ww_mutex_has_waiters(&lock->base));
+ if (likely(!has_waiters))
return;
/*
It appears that the _Pragma are ignored when the expression is inside
__builtin_expect(...). That's a bit inconvenient.
Another option is this given its exclusively used without holding this
lock:
diff --git a/kernel/locking/ww_mutex.h b/kernel/locking/ww_mutex.h
index 73eed6b7f24e..45a9c394fe91 100644
--- a/kernel/locking/ww_mutex.h
+++ b/kernel/locking/ww_mutex.h
@@ -71,7 +71,7 @@ __ww_mutex_owner(struct mutex *lock)
}
static inline bool
-__ww_mutex_has_waiters(struct mutex *lock)
+__ww_mutex_has_waiters_lockless(struct mutex *lock)
{
return atomic_long_read(&lock->owner) & MUTEX_FLAG_WAITERS;
}
@@ -151,10 +151,9 @@ __ww_mutex_owner(struct rt_mutex *lock)
}
static inline bool
-__ww_mutex_has_waiters(struct rt_mutex *lock)
- __must_hold(&lock->rtmutex.wait_lock)
+__ww_mutex_has_waiters_lockless(struct rt_mutex *lock)
{
- return rt_mutex_has_waiters(&lock->rtmutex);
+ return data_race(rt_mutex_has_waiters(&lock->rtmutex));
}
static inline void lock_wait_lock(struct rt_mutex *lock, unsigned long *flags)
@@ -436,7 +435,7 @@ ww_mutex_set_context_fastpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
* __ww_mutex_add_waiter() and makes sure we either observe ww->ctx
* and/or !empty list.
*/
- if (likely(!data_race(__ww_mutex_has_waiters(&lock->base))))
+ if (likely(!__ww_mutex_has_waiters_lockless(&lock->base)))
return;
/*
© 2016 - 2026 Red Hat, Inc.