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: 3db1de0e582c ("f2fs: change the current atomic write way")
Cc: stable@vger.kernel.org #v5.19+
Reviewed-by: Sungjong Seo <sj1557.seo@samsung.com>
Reviewed-by: Yeongjin Gil <youngjin.gil@samsung.com>
Signed-off-by: Sunmin Jeong <s_min.jeong@samsung.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

authored by Sunmin Jeong and committed by Jaegeuk Kim f18d0076 b40a2b00

+23 -7
+1 -1
fs/f2fs/data.c
··· 2608 return true; 2609 if (IS_NOQUOTA(inode)) 2610 return true; 2611 - if (f2fs_is_atomic_file(inode)) 2612 return true; 2613 /* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */ 2614 if (f2fs_compressed_file(inode) &&
··· 2608 return true; 2609 if (IS_NOQUOTA(inode)) 2610 return true; 2611 + if (f2fs_used_in_atomic_write(inode)) 2612 return true; 2613 /* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */ 2614 if (f2fs_compressed_file(inode) &&
+11 -2
fs/f2fs/f2fs.h
··· 843 struct task_struct *atomic_write_task; /* store atomic write task */ 844 struct extent_tree *extent_tree[NR_EXTENT_CACHES]; 845 /* cached extent_tree entry */ 846 - struct inode *cow_inode; /* copy-on-write inode for atomic write */ 847 848 /* avoid racing between foreground op and gc */ 849 struct f2fs_rwsem i_gc_rwsem[2]; ··· 4267 f2fs_compressed_file(inode); 4268 } 4269 4270 static inline bool f2fs_meta_inode_gc_required(struct inode *inode) 4271 { 4272 - return f2fs_post_read_required(inode) || f2fs_is_atomic_file(inode); 4273 } 4274 4275 /*
··· 843 struct task_struct *atomic_write_task; /* store atomic write task */ 844 struct extent_tree *extent_tree[NR_EXTENT_CACHES]; 845 /* cached extent_tree entry */ 846 + union { 847 + struct inode *cow_inode; /* copy-on-write inode for atomic write */ 848 + struct inode *atomic_inode; 849 + /* point to atomic_inode, available only for cow_inode */ 850 + }; 851 852 /* avoid racing between foreground op and gc */ 853 struct f2fs_rwsem i_gc_rwsem[2]; ··· 4263 f2fs_compressed_file(inode); 4264 } 4265 4266 + static inline bool f2fs_used_in_atomic_write(struct inode *inode) 4267 + { 4268 + return f2fs_is_atomic_file(inode) || f2fs_is_cow_file(inode); 4269 + } 4270 + 4271 static inline bool f2fs_meta_inode_gc_required(struct inode *inode) 4272 { 4273 + return f2fs_post_read_required(inode) || f2fs_used_in_atomic_write(inode); 4274 } 4275 4276 /*
+3
fs/f2fs/file.c
··· 2183 2184 set_inode_flag(fi->cow_inode, FI_COW_FILE); 2185 clear_inode_flag(fi->cow_inode, FI_INLINE_DATA); 2186 } else { 2187 /* Reuse the already created COW inode */ 2188 ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true);
··· 2183 2184 set_inode_flag(fi->cow_inode, FI_COW_FILE); 2185 clear_inode_flag(fi->cow_inode, FI_INLINE_DATA); 2186 + 2187 + /* Set the COW inode's atomic_inode to the atomic inode */ 2188 + F2FS_I(fi->cow_inode)->atomic_inode = inode; 2189 } else { 2190 /* Reuse the already created COW inode */ 2191 ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true);
+5 -2
fs/f2fs/gc.c
··· 1171 static int ra_data_block(struct inode *inode, pgoff_t index) 1172 { 1173 struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 1174 - struct address_space *mapping = inode->i_mapping; 1175 struct dnode_of_data dn; 1176 struct page *page; 1177 struct f2fs_io_info fio = { ··· 1261 static int move_data_block(struct inode *inode, block_t bidx, 1262 int gc_type, unsigned int segno, int off) 1263 { 1264 struct f2fs_io_info fio = { 1265 .sbi = F2FS_I_SB(inode), 1266 .ino = inode->i_ino, ··· 1285 CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; 1286 1287 /* do not read out */ 1288 - page = f2fs_grab_cache_page(inode->i_mapping, bidx, false); 1289 if (!page) 1290 return -ENOMEM; 1291
··· 1171 static int ra_data_block(struct inode *inode, pgoff_t index) 1172 { 1173 struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 1174 + struct address_space *mapping = f2fs_is_cow_file(inode) ? 1175 + F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; 1176 struct dnode_of_data dn; 1177 struct page *page; 1178 struct f2fs_io_info fio = { ··· 1260 static int move_data_block(struct inode *inode, block_t bidx, 1261 int gc_type, unsigned int segno, int off) 1262 { 1263 + struct address_space *mapping = f2fs_is_cow_file(inode) ? 1264 + F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; 1265 struct f2fs_io_info fio = { 1266 .sbi = F2FS_I_SB(inode), 1267 .ino = inode->i_ino, ··· 1282 CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; 1283 1284 /* do not read out */ 1285 + page = f2fs_grab_cache_page(mapping, bidx, false); 1286 if (!page) 1287 return -ENOMEM; 1288
+1 -1
fs/f2fs/inline.c
··· 16 17 static bool support_inline_data(struct inode *inode) 18 { 19 - if (f2fs_is_atomic_file(inode)) 20 return false; 21 if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) 22 return false;
··· 16 17 static bool support_inline_data(struct inode *inode) 18 { 19 + if (f2fs_used_in_atomic_write(inode)) 20 return false; 21 if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) 22 return false;
+2 -1
fs/f2fs/inode.c
··· 804 805 f2fs_abort_atomic_write(inode, true); 806 807 - if (fi->cow_inode) { 808 clear_inode_flag(fi->cow_inode, FI_COW_FILE); 809 iput(fi->cow_inode); 810 fi->cow_inode = NULL; 811 }
··· 804 805 f2fs_abort_atomic_write(inode, true); 806 807 + if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { 808 clear_inode_flag(fi->cow_inode, FI_COW_FILE); 809 + F2FS_I(fi->cow_inode)->atomic_inode = NULL; 810 iput(fi->cow_inode); 811 fi->cow_inode = NULL; 812 }