Skip to content

Commit 10b591e

Browse files
chaseyuJaegeuk Kim
authored andcommitted
f2fs: fix to avoid updating compression context during writeback
Bai, Shuangpeng <sjb7183@psu.edu> reported a bug as below: Oops: divide error: 0000 [#1] SMP KASAN PTI CPU: 0 UID: 0 PID: 11441 Comm: syz.0.46 Not tainted 6.17.0 #1 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:f2fs_all_cluster_page_ready+0x106/0x550 fs/f2fs/compress.c:857 Call Trace: <TASK> f2fs_write_cache_pages fs/f2fs/data.c:3078 [inline] __f2fs_write_data_pages fs/f2fs/data.c:3290 [inline] f2fs_write_data_pages+0x1c19/0x3600 fs/f2fs/data.c:3317 do_writepages+0x38e/0x640 mm/page-writeback.c:2634 filemap_fdatawrite_wbc mm/filemap.c:386 [inline] __filemap_fdatawrite_range mm/filemap.c:419 [inline] file_write_and_wait_range+0x2ba/0x3e0 mm/filemap.c:794 f2fs_do_sync_file+0x6e6/0x1b00 fs/f2fs/file.c:294 generic_write_sync include/linux/fs.h:3043 [inline] f2fs_file_write_iter+0x76e/0x2700 fs/f2fs/file.c:5259 new_sync_write fs/read_write.c:593 [inline] vfs_write+0x7e9/0xe00 fs/read_write.c:686 ksys_write+0x19d/0x2d0 fs/read_write.c:738 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xf7/0x470 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f The bug was triggered w/ below race condition: fsync setattr ioctl - f2fs_do_sync_file - file_write_and_wait_range - f2fs_write_cache_pages : inode is non-compressed : cc.cluster_size = F2FS_I(inode)->i_cluster_size = 0 - tag_pages_for_writeback - f2fs_setattr - truncate_setsize - f2fs_truncate - f2fs_fileattr_set - f2fs_setflags_common - set_compress_context : F2FS_I(inode)->i_cluster_size = 4 : set_inode_flag(inode, FI_COMPRESSED_FILE) - f2fs_compressed_file : return true - f2fs_all_cluster_page_ready : "pgidx % cc->cluster_size" trigger dividing 0 issue Let's change as below to fix this issue: - introduce a new atomic type variable .writeback in structure f2fs_inode_info to track the number of threads which calling f2fs_write_cache_pages(). - use .i_sem lock to protect .writeback update. - check .writeback before update compression context in f2fs_setflags_common() to avoid race w/ ->writepages. Fixes: 4c8ff70 ("f2fs: support data compression") Cc: stable@kernel.org Reported-by: Bai, Shuangpeng <sjb7183@psu.edu> Tested-by: Bai, Shuangpeng <sjb7183@psu.edu> Closes: https://lore.kernel.org/lkml/44D8F7B3-68AD-425F-9915-65D27591F93F@psu.edu Signed-off-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
1 parent 7c37c79 commit 10b591e

File tree

4 files changed

+23
-3
lines changed

4 files changed

+23
-3
lines changed

fs/f2fs/data.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3222,6 +3222,19 @@ static inline bool __should_serialize_io(struct inode *inode,
32223222
return false;
32233223
}
32243224

3225+
static inline void account_writeback(struct inode *inode, bool inc)
3226+
{
3227+
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
3228+
return;
3229+
3230+
f2fs_down_read(&F2FS_I(inode)->i_sem);
3231+
if (inc)
3232+
atomic_inc(&F2FS_I(inode)->writeback);
3233+
else
3234+
atomic_dec(&F2FS_I(inode)->writeback);
3235+
f2fs_up_read(&F2FS_I(inode)->i_sem);
3236+
}
3237+
32253238
static int __f2fs_write_data_pages(struct address_space *mapping,
32263239
struct writeback_control *wbc,
32273240
enum iostat_type io_type)
@@ -3267,10 +3280,14 @@ static int __f2fs_write_data_pages(struct address_space *mapping,
32673280
locked = true;
32683281
}
32693282

3283+
account_writeback(inode, true);
3284+
32703285
blk_start_plug(&plug);
32713286
ret = f2fs_write_cache_pages(mapping, wbc, io_type);
32723287
blk_finish_plug(&plug);
32733288

3289+
account_writeback(inode, false);
3290+
32743291
if (locked)
32753292
mutex_unlock(&sbi->writepages);
32763293

fs/f2fs/f2fs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,7 @@ struct f2fs_inode_info {
947947
unsigned char i_compress_level; /* compress level (lz4hc,zstd) */
948948
unsigned char i_compress_flag; /* compress flag */
949949
unsigned int i_cluster_size; /* cluster size */
950+
atomic_t writeback; /* count # of writeback thread */
950951

951952
unsigned int atomic_write_cnt;
952953
loff_t original_i_size; /* original i_size before atomic write */
@@ -4663,7 +4664,7 @@ static inline bool f2fs_disable_compressed_file(struct inode *inode)
46634664
f2fs_up_write(&fi->i_sem);
46644665
return true;
46654666
}
4666-
if (f2fs_is_mmap_file(inode) ||
4667+
if (f2fs_is_mmap_file(inode) || atomic_read(&fi->writeback) ||
46674668
(S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode))) {
46684669
f2fs_up_write(&fi->i_sem);
46694670
return false;

fs/f2fs/file.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,8 +2128,9 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask)
21282128

21292129
f2fs_down_write(&fi->i_sem);
21302130
if (!f2fs_may_compress(inode) ||
2131-
(S_ISREG(inode->i_mode) &&
2132-
F2FS_HAS_BLOCKS(inode))) {
2131+
atomic_read(&fi->writeback) ||
2132+
(S_ISREG(inode->i_mode) &&
2133+
F2FS_HAS_BLOCKS(inode))) {
21332134
f2fs_up_write(&fi->i_sem);
21342135
return -EINVAL;
21352136
}

fs/f2fs/super.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,6 +1759,7 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
17591759
atomic_set(&fi->dirty_pages, 0);
17601760
atomic_set(&fi->i_compr_blocks, 0);
17611761
atomic_set(&fi->open_count, 0);
1762+
atomic_set(&fi->writeback, 0);
17621763
init_f2fs_rwsem(&fi->i_sem);
17631764
spin_lock_init(&fi->i_size_lock);
17641765
INIT_LIST_HEAD(&fi->dirty_list);

0 commit comments

Comments
 (0)