From: Zhang Yi <yi.zhang@huawei.com> hulk inclusion category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/8477 -------------------------------- Move ext4_block_zero_eof() and ext4_zero_partial_blocks() calls out of the active handle context, making them independent operations. This is safe because it still ensures data is updated before metadata for data=ordered mode and data=journal mode because we still zero data and ordering data before modifying the metadata. This change is required for iomap infrastructure conversion because the iomap buffered I/O path does not use the same journal infrastructure for partial block zeroing. The lock ordering of folio lock and starting transactions is "folio lock -> transaction start", which is opposite of the current path. Therefore, zeroing partial blocks cannot be performed under the active handle. 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/extents.c | 31 +++++++++++++++---------------- fs/ext4/inode.c | 10 +++++----- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index b9dbe6072392..a35c39cad502 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4520,6 +4520,13 @@ static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len, credits = ext4_chunk_trans_blocks(inode, len_lblk); depth = ext_depth(inode); + /* Zero to the end of the block containing i_size */ + if (new_size > old_size) { + ret = ext4_block_zero_eof(inode, old_size, LLONG_MAX); + if (ret < 0) + return ret; + } + retry: while (len_lblk) { /* @@ -4560,10 +4567,8 @@ static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len, if (ext4_update_inode_size(inode, epos) & 0x1) inode_set_mtime_to_ts(inode, inode_get_ctime(inode)); - if (epos > old_size) { + if (epos > old_size) pagecache_isize_extended(inode, old_size, epos); - ext4_block_zero_eof(inode, old_size, epos); - } } ret2 = ext4_mark_inode_dirty(handle, inode); ext4_update_inode_fsync_trans(handle, inode, 1); @@ -4592,7 +4597,6 @@ static long ext4_zero_range(struct file *file, loff_t offset, loff_t new_size = 0; int ret = 0; int flags; - int credits; int partial_begin, partial_end; loff_t start, end; ext4_lblk_t lblk; @@ -4709,14 +4713,12 @@ static long ext4_zero_range(struct file *file, loff_t offset, if (!partial_begin && !partial_end) goto out_mutex; - /* - * In worst case we have to writeout two nonadjacent unwritten - * blocks and update the inode - */ - credits = (2 * ext4_ext_index_trans_blocks(inode, 2)) + 1; - if (ext4_should_journal_data(inode)) - credits += 2; - handle = ext4_journal_start(inode, EXT4_HT_MISC, credits); + /* Zero out partial block at the edges of the range */ + ret = ext4_zero_partial_blocks(inode, offset, len); + if (ret) + goto out_mutex; + + handle = ext4_journal_start(inode, EXT4_HT_MISC, 1); if (IS_ERR(handle)) { ret = PTR_ERR(handle); ext4_std_error(inode->i_sb, ret); @@ -4729,11 +4731,8 @@ static long ext4_zero_range(struct file *file, loff_t offset, ret = ext4_mark_inode_dirty(handle, inode); if (unlikely(ret)) goto out_handle; - /* Zero out partial block at the edges of the range */ - ret = ext4_zero_partial_blocks(inode, offset, len); - if (ret >= 0) - ext4_update_inode_fsync_trans(handle, inode, 1); + ext4_update_inode_fsync_trans(handle, inode, 1); if (file->f_flags & O_SYNC) ext4_handle_sync(handle); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 271a64050b08..94a003d9b77c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4729,6 +4729,10 @@ 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); + if (ret) + goto out_dio; + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) credits = ext4_writepage_trans_blocks(inode); else @@ -4740,10 +4744,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) goto out_dio; } - ret = ext4_zero_partial_blocks(inode, offset, length); - if (ret) - goto out_stop; - first_block = (offset + sb->s_blocksize - 1) >> EXT4_BLOCK_SIZE_BITS(sb); stop_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb); @@ -4778,7 +4778,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) ret = ret2; if (ret >= 0) ext4_update_inode_fsync_trans(handle, inode, 1); -out_stop: + ext4_journal_stop(handle); out_dio: filemap_invalidate_unlock(mapping); -- 2.39.2