Skip to contentSkip to conversation

once_lite: Simplify condition handling and fix context analysis

PATCH In review Marco Elver 4 participants 7 messages Sep 3 – Sep 9 VFS

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 once_lite: Simplify condition handling and fix context analysis

When WARN_ON_ONCE() wraps a conditional lock acquisition (such as down_write_trylock()) on architectures relying on DO_ONCE_LITE_IF() (e.g. arm), Clang's context analysis (Thread Safety Analysis) failed with a false positive:

fs/ext2/xattr.c:825:6: error: rw_semaphore 'EXT2_I().xattr_sem' is not held on every path through here [-Werror,-Wthread-safety-analysis]

    825 |         if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
        |             ^

This happens because DO_ONCE_LITE_IF() branches on __ONCE_LITE_IF()'s return value (__ret_once), creating an intermediate branch merge point where the lock may or may not be held depending on whether the once-flag (__already_done) was already set. Because the merge branch condition is __ret_once rather than the trylock predicate (__ret_do_once), Clang cannot reconcile the lockset at the branch merge points.

Fix it by refactoring __ONCE_LITE_IF() into an unconditional __ONCE_LITE() primitive and redefining __ONCE_LITE_IF(condition) as:

  (unlikely(condition) && __ONCE_LITE())

This simplifies the implementation, short-circuits evaluation so that __ONCE_LITE() is not called when the condition is false, and ensures that DO_ONCE_LITE_IF() only enters __ONCE_LITE() when __ret_do_once is true.

---
 include/linux/once_lite.h | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
