Skip to content

Commit 4df031f

Browse files
zhangyi089tytso
authored andcommitted
ext4: check and update i_disksize properly
After commit 3da40c7 ("ext4: only call ext4_truncate when size <= isize"), i_disksize could always be updated to i_size in ext4_setattr(), and we could sure that i_disksize <= i_size since holding inode lock and if i_disksize < i_size there are delalloc writes pending in the range upto i_size. If the end of the current write is <= i_size, there's no need to touch i_disksize since writeback will push i_disksize upto i_size eventually. So we can switch to check i_size instead of i_disksize in ext4_da_write_end() when write to the end of the file. we also could remove ext4_mark_inode_dirty() together because we defer inode dirtying to generic_write_end() or ext4_da_write_inline_data_end(). Signed-off-by: Zhang Yi <[email protected]> Reviewed-by: Jan Kara <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent b33d9f5 commit 4df031f

File tree

1 file changed

+18
-16
lines changed

1 file changed

+18
-16
lines changed

fs/ext4/inode.c

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3084,35 +3084,37 @@ static int ext4_da_write_end(struct file *file,
30843084
end = start + copied - 1;
30853085

30863086
/*
3087-
* generic_write_end() will run mark_inode_dirty() if i_size
3088-
* changes. So let's piggyback the i_disksize mark_inode_dirty
3089-
* into that.
3087+
* Since we are holding inode lock, we are sure i_disksize <=
3088+
* i_size. We also know that if i_disksize < i_size, there are
3089+
* delalloc writes pending in the range upto i_size. If the end of
3090+
* the current write is <= i_size, there's no need to touch
3091+
* i_disksize since writeback will push i_disksize upto i_size
3092+
* eventually. If the end of the current write is > i_size and
3093+
* inside an allocated block (ext4_da_should_update_i_disksize()
3094+
* check), we need to update i_disksize here as neither
3095+
* ext4_writepage() nor certain ext4_writepages() paths not
3096+
* allocating blocks update i_disksize.
3097+
*
3098+
* Note that we defer inode dirtying to generic_write_end() /
3099+
* ext4_da_write_inline_data_end().
30903100
*/
30913101
new_i_size = pos + copied;
3092-
if (copied && new_i_size > EXT4_I(inode)->i_disksize) {
3102+
if (copied && new_i_size > inode->i_size) {
30933103
if (ext4_has_inline_data(inode) ||
3094-
ext4_da_should_update_i_disksize(page, end)) {
3104+
ext4_da_should_update_i_disksize(page, end))
30953105
ext4_update_i_disksize(inode, new_i_size);
3096-
/* We need to mark inode dirty even if
3097-
* new_i_size is less that inode->i_size
3098-
* bu greater than i_disksize.(hint delalloc)
3099-
*/
3100-
ret = ext4_mark_inode_dirty(handle, inode);
3101-
}
31023106
}
31033107

31043108
if (write_mode != CONVERT_INLINE_DATA &&
31053109
ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA) &&
31063110
ext4_has_inline_data(inode))
3107-
ret2 = ext4_da_write_inline_data_end(inode, pos, len, copied,
3111+
ret = ext4_da_write_inline_data_end(inode, pos, len, copied,
31083112
page);
31093113
else
3110-
ret2 = generic_write_end(file, mapping, pos, len, copied,
3114+
ret = generic_write_end(file, mapping, pos, len, copied,
31113115
page, fsdata);
31123116

3113-
copied = ret2;
3114-
if (ret2 < 0)
3115-
ret = ret2;
3117+
copied = ret;
31163118
ret2 = ext4_journal_stop(handle);
31173119
if (unlikely(ret2 && !ret))
31183120
ret = ret2;

0 commit comments

Comments
 (0)