Support Clang context analysis for ext2
Subsystems (2)
v2 cover Support Clang context analysis for ext2
This description is mostly copied from v1:
This series adds annotations for Clang's context analysis to ext2. Clang context analysis was recently added in a series by Marco Elver [1]. This allows the compiler to validate different locking patterns at compile time.
This series enables context analysis, fixes pre-existing warnings, and adds new annotations. It is inspired by similar series in the block layer (NVMe host driver, for example [2]).
I'm starting with ext2 since it's smaller and simpler compared to ext4/btrfs/etc. After ext2, I'd be interested in converting the other filesystems and infrastructure code in fs/. I think the ultimate goal would be to enable this by default across all of fs/.
The series was built and tested with Clang 23 with CONFIG_WARN_CONTEXT_ANALYSIS enabled. I based on 7.2-rc7.
Thanks!
Changes from v1:
* A false positive has been fixed in Clang [3] and ported to Clang 23. Hence, the first patch (silencing that false positive) has been dropped.
* Use guard() and scoped_guard() instead of context_unsafe(), when possible, to express that certain fields are being protected by a newly initialized lock during init.
Link to v1: https://lore.kernel.org/linux-fsdevel/[email protected]/
[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://github.com/llvm/llvm-project/pull/209796
Timothy Day (8):
ext2: mark s_next_generation as guarded by s_next_gen_lock
ext2: annotate ext2_update_dynamic_rev() as requiring s_lock
ext2: mark statfs overhead cache as guarded by s_lock
ext2: mark s_mount_state as guarded by s_lock
ext2: annotate ext2_init_block_alloc_info() as requiring
truncate_mutex
ext2: annotate block-mapping helpers as requiring truncate_mutex
ext2: annotate s_rsv_window_root as requiring s_rsv_window_lock
ext2: enable context analysis support for ext2 filesystem
fs/ext2/Makefile | 2 ++ fs/ext2/balloc.c | 4 ++++ fs/ext2/ext2.h | 19 +++++++++++-------- fs/ext2/inode.c | 7 +++++++ fs/ext2/super.c | 35 +++++++++++++++++++---------------- 5 files changed, 43 insertions(+), 24 deletions(-)
Signature · 1 line
2.43.0
PATCH v2 1/8 ext2: mark s_next_generation as guarded by s_next_gen_lock
s_next_generation is only ever modified while holding s_next_gen_lock (in ext2_new_inode()), so annotate it with __guarded_by() for Clang's context analysis.
The only other write is the initialisation in ext2_fill_super(), which runs before the superblock is live. No concurrent access should be possible. Convert the spinlock initialization to use scoped_guard(spinlock_init, ...) and place the write under the guard to prevent a warning.
--- fs/ext2/ext2.h | 2 +- fs/ext2/super.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
fs/ext2/ext2.h +1 -1
@@ -93,7 +93,7 @@ struct ext2_sb_info {int s_inode_size;int s_first_ino;spinlock_t s_next_gen_lock; -u32 s_next_generation; + u32 s_next_generation __guarded_by(&s_next_gen_lock);unsigned long s_dir_count;u8 *s_debts;struct percpu_counter s_freeblocks_counter;
fs/ext2/super.c +2 -2
@@ -1125,8 +1125,8 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) goto failed_mount2; } sbi->s_gdb_count = db_count; - sbi->s_next_generation =get_random_u32 (); -spin_lock_init (&sbi->s_next_gen_lock); +scoped_guard (spinlock_init, &sbi->s_next_gen_lock) + sbi->s_next_generation = get_random_u32();/* per filesystem reservation list head & lock */ spin_lock_init (&sbi->s_rsv_window_lock);
Signature · 1 line
2.43.0
PATCH v2 2/8 ext2: annotate ext2_update_dynamic_rev() as requiring s_lock
In reply to Timothy Dayext2_update_dynamic_rev() modifies several fields within 'struct ext2_super_block' and is already documented as requiring s_lock. Express that with __must_hold() for Clang's context analysis.
Both callers ext2_xattr_update_super_block() and __ext2_write_inode() already hold s_lock.
--- fs/ext2/ext2.h | 3 ++- fs/ext2/super.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-)
fs/ext2/ext2.h +2 -1
@@ -760,7 +760,8 @@ extern __printf(3, 4)void ext2_error (struct super_block *,const char *,const char *, ...); extern __printf(3, 4) void ext2_msg(struct super_block *, const char *, const char *, ...); -extern void ext2_update_dynamic_rev (struct super_block *sb); +extern void ext2_update_dynamic_rev(struct super_block *sb) + __must_hold(&EXT2_SB(sb)->s_lock); extern void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es, int wait);
fs/ext2/super.c +1 -0
@@ -127,6 +127,7 @@ void ext2_msg(struct super_block *sb, const char *prefix,* This must be called with sbi->s_lock held. */ void ext2_update_dynamic_rev (struct super_block *sb) + __must_hold(&EXT2_SB(sb)->s_lock) {struct ext2_super_block *es =EXT2_SB (sb)->s_es;
Signature · 1 line
2.43.0
PATCH v2 3/8 ext2: mark statfs overhead cache as guarded by s_lock
In reply to Timothy Days_overhead_last and s_blocks_last cache the filesystem overhead computation and are only ever accessed in ext2_statfs() under s_lock. Annotate them with __guarded_by() for Clang's context analysis.
--- fs/ext2/ext2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/ext2/ext2.h +2 -2
@@ -77,8 +77,8 @@ struct ext2_sb_info {unsigned long s_gdb_count;/* Number of group descriptor blocks */ unsigned long s_desc_per_block;/* Number of group descriptors per block */ unsigned long s_groups_count;/* Number of groups in the fs */ -unsigned long s_overhead_last;/* Last calculated overhead */ -unsigned long s_blocks_last;/* Last seen block count */ + unsigned long s_overhead_last __guarded_by(&s_lock); /* Last calculated overhead */ + unsigned long s_blocks_last __guarded_by(&s_lock); /* Last seen block count */struct buffer_head * s_sbh;/* Buffer containing the super block */ struct ext2_super_block * s_es;/* Pointer to the super block in the buffer */ struct buffer_head ** s_group_desc;
Signature · 1 line
2.43.0
PATCH v2 4/8 ext2: mark s_mount_state as guarded by s_lock
In reply to Timothy Days_mount_state tracks the filesystem's mount/error state and is protected by s_lock everywhere it is accessed. Annotate it with __guarded_by() for Clang's context analysis.
ext2_setup_super() reads s_mount_state and is called with s_lock held from the remount path, so annotate it with __must_hold().
There are two accesses (the initial read in ext2_fill_super() and the setup_super() call) in the mount flow. This is before the superblock is live, so no concurrent access should be possible. Convert the spinlock initialization to use guard(spinlock_init) to prevent warnings on the writes later in initialization.
--- fs/ext2/ext2.h | 2 +- fs/ext2/super.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)
fs/ext2/ext2.h +1 -1
@@ -86,7 +86,7 @@ struct ext2_sb_info {unsigned long s_sb_block;kuid_t s_resuid;kgid_t s_resgid; -unsigned short s_mount_state; + unsigned short s_mount_state __guarded_by(&s_lock);unsigned short s_pad;int s_addr_per_block_bits;int s_desc_per_block_bits;
fs/ext2/super.c +2 -1
@@ -633,6 +633,7 @@ static int ext2_parse_param(struct fs_context *fc, struct fs_parameter *param)static int ext2_setup_super (struct super_block * sb,struct ext2_super_block * es,int read_only) + __must_hold(&EXT2_SB(sb)->s_lock) {int res =0 ;struct ext2_sb_info *sbi =EXT2_SB (sb);
@@ -894,7 +895,7 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_fs_info = sbi; sbi->s_sb_block = sb_block; -spin_lock_init (&sbi->s_lock); +guard (spinlock_init)(&EXT2_SB (sb)->s_lock); ret = -EINVAL ;/*
Signature · 1 line
2.43.0
PATCH v2 5/8 ext2: annotate ext2_init_block_alloc_info() as requiring truncate_mutex
In reply to Timothy Dayext2_init_block_alloc_info() sets up the inode's block reservation info and is already documented as needing truncate_mutex protection. Express that with __must_hold() for Clang's context analysis.
Both callers (ext2_get_blocks() and ext2_ioctl()) already hold truncate_mutex.
--- fs/ext2/balloc.c | 1 + fs/ext2/ext2.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
fs/ext2/balloc.c +1 -0
@@ -414,6 +414,7 @@ static inline int rsv_is_empty(struct ext2_reserve_window *rsv)* Needs truncate_mutex protection prior to calling this function. */ void ext2_init_block_alloc_info (struct inode *inode) + __must_hold(&EXT2_I(inode)->truncate_mutex) {struct ext2_inode_info *ei =EXT2_I (inode);struct ext2_block_alloc_info *block_i;
fs/ext2/ext2.h +2 -1
@@ -710,7 +710,8 @@ extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, struct buffer_head ** bh);extern void ext2_discard_reservation (struct inode *);extern int ext2_should_retry_alloc (struct super_block *sb,int *retries); -extern void ext2_init_block_alloc_info (struct inode *); +extern void ext2_init_block_alloc_info(struct inode *inode) + __must_hold(&EXT2_I(inode)->truncate_mutex);extern void ext2_rsv_window_add (struct super_block *sb,struct ext2_reserve_window_node *rsv);/* dir.c */
Signature · 1 line
2.43.0
PATCH v2 6/8 ext2: annotate block-mapping helpers as requiring truncate_mutex
In reply to Timothy DayThe indirect-block mapping helpers in inode.c all run under the inode's truncate_mutex.
ext2_find_goal(), ext2_alloc_blocks(), ext2_alloc_branch() and ext2_splice_branch() are reached only from ext2_get_blocks() while it holds the mutex. ext2_find_shared(), ext2_free_data() and ext2_free_branches() are called from __ext2_truncate_blocks() while it holds the mutex.
Add __must_hold() annotations for the Clang's context analysis to reflect this.
--- fs/ext2/inode.c | 7 +++++++ 1 file changed, 7 insertions(+)
fs/ext2/inode.c +7 -0
@@ -329,6 +329,7 @@ static ext2_fsblk_t ext2_find_near(struct inode *inode, Indirect *ind)static inline ext2_fsblk_t ext2_find_goal (struct inode *inode,long block,Indirect *partial) + __must_hold(&EXT2_I(inode)->truncate_mutex) {struct ext2_block_alloc_info *block_i;
@@ -399,6 +400,7 @@ ext2_blks_to_allocate(Indirect * branch, int k, unsigned long blks,static int ext2_alloc_blocks (struct inode *inode,ext2_fsblk_t goal,int indirect_blks,int blks,ext2_fsblk_t new_blocks[4 ],int *err) + __must_hold(&EXT2_I(inode)->truncate_mutex) {int target, i;unsigned long count =0 ;
@@ -479,6 +481,7 @@ static int ext2_alloc_blocks(struct inode *inode,static int ext2_alloc_branch (struct inode *inode,int indirect_blks,int *blks,ext2_fsblk_t goal,int *offsets,Indirect *branch) + __must_hold(&EXT2_I(inode)->truncate_mutex) {int blocksize = inode->i_sb->s_blocksize;int i, n =0 ;
@@ -560,6 +563,7 @@ static int ext2_alloc_branch(struct inode *inode,*/ static void ext2_splice_branch (struct inode *inode,long block,Indirect *where,int num,int blks) + __must_hold(&EXT2_I(inode)->truncate_mutex) {int i;struct ext2_block_alloc_info *block_i;
@@ -1002,6 +1006,7 @@ static Indirect *ext2_find_shared(struct inode *inode,int offsets[4 ], Indirect chain[4], __le32 *top) + __must_hold(&EXT2_I(inode)->truncate_mutex) { Indirect *partial, *p;int k, err;
@@ -1057,6 +1062,7 @@ static Indirect *ext2_find_shared(struct inode *inode,* appropriately. */ static inline void ext2_free_data (struct inode *inode,__le32 *p,__le32 *q) + __must_hold(&EXT2_I(inode)->truncate_mutex) {ext2_fsblk_t block_to_free =0 , count =0 ;ext2_fsblk_t nr;
@@ -1097,6 +1103,7 @@ static inline void ext2_free_data(struct inode *inode, __le32 *p, __le32 *q)* appropriately. */ static void ext2_free_branches (struct inode *inode,__le32 *p,__le32 *q,int depth) + __must_hold(&EXT2_I(inode)->truncate_mutex) {struct buffer_head * bh;ext2_fsblk_t nr;
Signature · 1 line
2.43.0
PATCH v2 7/8 ext2: annotate s_rsv_window_root as requiring s_rsv_window_lock
In reply to Timothy DayThe per-filesystem reservation window rb-tree (s_rsv_window_root) is protected by s_rsv_window_lock. Mark the s_rsv_window_root field with __guarded_by() for Clang's context analysis.
The helpers (ext2_rsv_window_add, rsv_window_remove, and find_next_reservable_window) that mutate or walk the tree are all called with the s_rsv_window_lock held. Annotate these helpers with __must_hold().
The accesses in ext2_fill_super() are before the superblock is live. Since no concurrent access should be possible, s_rsv_window_lock is not taken. Convert the spinlock initialization to use scoped_guard(spinlock_init, ...) and place the writes under the guard to prevent warnings.
--- fs/ext2/balloc.c | 3 +++ fs/ext2/ext2.h | 5 +++-- fs/ext2/super.c | 27 ++++++++++++++------------- 3 files changed, 20 insertions(+), 15 deletions(-)
fs/ext2/balloc.c +3 -0
@@ -334,6 +334,7 @@ search_reserve_window(struct rb_root *root, ext2_fsblk_t goal)*/ void ext2_rsv_window_add (struct super_block *sb,struct ext2_reserve_window_node *rsv) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock) {struct rb_root *root = &EXT2_SB (sb)->s_rsv_window_root;struct rb_node *node = &rsv->rsv_node;
@@ -373,6 +374,7 @@ void ext2_rsv_window_add(struct super_block *sb,*/ static void rsv_window_remove (struct super_block *sb,struct ext2_reserve_window_node *rsv) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock) { rsv->rsv_start =EXT2_RESERVE_WINDOW_NOT_ALLOCATED ; rsv->rsv_end =EXT2_RESERVE_WINDOW_NOT_ALLOCATED ;
@@ -759,6 +761,7 @@ static int find_next_reservable_window(struct super_block * sb, ext2_fsblk_t start_block, ext2_fsblk_t last_block) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock) { struct rb_node *next;struct ext2_reserve_window_node *rsv, *prev;
fs/ext2/ext2.h +3 -2
@@ -102,7 +102,7 @@ struct ext2_sb_info {struct blockgroup_lock *s_blockgroup_lock;/* root of the per fs reservation window tree */ spinlock_t s_rsv_window_lock; -struct rb_root s_rsv_window_root; + struct rb_root s_rsv_window_root __guarded_by(&s_rsv_window_lock);struct ext2_reserve_window_node s_rsv_window_head;/* * s_lock protects against concurrent modifications of s_mount_state,
@@ -712,7 +712,8 @@ extern void ext2_discard_reservation (struct inode *);extern int ext2_should_retry_alloc (struct super_block *sb,int *retries); extern void ext2_init_block_alloc_info(struct inode *inode) __must_hold(&EXT2_I(inode)->truncate_mutex); -extern void ext2_rsv_window_add (struct super_block *sb,struct ext2_reserve_window_node *rsv); +extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv) + __must_hold(&EXT2_SB(sb)->s_rsv_window_lock);/* dir.c */ int ext2_add_link (struct dentry *,struct inode *);
fs/ext2/super.c +14 -13
@@ -1131,19 +1131,20 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) sbi->s_next_generation =get_random_u32 ();/* per filesystem reservation list head & lock */ -spin_lock_init (&sbi->s_rsv_window_lock); - sbi->s_rsv_window_root =RB_ROOT ; -/* -* Add a single, static dummy reservation to the start of the -* reservation window list --- it gives us a placeholder for -* append-at-start-of-list which makes the allocation logic -* _much_ simpler. -*/ - sbi->s_rsv_window_head.rsv_start =EXT2_RESERVE_WINDOW_NOT_ALLOCATED ; - sbi->s_rsv_window_head.rsv_end =EXT2_RESERVE_WINDOW_NOT_ALLOCATED ; - sbi->s_rsv_window_head.rsv_alloc_hit =0 ; - sbi->s_rsv_window_head.rsv_goal_size =0 ; -ext2_rsv_window_add (sb, &sbi->s_rsv_window_head); + scoped_guard(spinlock_init, &EXT2_SB(sb)->s_rsv_window_lock) { + sbi->s_rsv_window_root =RB_ROOT ; +/* +* Add a single, static dummy reservation to the start of the +* reservation window list --- it gives us a placeholder for +* append-at-start-of-list which makes the allocation logic +* _much_ simpler. +*/ + sbi->s_rsv_window_head.rsv_start =EXT2_RESERVE_WINDOW_NOT_ALLOCATED ; + sbi->s_rsv_window_head.rsv_end =EXT2_RESERVE_WINDOW_NOT_ALLOCATED ; + sbi->s_rsv_window_head.rsv_alloc_hit =0 ; + sbi->s_rsv_window_head.rsv_goal_size =0 ; +ext2_rsv_window_add (sb, &sbi->s_rsv_window_head); + } err =percpu_counter_init (&sbi->s_freeblocks_counter,ext2_count_free_blocks (sb),GFP_KERNEL );
Signature · 1 line
2.43.0
PATCH v2 8/8 ext2: enable context analysis support for ext2 filesystem
In reply to Timothy DayUpdate ext2 Makefile to support context analysis [1].
[1] https://docs.kernel.org/dev-tools/context-analysis.html
--- fs/ext2/Makefile | 2 ++ 1 file changed, 2 insertions(+)
fs/ext2/Makefile +2 -0
@@ -3,6 +3,8 @@ # Makefile for the linux ext2-filesystem routines. # +CONTEXT_ANALYSIS := y + obj-$(CONFIG_EXT2_FS) += ext2.o ext2-y := balloc.o dir.o file.o ialloc.o inode.o \
Signature · 1 line
2.43.0
Quoted 22 lines · Timothy Day
This description is mostly copied from v1:
This series adds annotations for Clang's context analysis to ext2. Clang context analysis was recently added in a series by Marco Elver [1]. This allows the compiler to validate different locking patterns at compile time.
This series enables context analysis, fixes pre-existing warnings, and adds new annotations. It is inspired by similar series in the block layer (NVMe host driver, for example [2]).
I'm starting with ext2 since it's smaller and simpler compared to ext4/btrfs/etc. After ext2, I'd be interested in converting the other filesystems and infrastructure code in
fs/. I think the ultimate goal would be to enable this by default across all offs/.The series was built and tested with Clang 23 with
CONFIG_WARN_CONTEXT_ANALYSISenabled. I based on 7.2-rc7.Thanks!
Quoted 7 lines
Changes from v1:
* A false positive has been fixed in Clang [3] and ported to Clang 23. Hence, the first patch (silencing that false positive) has been dropped.
* Useguard()andscoped_guard()instead ofcontext_unsafe(), when possible, to express that certain fields are being protected by a newly initialized lock during init.
But ultimately up to maintainers. Also, thanks for helping improve the Clang side (FWIW, Clang 23 will release August 23)!
Quoted 27 lines
Link to v1: https://lore.kernel.org/linux-fsdevel/[email protected]/
[1] https://lore.kernel.org/lkml/[email protected]/
[2] https://lore.kernel.org/all/[email protected]/
[3] https://github.com/llvm/llvm-project/pull/209796Timothy Day (8):
ext2: marks_next_generationas guarded bys_next_gen_lock
ext2: annotateext2_update_dynamic_rev()as requirings_lock
ext2: mark statfs overhead cache as guarded bys_lock
ext2: marks_mount_stateas guarded bys_lock
ext2: annotateext2_init_block_alloc_info()as requiring
truncate_mutex
ext2: annotate block-mapping helpers as requiringtruncate_mutex
ext2: annotates_rsv_window_rootas requirings_rsv_window_lock
ext2: enable context analysis support for ext2 filesystemfs/ext2/Makefile | 2 ++ fs/ext2/balloc.c | 4 ++++ fs/ext2/ext2.h | 19 +++++++++++-------- fs/ext2/inode.c | 7 +++++++ fs/ext2/super.c | 35 +++++++++++++++++++---------------- 5 files changed, 43 insertions(+), 24 deletions(-)--
2.43.0
Jan Kara
reviewerHello!
Quoted 11 lines · Timothy Day
This description is mostly copied from v1:
This series adds annotations for Clang's context analysis to ext2. Clang context analysis was recently added in a series by Marco Elver [1]. This allows the compiler to validate different locking patterns at compile time.
This series enables context analysis, fixes pre-existing warnings, and adds new annotations. It is inspired by similar series in the block layer (NVMe host driver, for example [2]).
Quoted 7 lines
I'm starting with ext2 since it's smaller and simpler compared to ext4/btrfs/etc. After ext2, I'd be interested in converting the other filesystems and infrastructure code in
fs/. I think the ultimate goal would be to enable this by default across all offs/.The series was built and tested with Clang 23 with
CONFIG_WARN_CONTEXT_ANALYSISenabled. I based on 7.2-rc7.
Thanks for the patches! They look good to me. Once the merge window is over I'll queue them to my tree. The only thing I'm not fully sure is how much I like the spinlock_init scoped guards - they looked quite confusing to me at the first sight (as much as I understand the convenience, conceptually how can initialization of a global lock be scoped?). I'll sleep over it, maybe I'll change them to just spinlock_init() + scoped_guard for the lock itself or maybe I'll get used to them. Anyway, no action on your side needed :).
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Quoted 20 lines · Jan Kara
Quoted 11 lines · Timothy Day
This description is mostly copied from v1:
This series adds annotations for Clang's context analysis to ext2. Clang context analysis was recently added in a series by Marco Elver [1]. This allows the compiler to validate different locking patterns at compile time.
This series enables context analysis, fixes pre-existing warnings, and adds new annotations. It is inspired by similar series in the block layer (NVMe host driver, for example [2]).
Quoted 7 lines
I'm starting with ext2 since it's smaller and simpler compared to ext4/btrfs/etc. After ext2, I'd be interested in converting the other filesystems and infrastructure code in
fs/. I think the ultimate goal would be to enable this by default across all offs/.The series was built and tested with Clang 23 with
CONFIG_WARN_CONTEXT_ANALYSISenabled. I based on 7.2-rc7.
Quoted 7 lines
Thanks for the patches! They look good to me. Once the merge window is over I'll queue them to my tree. The only thing I'm not fully sure is how much I like the
spinlock_initscoped guards - they looked quite confusing to me at the first sight (as much as I understand the convenience, conceptually how can initialization of a global lock be scoped?). I'll sleep over it, maybe I'll change them to justspinlock_init()+scoped_guardfor the lock itself or maybe I'll get used to them. Anyway, no action on your side needed :).
This series is now in -next, where I see the following warnings (or errors with CONFIG_WERROR=y / W=e) with various configurations, such as ARCH=arm allmodconfig, when building with LLVM 23.1.0
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)))
| ^
include/asm-generic/bug.h:180:2: note: expanded from macro 'WARN_ON_ONCE'
180 | DO_ONCE_LITE_IF(condition, WARN_ON, 1)
| ^
include/linux/once_lite.h:30:7: note: expanded from macro 'DO_ONCE_LITE_IF'
30 | if (__ONCE_LITE_IF(__ret_do_once)) \
| ^
include/linux/once_lite.h:23:3: note: expanded from macro '__ONCE_LITE_IF'
23 | unlikely(__ret_once); \
| ^
include/linux/compiler.h:77:22: note: expanded from macro 'unlikely'
77 | # define unlikely(x) __builtin_expect(!!(x), 0)
| ^
fs/ext2/xattr.c:825:20: note: rw_semaphore acquired here
825 | if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
| ^ fs/ext2/super.c:1149:3: error: calling function 'ext2_rsv_window_add' requires holding spinlock 'EXT2_SB(sb).s_rsv_window_lock' exclusively [-Werror,-Wthread-safety-precise]
1149 | ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);
| ^ fs/ext2/super.c:1149:3: note: found near match '_res->s_rsv_window_lock'
Signature · 2 lines
Cheers, Nathan
Quoted 43 lines · Nathan Chancellor
Quoted 20 lines · Jan Kara
Quoted 11 lines · Timothy Day
This description is mostly copied from v1:
This series adds annotations for Clang's context analysis to ext2. Clang context analysis was recently added in a series by Marco Elver [1]. This allows the compiler to validate different locking patterns at compile time.
This series enables context analysis, fixes pre-existing warnings, and adds new annotations. It is inspired by similar series in the block layer (NVMe host driver, for example [2]).
Quoted 7 lines
I'm starting with ext2 since it's smaller and simpler compared to ext4/btrfs/etc. After ext2, I'd be interested in converting the other filesystems and infrastructure code in
fs/. I think the ultimate goal would be to enable this by default across all offs/.The series was built and tested with Clang 23 with
CONFIG_WARN_CONTEXT_ANALYSISenabled. I based on 7.2-rc7.Quoted 7 lines
Thanks for the patches! They look good to me. Once the merge window is over I'll queue them to my tree. The only thing I'm not fully sure is how much I like the
spinlock_initscoped guards - they looked quite confusing to me at the first sight (as much as I understand the convenience, conceptually how can initialization of a global lock be scoped?). I'll sleep over it, maybe I'll change them to justspinlock_init()+scoped_guardfor the lock itself or maybe I'll get used to them. Anyway, no action on your side needed :).This series is now in -next, where I see the following warnings (or errors with
CONFIG_WERROR=y/ W=e) with various configurations, such as ARCH=arm allmodconfig, when building with LLVM 23.1.0
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))) | ^ include/asm-generic/bug.h:180:2: note: expanded from macro 'WARN_ON_ONCE' 180 | DO_ONCE_LITE_IF(condition, WARN_ON, 1) | ^ include/linux/once_lite.h:30:7: note: expanded from macro 'DO_ONCE_LITE_IF' 30 | if (__ONCE_LITE_IF(__ret_do_once)) \ | ^
include/linux/once_lite.h:23:3: note: expanded from macro '__ONCE_LITE_IF'
Quoted 8 lines
23 | unlikely(__ret_once); \
| ^
include/linux/compiler.h:77:22: note: expanded from macro 'unlikely'
77 | # define unlikely(x) __builtin_expect(!!(x), 0)
| ^
fs/ext2/xattr.c:825:20: note: rw_semaphore acquired here
825 | if (WARN_ON_ONCE(!down_write_trylock(&EXT2_I(inode)->xattr_sem)))
| ^This one can be fixed with (should also be a bit more efficient):
https://lore.kernel.org/all/[email protected]/
Quoted 4 lines
fs/ext2/super.c:1149:3: error: calling function 'ext2_rsv_window_add' requires holding spinlock 'EXT2_SB(sb).s_rsv_window_lock' exclusively [-Werror,-Wthread-safety-precise]1149 | ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); | ^
fs/ext2/super.c:1149:3: note: found near match '_res->s_rsv_window_lock'
sbi was just allocated, and EXT2_SB(sb) and sbi are pointing to the same object (unless I misread the code), but the compiler can't tell since aliases can't be tracked through non-local objects. So that scoped_guard could just become:
scoped_guard(spinlock, &EXT2_SB(sb)->s_rsv_window_lock) {
...But I'll leave that to Jan and Tim.
Thanks,
-- Marco
Jan Kara
reviewerQuoted 11 lines · Marco Elver
Quoted 4 lines · Nathan Chancellor
fs/ext2/super.c:1149:3: error: calling function 'ext2_rsv_window_add' requires holding spinlock 'EXT2_SB(sb).s_rsv_window_lock' exclusively [-Werror,-Wthread-safety-precise]1149 | ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); | ^
fs/ext2/super.c:1149:3: note: found near match '_res->s_rsv_window_lock'sbi was just allocated, and
EXT2_SB(sb)and sbi are pointing to the same object (unless I misread the code), but the compiler can't tell since aliases can't be tracked through non-local objects. So thatscoped_guardcould just become:
Quoted 4 lines
scoped_guard(spinlock, &EXT2_SB(sb)->s_rsv_window_lock) { ...But I'll leave that to Jan and Tim.
Yes, at the beginning of ext4_fill_super() we do:
sbi = kzalloc_obj(*sbi); ... sb->s_fs_info = sbi;
and EXT2_SB(sb) is just sb->s_fs_info. Are you saying that clang is not able to infer that sb->s_fs_info and sbi are still pointing to the same memory later in the function where we do scoped_guard()?
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Quoted 20 lines · Jan Kara
Quoted 11 lines · Marco Elver
Quoted 4 lines · Nathan Chancellor
fs/ext2/super.c:1149:3: error: calling function 'ext2_rsv_window_add' requires holding spinlock 'EXT2_SB(sb).s_rsv_window_lock' exclusively [-Werror,-Wthread-safety-precise]1149 | ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); | ^
fs/ext2/super.c:1149:3: note: found near match '_res->s_rsv_window_lock'sbi was just allocated, and
EXT2_SB(sb)and sbi are pointing to the same object (unless I misread the code), but the compiler can't tell since aliases can't be tracked through non-local objects. So thatscoped_guardcould just become:Quoted 4 lines
scoped_guard(spinlock, &EXT2_SB(sb)->s_rsv_window_lock) { ...But I'll leave that to Jan and Tim.
Yes, at the beginning of
ext4_fill_super()we do:
Quoted 7 lines
sbi = kzalloc_obj(*sbi); ... sb->s_fs_info = sbi;and
EXT2_SB(sb)is justsb->s_fs_info. Are you saying that clang is not able to infer thatsb->s_fs_infoand sbi are still pointing to the same memory later in the function where we doscoped_guard()?
Yes - alias tracking is only done through local aliases. sb is non-local, along with additional member indirection; unfortunately, the C language doesn't give us the guarantees that it wasn't modified somewhere in between, say after a function call (this rule is applied also for local aliases if clang sees that they "escape" their local scope via non-const pointer to pointer).
Jan Kara
reviewerQuoted 29 lines · Marco Elver
Quoted 20 lines · Jan Kara
Quoted 11 lines · Marco Elver
Quoted 4 lines · Nathan Chancellor
fs/ext2/super.c:1149:3: error: calling function 'ext2_rsv_window_add' requires holding spinlock 'EXT2_SB(sb).s_rsv_window_lock' exclusively [-Werror,-Wthread-safety-precise]1149 | ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); | ^
fs/ext2/super.c:1149:3: note: found near match '_res->s_rsv_window_lock'sbi was just allocated, and
EXT2_SB(sb)and sbi are pointing to the same object (unless I misread the code), but the compiler can't tell since aliases can't be tracked through non-local objects. So thatscoped_guardcould just become:Quoted 4 lines
scoped_guard(spinlock, &EXT2_SB(sb)->s_rsv_window_lock) { ...But I'll leave that to Jan and Tim.
Yes, at the beginning of
ext4_fill_super()we do:Quoted 7 lines
sbi = kzalloc_obj(*sbi); ... sb->s_fs_info = sbi;and
EXT2_SB(sb)is justsb->s_fs_info. Are you saying that clang is not able to infer thatsb->s_fs_infoand sbi are still pointing to the same memory later in the function where we doscoped_guard()?
Quoted 6 lines
Yes - alias tracking is only done through local aliases. sb is non-local, along with additional member indirection; unfortunately, the C language doesn't give us the guarantees that it wasn't modified somewhere in between, say after a function call (this rule is applied also for local aliases if clang sees that they "escape" their local scope via non-const pointer to pointer).
Hrm, ok, understood (but still it's annoying ;)). I've pushed out the patch with the suggested fixup.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Quoted 37 lines · Jan Kara
Quoted 29 lines · Marco Elver
Quoted 20 lines · Jan Kara
Quoted 11 lines · Marco Elver
Quoted 4 lines · Nathan Chancellor
fs/ext2/super.c:1149:3: error: calling function 'ext2_rsv_window_add' requires holding spinlock 'EXT2_SB(sb).s_rsv_window_lock' exclusively [-Werror,-Wthread-safety-precise]1149 | ext2_rsv_window_add(sb, &sbi->s_rsv_window_head); | ^
fs/ext2/super.c:1149:3: note: found near match '_res->s_rsv_window_lock'sbi was just allocated, and
EXT2_SB(sb)and sbi are pointing to the same object (unless I misread the code), but the compiler can't tell since aliases can't be tracked through non-local objects. So thatscoped_guardcould just become:Quoted 4 lines
scoped_guard(spinlock, &EXT2_SB(sb)->s_rsv_window_lock) { ...But I'll leave that to Jan and Tim.
Yes, at the beginning of
ext4_fill_super()we do:Quoted 7 lines
sbi = kzalloc_obj(*sbi); ... sb->s_fs_info = sbi;and
EXT2_SB(sb)is justsb->s_fs_info. Are you saying that clang is not able to infer thatsb->s_fs_infoand sbi are still pointing to the same memory later in the function where we doscoped_guard()?Quoted 6 lines
Yes - alias tracking is only done through local aliases. sb is non-local, along with additional member indirection; unfortunately, the C language doesn't give us the guarantees that it wasn't modified somewhere in between, say after a function call (this rule is applied also for local aliases if clang sees that they "escape" their local scope via non-const pointer to pointer).
Quoted 2 lines
Hrm, ok, understood (but still it's annoying ;)). I've pushed out the patch with the suggested fixup.
Sorry for the late reply. I was out last month due to lung trouble (spontaneous pneumothorax corrected via surgery). Thanks Jan for pulling the series. I took a look at your branch [2] and everything seems fine. I agree the scoped guards can be peculiar. If you wanted to make some minor changes, I have no objection.
Thanks Marco for the compilation fix in the other thread [1]. It seems like a reasonable fix. If there is anything else needed from me, let me know.
Now that I'm back, I'm hoping to resume work on some more context analysis patches. I have a few that were close to done that I hope to share soon.
Tim Day
[1] https://lore.kernel.org/all/[email protected]/
[2] https://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs.git/log/?h=for_next