include/linux/once_lite.h +5 -3
index 236592c4eeb1..5e2b67039aaf 100644
@@ -10,19 +10,21 @@
 #define DO_ONCE_LITE(func, ...)						\
 	DO_ONCE_LITE_IF(true, func, ##__VA_ARGS__)
 
-#define __ONCE_LITE_IF(condition)					\
+#define __ONCE_LITE()							\
 	({								\
 		static bool __section(".data..once") __already_done;	\
-		bool __ret_cond = !!(condition);			\
 		bool __ret_once = false;				\
 									\
-		if (unlikely(__ret_cond) && unlikely(!__already_done)) {\
+		if (unlikely(!__already_done)) {			\
 			__already_done = true;				\
 			__ret_once = true;				\
 		}							\
 		unlikely(__ret_once);					\
 	})
 
+#define __ONCE_LITE_IF(condition)					\
+	(unlikely(condition) && __ONCE_LITE())
+
 #define DO_ONCE_LITE_IF(condition, func, ...)				\
 	({								\
 		bool __ret_do_once = !!(condition);			\
Signature · 1 line
2.55.0.970.g62bdec98f9-goog
#1 lore raw
Sashiko has reviewed this patch and found no issues. It looks great!
Quoted 21 lines

When WARN_ON_ONCE() wraps a conditional lock acquisition (such as down_write_trylock()) on architectures relying on DO_ONCE_LITE_IF() (e.g. arm), Clang's context analysis (Thread Safety Analysis) failed with a false positive:

fs/ext2/xattr.c:825:6: error: rw_semaphore 'EXT2_I().xattr_sem' is not held on every path through here [-Werror,-Wthread-safety-analysis]

    825 |         if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
        |             ^

This happens because DO_ONCE_LITE_IF() branches on __ONCE_LITE_IF()'s return value (__ret_once), creating an intermediate branch merge point where the lock may or may not be held depending on whether the once-flag (__already_done) was already set. Because the merge branch condition is __ret_once rather than the trylock predicate (__ret_do_once), Clang cannot reconcile the lockset at the branch merge points.

Fix it by refactoring __ONCE_LITE_IF() into an unconditional __ONCE_LITE() primitive and redefining __ONCE_LITE_IF(condition) as:

  (unlikely(condition) && __ONCE_LITE())
Quoted 8 lines

This simplifies the implementation, short-circuits evaluation so that __ONCE_LITE() is not called when the condition is false, and ensures that DO_ONCE_LITE_IF() only enters __ONCE_LITE() when __ret_do_once is true.

Sashiko has reviewed this patch and found no issues. It looks great!

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Tested-by on v1 1/1
#2 lore raw
In reply to Marco Elver
Quoted 21 lines · Marco Elver

When WARN_ON_ONCE() wraps a conditional lock acquisition (such as down_write_trylock()) on architectures relying on DO_ONCE_LITE_IF() (e.g. arm), Clang's context analysis (Thread Safety Analysis) failed with a false positive:

fs/ext2/xattr.c:825:6: error: rw_semaphore 'EXT2_I().xattr_sem' is not held on every path through here [-Werror,-Wthread-safety-analysis]

    825 |         if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
        |             ^

This happens because DO_ONCE_LITE_IF() branches on __ONCE_LITE_IF()'s return value (__ret_once), creating an intermediate branch merge point where the lock may or may not be held depending on whether the once-flag (__already_done) was already set. Because the merge branch condition is __ret_once rather than the trylock predicate (__ret_do_once), Clang cannot reconcile the lockset at the branch merge points.

Fix it by refactoring __ONCE_LITE_IF() into an unconditional __ONCE_LITE() primitive and redefining __ONCE_LITE_IF(condition) as:

  (unlikely(condition) && __ONCE_LITE())
Quoted 8 lines

This simplifies the implementation, short-circuits evaluation so that __ONCE_LITE() is not called when the condition is false, and ensures that DO_ONCE_LITE_IF() only enters __ONCE_LITE() when __ret_do_once is true.

Thanks, this fixes that warning for me across all my builds.

Signature · 2 lines
Cheers,
Nathan
#3 lore raw
Quoted 28 lines · Nathan Chancellor
Quoted 26 lines · Marco Elver

When WARN_ON_ONCE() wraps a conditional lock acquisition (such as down_write_trylock()) on architectures relying on DO_ONCE_LITE_IF() (e.g. arm), Clang's context analysis (Thread Safety Analysis) failed with a false positive:

fs/ext2/xattr.c:825:6: error: rw_semaphore 'EXT2_I().xattr_sem' is not held on every path through here [-Werror,-Wthread-safety-analysis]

    825 |         if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
        |             ^

This happens because DO_ONCE_LITE_IF() branches on __ONCE_LITE_IF()'s return value (__ret_once), creating an intermediate branch merge point where the lock may or may not be held depending on whether the once-flag (__already_done) was already set. Because the merge branch condition is __ret_once rather than the trylock predicate (__ret_do_once), Clang cannot reconcile the lockset at the branch merge points.

Fix it by refactoring __ONCE_LITE_IF() into an unconditional __ONCE_LITE() primitive and redefining __ONCE_LITE_IF(condition) as:

  (unlikely(condition) && __ONCE_LITE())

This simplifies the implementation, short-circuits evaluation so that __ONCE_LITE() is not called when the condition is false, and ensures that DO_ONCE_LITE_IF() only enters __ONCE_LITE() when __ret_do_once is true.

Quoted 7 lines
Quoted 3 lines

Thanks, this fixes that warning for me across all my builds.

Thanks! I think this is one of those orphaned files - which tree can this go through? Can it go through your tree, unless Jan already picked it up with the other fix?

reviewer
#4 lore raw
Quoted 33 lines · Marco Elver
Quoted 32 lines · Nathan Chancellor
Quoted 29 lines · Marco Elver

When WARN_ON_ONCE() wraps a conditional lock acquisition (such as down_write_trylock()) on architectures relying on DO_ONCE_LITE_IF() (e.g. arm), Clang's context analysis (Thread Safety Analysis) failed with a false positive:

fs/ext2/xattr.c:825:6: error: rw_semaphore 'EXT2_I().xattr_sem' is not held on every path through here [-Werror,-Wthread-safety-analysis]

    825 |         if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
        |             ^

This happens because DO_ONCE_LITE_IF() branches on __ONCE_LITE_IF()'s return value (__ret_once), creating an intermediate branch merge point where the lock may or may not be held depending on whether the once-flag (__already_done) was already set. Because the merge branch condition is __ret_once rather than the trylock predicate (__ret_do_once), Clang cannot reconcile the lockset at the branch merge points.

Fix it by refactoring __ONCE_LITE_IF() into an unconditional __ONCE_LITE() primitive and redefining __ONCE_LITE_IF(condition) as:

  (unlikely(condition) && __ONCE_LITE())

This simplifies the implementation, short-circuits evaluation so that __ONCE_LITE() is not called when the condition is false, and ensures that DO_ONCE_LITE_IF() only enters __ONCE_LITE() when __ret_do_once is true.

Quoted 7 lines
Quoted 3 lines

Thanks, this fixes that warning for me across all my builds.

Thanks! I think this is one of those orphaned files - which tree can this go through? Can it go through your tree, unless Jan already picked it up with the other fix?

If Nathan can take it through his tree, that would be great, otherwise I can take it through mine. Just let me know.

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
#5 lore raw
Quoted 7 lines · Jan Kara
Quoted 3 lines · Marco Elver

Thanks! I think this is one of those orphaned files - which tree can this go through? Can it go through your tree, unless Jan already picked it up with the other fix?

If Nathan can take it through his tree, that would be great, otherwise I can take it through mine. Just let me know.

I applied this to my clang-fixes tree and created an immutable tag for you to pull from, as you should have it in your tree since you are enabling context analysis there. If nobody else needs it and I do not have any other material for 7.4 in that tree, I won't send a pull request if the ext2 tree is merged with this included.

The following changes since commit cee9395acd8043be0644b25c34bfa86623f2b935:

Linux 7.3-rc1 (2026-08-30 13:34:40 -0700)

are available in the Git repository at:

[email protected]:pub/scm/linux/kernel/git/nathan/linux.git tags/clang-fixes-once_lite-for-7.4

for you to fetch changes up to 0bb666d5f5a2339a5692afb312c2161df9620503:

once_lite: Simplify condition handling and fix context analysis (2026-09-08 16:54:02 -0700)

----------------------------------------------------------------
Immutable branch for once_lite.h context analysis fix

----------------------------------------------------------------
Marco Elver (1):
once_lite: Simplify condition handling and fix context analysis

 include/linux/once_lite.h | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
Signature · 2 lines
Cheers,
Nathan
reviewer
#6 lore raw
Quoted 23 lines · Nathan Chancellor
Quoted 7 lines · Jan Kara
Quoted 3 lines · Marco Elver

Thanks! I think this is one of those orphaned files - which tree can this go through? Can it go through your tree, unless Jan already picked it up with the other fix?

If Nathan can take it through his tree, that would be great, otherwise I can take it through mine. Just let me know.

I applied this to my clang-fixes tree and created an immutable tag for you to pull from, as you should have it in your tree since you are enabling context analysis there. If nobody else needs it and I do not have any other material for 7.4 in that tree, I won't send a pull request if the ext2 tree is merged with this included.

The following changes since commit cee9395acd8043be0644b25c34bfa86623f2b935:

Linux 7.3-rc1 (2026-08-30 13:34:40 -0700)

are available in the Git repository at:

[email protected]:pub/scm/linux/kernel/git/nathan/linux.git tags/clang-fixes-once_lite-for-7.4

Quoted 8 lines

for you to fetch changes up to 0bb666d5f5a2339a5692afb312c2161df9620503:

once_lite: Simplify condition handling and fix context analysis (2026-09-08 16:54:02 -0700)

----------------------------------------------------------------
Immutable branch for once_lite.h context analysis fix

OK, thanks. I've pulled this branch into my tree.

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR