Expanding extra isize on an inode that is being evicted is pointless since the inode is about to be deleted. Skip it by setting EXT4_STATE_NO_EXPAND before calling ext4_mark_inode_dirty() in the eviction path. This also breaks a circular lock dependency reported by lockdep during orphan cleanup at mount time: CPU0 (writeback worker) CPU1 (open) ---- ---- ext4_writepages() s_writepages_rwsem (read) ext4_create() ext4_do_writepages() __ext4_new_inode() ext4_journal_start() [holds jbd2 handle] wait_transaction_locked() ext4_xattr_set_handle() [WAIT for jbd2_handle] xattr_sem (write) CPU2 (mount / orphan cleanup) ---- ext4_evict_inode() __ext4_mark_inode_dirty() ext4_try_to_expand_extra_isize() xattr_sem (write) ext4_expand_extra_isize_ea() ext4_xattr_block_set() iput(ea_inode) write_inode_now() ext4_writepages() s_writepages_rwsem (read) [WAIT for s_writepages_rwsem -- if blocked by write lock holder] This forms a circular dependency on lock classes: s_writepages_rwsem --> jbd2_handle --> xattr_sem --> s_writepages_rwsem The iput() inside ext4_xattr_block_set() triggers write_inode_now() because SB_ACTIVE is not yet set during mount, so iput_final() cannot cache the inode in the LRU and must flush it synchronously. Setting EXT4_STATE_NO_EXPAND prevents ext4_try_to_expand_extra_isize() from executing, which eliminates the xattr_sem --> s_writepages_rwsem edge and breaks the cycle. Reported-by: syzbot+5d19358d7eb30ffb0cc5@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=5d19358d7eb30ffb0cc5 Fixes: c8585c6fcaf2 ("ext4: fix races between changing inode journal mode and ext4_writepages") Signed-off-by: Yun Zhou --- fs/ext4/inode.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index cd7588a3fa45..cbfd1d1282e6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -264,6 +264,12 @@ void ext4_evict_inode(struct inode *inode) if (ext4_inode_is_fast_symlink(inode)) memset(EXT4_I(inode)->i_data, 0, sizeof(EXT4_I(inode)->i_data)); inode->i_size = 0; + /* + * Skip extra isize expansion on inodes being deleted -- it is + * pointless and can trigger a circular lock dependency: + * xattr_sem -> ext4_xattr_block_set -> iput -> s_writepages_rwsem + */ + ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND); err = ext4_mark_inode_dirty(handle, inode); if (err) { ext4_warning(inode->i_sb, -- 2.43.0