From: Zhang Yi <yi.zhang@huawei.com> hulk inclusion category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/8477 -------------------------------- In ext4_zero_range() and ext4_punch_hole(), when operating in SYNC mode and zeroing a partial block, only data=journal modes guarantee that the zeroed data is synchronously persisted after the operation completes. For data=ordered/writeback mode and non-journal modes, this guarantee is missing. Introduce a partial_zero parameter to explicitly trigger writeback for all scenarios where a partial block is zeroed, ensuring the zeroed data is durably persisted. Fixes: 5721968224e0 ("ext4: implement zero_range iomap path") Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Signed-off-by: Yongjian Sun <sunyongjian1@huawei.com> --- fs/ext4/ext4.h | 2 +- fs/ext4/extents.c | 9 ++++++++- fs/ext4/inode.c | 19 ++++++++++++++----- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f33676aac29c..45a90910ecba 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -3026,7 +3026,7 @@ extern int ext4_normal_submit_inode_data_buffers(struct jbd2_inode *jinode); extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks); extern int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end); extern int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart, - loff_t length); + loff_t length, bool *did_zero); extern vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf); extern qsize_t *ext4_get_reserved_space(struct inode *inode); extern int ext4_get_projid(struct inode *inode, kprojid_t *projid); diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index a35c39cad502..09477549ba3d 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4601,6 +4601,7 @@ static long ext4_zero_range(struct file *file, loff_t offset, loff_t start, end; ext4_lblk_t lblk; unsigned int blkbits = inode->i_blkbits; + bool partial_zeroed = false; trace_ext4_zero_range(inode, offset, len, mode); @@ -4714,9 +4715,15 @@ static long ext4_zero_range(struct file *file, loff_t offset, goto out_mutex; /* Zero out partial block at the edges of the range */ - ret = ext4_zero_partial_blocks(inode, offset, len); + ret = ext4_zero_partial_blocks(inode, offset, len, &partial_zeroed); if (ret) goto out_mutex; + if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) && partial_zeroed) { + ret = filemap_write_and_wait_range(inode->i_mapping, offset, + offset + len - 1); + if (ret) + goto out_mutex; + } handle = ext4_journal_start(inode, EXT4_HT_MISC, 1); if (IS_ERR(handle)) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 94a003d9b77c..d4d705d30b7e 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4476,7 +4476,8 @@ int ext4_block_zero_eof(struct inode *inode, loff_t from, loff_t end) return did_zero ? length : 0; } -int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart, loff_t length) +int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart, loff_t length, + bool *did_zero) { struct super_block *sb = inode->i_sb; unsigned partial_start, partial_end; @@ -4493,20 +4494,21 @@ int ext4_zero_partial_blocks(struct inode *inode, loff_t lstart, loff_t length) /* Handle partial zero within the single block */ if (start == end && (partial_start || (partial_end != sb->s_blocksize - 1))) { - err = ext4_block_zero_range(inode, lstart, length, NULL, NULL); + err = ext4_block_zero_range(inode, lstart, length, did_zero, + NULL); return err; } /* Handle partial zero out on the start of the range */ if (partial_start) { err = ext4_block_zero_range(inode, lstart, sb->s_blocksize, - NULL, NULL); + did_zero, NULL); if (err) return err; } /* Handle partial zero out on the end of the range */ if (partial_end != sb->s_blocksize - 1) err = ext4_block_zero_range(inode, byte_end - partial_end, - partial_end + 1, NULL, NULL); + partial_end + 1, did_zero, NULL); return err; } @@ -4671,6 +4673,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) handle_t *handle; unsigned int credits; int ret = 0, ret2 = 0; + bool partial_zeroed = false; trace_ext4_punch_hole(inode, offset, length, 0); @@ -4729,9 +4732,15 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) if (ret) goto out_dio; - ret = ext4_zero_partial_blocks(inode, offset, length); + ret = ext4_zero_partial_blocks(inode, offset, length, &partial_zeroed); if (ret) goto out_dio; + if (((file->f_flags & O_SYNC) || IS_SYNC(inode)) && partial_zeroed) { + ret = filemap_write_and_wait_range(inode->i_mapping, offset, + offset + length - 1); + if (ret) + goto out_dio; + } if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) credits = ext4_writepage_trans_blocks(inode); -- 2.39.2