Offering: HULK hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAODWM
--------------------------------
The writeback process and error branch in writeback process may concurrently access extent tree, which leads to wrong da reservation count is calculated, specifically:
write(4M) // folio 0, 1 are dirty
wb_workfn fsync ext4_iomap_writepages iomap_do_writepage // writeback folio 0 iomap_writepage_map_blocks ext4_iomap_map_blocks ext4_iomap_map_one_extent ext4_map_create_blocks // down write(EXT4_I(inode)->i_data_sem) ext4_ext_map_blocks ext4_da_update_reserve_space // da reservation becomes 0 . . >> EIO occurs, journal is aborted << . ext4_sync_file . file_write_and_wait_range . do_writepages // writeback folio 1 . ext4_iomap_writepages . iomap_writepage_map_blocks . map_blocks // failed . discard_folio // error branch . ext4_es_remove_extent . // scan extents, delayed es is 512 . ext4_da_release_space . // da reservation count, 0 < 512 . WARN_ON(1) ext4_es_insert_extent // convert delayed es to unwritten status
Since all changes on extent tree are protected by holding EXT4_I(inode)->i_data_sem, fix the problem by adding the lock while doing ext4_es_remove_extent().
Fixes: 7f6416dcd4a3 ("ext4: implement writeback iomap path") Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com --- fs/ext4/inode.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index a22c4184d9ef..ef5dfb994f0e 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3676,8 +3676,10 @@ static inline int ext4_iomap_buffered_da_write_begin(struct inode *inode, static int ext4_iomap_punch_delalloc(struct inode *inode, loff_t offset, loff_t length) { + down_write(&EXT4_I(inode)->i_data_sem); ext4_es_remove_extent(inode, offset >> inode->i_blkbits, DIV_ROUND_UP_ULL(length, EXT4_BLOCK_SIZE(inode->i_sb))); + up_write(&EXT4_I(inode)->i_data_sem); return 0; }