Commit f18d0076 authored by Sunmin Jeong's avatar Sunmin Jeong Committed by Jaegeuk Kim

f2fs: use meta inode for GC of COW file

In case of the COW file, new updates and GC writes are already
separated to page caches of the atomic file and COW file. As some cases
that use the meta inode for GC, there are some race issues between a
foreground thread and GC thread.

To handle them, we need to take care when to invalidate and wait
writeback of GC pages in COW files as the case of using the meta inode.
Also, a pointer from the COW inode to the original inode is required to
check the state of original pages.

For the former, we can solve the problem by using the meta inode for GC
of COW files. Then let's get a page from the original inode in
move_data_block when GCing the COW file to avoid race condition.

Fixes: 3db1de0e ("f2fs: change the current atomic write way")
Cc: stable@vger.kernel.org #v5.19+
Reviewed-by: default avatarSungjong Seo <sj1557.seo@samsung.com>
Reviewed-by: default avatarYeongjin Gil <youngjin.gil@samsung.com>
Signed-off-by: default avatarSunmin Jeong <s_min.jeong@samsung.com>
Reviewed-by: default avatarChao Yu <chao@kernel.org>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent b40a2b00
...@@ -2608,7 +2608,7 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio) ...@@ -2608,7 +2608,7 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
return true; return true;
if (IS_NOQUOTA(inode)) if (IS_NOQUOTA(inode))
return true; return true;
if (f2fs_is_atomic_file(inode)) if (f2fs_used_in_atomic_write(inode))
return true; return true;
/* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */ /* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */
if (f2fs_compressed_file(inode) && if (f2fs_compressed_file(inode) &&
......
...@@ -843,7 +843,11 @@ struct f2fs_inode_info { ...@@ -843,7 +843,11 @@ struct f2fs_inode_info {
struct task_struct *atomic_write_task; /* store atomic write task */ struct task_struct *atomic_write_task; /* store atomic write task */
struct extent_tree *extent_tree[NR_EXTENT_CACHES]; struct extent_tree *extent_tree[NR_EXTENT_CACHES];
/* cached extent_tree entry */ /* cached extent_tree entry */
struct inode *cow_inode; /* copy-on-write inode for atomic write */ union {
struct inode *cow_inode; /* copy-on-write inode for atomic write */
struct inode *atomic_inode;
/* point to atomic_inode, available only for cow_inode */
};
/* avoid racing between foreground op and gc */ /* avoid racing between foreground op and gc */
struct f2fs_rwsem i_gc_rwsem[2]; struct f2fs_rwsem i_gc_rwsem[2];
...@@ -4263,9 +4267,14 @@ static inline bool f2fs_post_read_required(struct inode *inode) ...@@ -4263,9 +4267,14 @@ static inline bool f2fs_post_read_required(struct inode *inode)
f2fs_compressed_file(inode); f2fs_compressed_file(inode);
} }
static inline bool f2fs_used_in_atomic_write(struct inode *inode)
{
return f2fs_is_atomic_file(inode) || f2fs_is_cow_file(inode);
}
static inline bool f2fs_meta_inode_gc_required(struct inode *inode) static inline bool f2fs_meta_inode_gc_required(struct inode *inode)
{ {
return f2fs_post_read_required(inode) || f2fs_is_atomic_file(inode); return f2fs_post_read_required(inode) || f2fs_used_in_atomic_write(inode);
} }
/* /*
......
...@@ -2183,6 +2183,9 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate) ...@@ -2183,6 +2183,9 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
set_inode_flag(fi->cow_inode, FI_COW_FILE); set_inode_flag(fi->cow_inode, FI_COW_FILE);
clear_inode_flag(fi->cow_inode, FI_INLINE_DATA); clear_inode_flag(fi->cow_inode, FI_INLINE_DATA);
/* Set the COW inode's atomic_inode to the atomic inode */
F2FS_I(fi->cow_inode)->atomic_inode = inode;
} else { } else {
/* Reuse the already created COW inode */ /* Reuse the already created COW inode */
ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true); ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true);
......
...@@ -1171,7 +1171,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, ...@@ -1171,7 +1171,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static int ra_data_block(struct inode *inode, pgoff_t index) static int ra_data_block(struct inode *inode, pgoff_t index)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = f2fs_is_cow_file(inode) ?
F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
struct dnode_of_data dn; struct dnode_of_data dn;
struct page *page; struct page *page;
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
...@@ -1260,6 +1261,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index) ...@@ -1260,6 +1261,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
static int move_data_block(struct inode *inode, block_t bidx, static int move_data_block(struct inode *inode, block_t bidx,
int gc_type, unsigned int segno, int off) int gc_type, unsigned int segno, int off)
{ {
struct address_space *mapping = f2fs_is_cow_file(inode) ?
F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
struct f2fs_io_info fio = { struct f2fs_io_info fio = {
.sbi = F2FS_I_SB(inode), .sbi = F2FS_I_SB(inode),
.ino = inode->i_ino, .ino = inode->i_ino,
...@@ -1282,7 +1285,7 @@ static int move_data_block(struct inode *inode, block_t bidx, ...@@ -1282,7 +1285,7 @@ static int move_data_block(struct inode *inode, block_t bidx,
CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
/* do not read out */ /* do not read out */
page = f2fs_grab_cache_page(inode->i_mapping, bidx, false); page = f2fs_grab_cache_page(mapping, bidx, false);
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
static bool support_inline_data(struct inode *inode) static bool support_inline_data(struct inode *inode)
{ {
if (f2fs_is_atomic_file(inode)) if (f2fs_used_in_atomic_write(inode))
return false; return false;
if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode))
return false; return false;
......
...@@ -804,8 +804,9 @@ void f2fs_evict_inode(struct inode *inode) ...@@ -804,8 +804,9 @@ void f2fs_evict_inode(struct inode *inode)
f2fs_abort_atomic_write(inode, true); f2fs_abort_atomic_write(inode, true);
if (fi->cow_inode) { if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) {
clear_inode_flag(fi->cow_inode, FI_COW_FILE); clear_inode_flag(fi->cow_inode, FI_COW_FILE);
F2FS_I(fi->cow_inode)->atomic_inode = NULL;
iput(fi->cow_inode); iput(fi->cow_inode);
fi->cow_inode = NULL; fi->cow_inode = NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment