aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChao Yu <chao@kernel.org>2021-04-12 16:15:12 +0800
committerChao Yu <chao@kernel.org>2022-03-29 13:10:35 +0800
commit3f75783c1f20e52b81471b577266d8eee74786e5 (patch)
treed9825ab32be4e314031021c50f665acdeb5eefd6
parentf96877a2dec6a9577ceecf26e27897182013a996 (diff)
downloadlinux-dev.tar.gz
f2fs: fix to keep isolation of atomic writedev
As Yi Chen reported, there is a potential race case described as below: Thread A Thread B - f2fs_ioc_start_atomic_write - mkwrite - set_page_dirty - f2fs_set_page_private(page, 0) - set_inode_flag(FI_ATOMIC_FILE) - mkwrite same page - set_page_dirty - f2fs_register_inmem_page - f2fs_set_page_private(ATOMIC_WRITTEN_PAGE) failed due to PagePrivate flag has been set - list_add_tail - truncate_inode_pages - f2fs_invalidate_page - clear page private but w/o remove it from inmem_list - set page->mapping to NULL - f2fs_ioc_commit_atomic_write - __f2fs_commit_inmem_pages - __revoke_inmem_pages - f2fs_put_page panic as page->mapping is NULL The root cause is we missed to keep isolation of atomic write in the case of start_atomic_write vs mkwrite, let start_atomic_write helds i_mmap_sem lock to avoid this issue. Reported-by: Yi Chen <chenyi77@huawei.com> Signed-off-by: Chao Yu <chao@kernel.org>
-rw-r--r--fs/f2fs/file.c3
-rw-r--r--fs/f2fs/segment.c6
2 files changed, 9 insertions, 0 deletions
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index e0a83fee7f605..799c2c9c32208 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2028,6 +2028,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
goto out;
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
+ filemap_invalidate_lock(inode->i_mapping);
/*
* Should wait end_io to count F2FS_WB_CP_DATA correctly by
@@ -2038,6 +2039,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
inode->i_ino, get_dirty_pages(inode));
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
if (ret) {
+ filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
goto out;
}
@@ -2051,6 +2053,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
/* add inode in inmem_list first and set atomic_file */
set_inode_flag(inode, FI_ATOMIC_FILE);
clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
+ filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 1c20d7c9eca3e..5fa14e2bd1ae6 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -328,6 +328,7 @@ void f2fs_drop_inmem_pages(struct inode *inode)
struct f2fs_inode_info *fi = F2FS_I(inode);
do {
+ filemap_invalidate_lock(inode->i_mapping);
mutex_lock(&fi->inmem_lock);
if (list_empty(&fi->inmem_pages)) {
fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
@@ -342,11 +343,13 @@ void f2fs_drop_inmem_pages(struct inode *inode)
spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
mutex_unlock(&fi->inmem_lock);
+ filemap_invalidate_unlock(inode->i_mapping);
break;
}
__revoke_inmem_pages(inode, &fi->inmem_pages,
true, false, true);
mutex_unlock(&fi->inmem_lock);
+ filemap_invalidate_unlock(inode->i_mapping);
} while (1);
}
@@ -472,6 +475,7 @@ int f2fs_commit_inmem_pages(struct inode *inode)
f2fs_balance_fs(sbi, true);
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
+ filemap_invalidate_lock(inode->i_mapping);
f2fs_lock_op(sbi);
set_inode_flag(inode, FI_ATOMIC_COMMIT);
@@ -483,6 +487,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
clear_inode_flag(inode, FI_ATOMIC_COMMIT);
f2fs_unlock_op(sbi);
+
+ filemap_invalidate_unlock(inode->i_mapping);
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
return err;