|
|
@@ -3558,6 +3558,35 @@ int ext4_can_truncate(struct inode *inode)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * We have to make sure i_disksize gets properly updated before we truncate
|
|
|
+ * page cache due to hole punching or zero range. Otherwise i_disksize update
|
|
|
+ * can get lost as it may have been postponed to submission of writeback but
|
|
|
+ * that will never happen after we truncate page cache.
|
|
|
+ */
|
|
|
+int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
|
|
|
+ loff_t len)
|
|
|
+{
|
|
|
+ handle_t *handle;
|
|
|
+ loff_t size = i_size_read(inode);
|
|
|
+
|
|
|
+ WARN_ON(!mutex_is_locked(&inode->i_mutex));
|
|
|
+ if (offset > size || offset + len < size)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (EXT4_I(inode)->i_disksize >= size)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ handle = ext4_journal_start(inode, EXT4_HT_MISC, 1);
|
|
|
+ if (IS_ERR(handle))
|
|
|
+ return PTR_ERR(handle);
|
|
|
+ ext4_update_i_disksize(inode, size);
|
|
|
+ ext4_mark_inode_dirty(handle, inode);
|
|
|
+ ext4_journal_stop(handle);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* ext4_punch_hole: punches a hole in a file by releaseing the blocks
|
|
|
* associated with the given offset and length
|
|
|
@@ -3636,9 +3665,13 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
|
|
|
last_block_offset = round_down((offset + length), sb->s_blocksize) - 1;
|
|
|
|
|
|
/* Now release the pages and zero block aligned part of pages*/
|
|
|
- if (last_block_offset > first_block_offset)
|
|
|
+ if (last_block_offset > first_block_offset) {
|
|
|
+ ret = ext4_update_disksize_before_punch(inode, offset, length);
|
|
|
+ if (ret)
|
|
|
+ goto out_dio;
|
|
|
truncate_pagecache_range(inode, first_block_offset,
|
|
|
last_block_offset);
|
|
|
+ }
|
|
|
|
|
|
if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
|
|
|
credits = ext4_writepage_trans_blocks(inode);
|