| From 02a3ab1d483207efb375272bcfa24c51b6d0a451 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 12 Jan 2021 09:55:09 +0800 |
| Subject: f2fs: fix to set/clear I_LINKABLE under i_lock |
| |
| From: Chao Yu <yuchao0@huawei.com> |
| |
| [ Upstream commit 46085f37fc9e12d5c3539fb768b5ad7951e72acf ] |
| |
| fsstress + fault injection test case reports a warning message as |
| below: |
| |
| WARNING: CPU: 13 PID: 6226 at fs/inode.c:361 inc_nlink+0x32/0x40 |
| Call Trace: |
| f2fs_init_inode_metadata+0x25c/0x4a0 [f2fs] |
| f2fs_add_inline_entry+0x153/0x3b0 [f2fs] |
| f2fs_add_dentry+0x75/0x80 [f2fs] |
| f2fs_do_add_link+0x108/0x160 [f2fs] |
| f2fs_rename2+0x6ab/0x14f0 [f2fs] |
| vfs_rename+0x70c/0x940 |
| do_renameat2+0x4d8/0x4f0 |
| __x64_sys_renameat2+0x4b/0x60 |
| do_syscall_64+0x33/0x80 |
| entry_SYSCALL_64_after_hwframe+0x44/0xa9 |
| |
| Following race case can cause this: |
| Thread A Kworker |
| - f2fs_rename |
| - f2fs_create_whiteout |
| - __f2fs_tmpfile |
| - f2fs_i_links_write |
| - f2fs_mark_inode_dirty_sync |
| - mark_inode_dirty_sync |
| - writeback_single_inode |
| - __writeback_single_inode |
| - spin_lock(&inode->i_lock) |
| - inode->i_state |= I_LINKABLE |
| - inode->i_state &= ~dirty |
| - spin_unlock(&inode->i_lock) |
| - f2fs_add_link |
| - f2fs_do_add_link |
| - f2fs_add_dentry |
| - f2fs_add_inline_entry |
| - f2fs_init_inode_metadata |
| - f2fs_i_links_write |
| - inc_nlink |
| - WARN_ON(!(inode->i_state & I_LINKABLE)) |
| |
| Fix to add i_lock to avoid i_state update race condition. |
| |
| Signed-off-by: Chao Yu <yuchao0@huawei.com> |
| Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/f2fs/namei.c | 8 ++++++++ |
| 1 file changed, 8 insertions(+) |
| |
| diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c |
| index 6edb1ab579a1..887804968576 100644 |
| --- a/fs/f2fs/namei.c |
| +++ b/fs/f2fs/namei.c |
| @@ -855,7 +855,11 @@ static int __f2fs_tmpfile(struct inode *dir, struct dentry *dentry, |
| |
| if (whiteout) { |
| f2fs_i_links_write(inode, false); |
| + |
| + spin_lock(&inode->i_lock); |
| inode->i_state |= I_LINKABLE; |
| + spin_unlock(&inode->i_lock); |
| + |
| *whiteout = inode; |
| } else { |
| d_tmpfile(dentry, inode); |
| @@ -1041,7 +1045,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, |
| err = f2fs_add_link(old_dentry, whiteout); |
| if (err) |
| goto put_out_dir; |
| + |
| + spin_lock(&whiteout->i_lock); |
| whiteout->i_state &= ~I_LINKABLE; |
| + spin_unlock(&whiteout->i_lock); |
| + |
| iput(whiteout); |
| } |
| |
| -- |
| 2.30.1 |
| |