Skip to content

Commit 2be4751

Browse files
achendertytso
authored andcommitted
ext4: fix 2nd xfstests 127 punch hole failure
This patch fixes a second punch hole bug found by xfstests 127. This bug happens because punch hole needs to flush the pages of the hole to avoid race conditions. But if the end of the hole is in the same page as i_size, the buffer heads beyond i_size need to be unmapped and the page needs to be zeroed after it is flushed. To correct this, the new ext4_discard_partial_page_buffers routine is used to zero and unmap the partial page beyond i_size if the end of the hole appears in the same page as i_size. The code has also been optimized to set the end of the hole to the page after i_size if the specified hole exceeds i_size, and the code that flushes the pages has been simplified. Signed-off-by: Allison Henderson <[email protected]>
1 parent ba06208 commit 2be4751

File tree

1 file changed

+37
-4
lines changed

1 file changed

+37
-4
lines changed

fs/ext4/extents.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4166,6 +4166,20 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
41664166
loff_t first_page_offset, last_page_offset;
41674167
int ret, credits, blocks_released, err = 0;
41684168

4169+
/* No need to punch hole beyond i_size */
4170+
if (offset >= inode->i_size)
4171+
return 0;
4172+
4173+
/*
4174+
* If the hole extends beyond i_size, set the hole
4175+
* to end after the page that contains i_size
4176+
*/
4177+
if (offset + length > inode->i_size) {
4178+
length = inode->i_size +
4179+
PAGE_CACHE_SIZE - (inode->i_size & (PAGE_CACHE_SIZE - 1)) -
4180+
offset;
4181+
}
4182+
41694183
first_block = (offset + sb->s_blocksize - 1) >>
41704184
EXT4_BLOCK_SIZE_BITS(sb);
41714185
last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);
@@ -4182,11 +4196,10 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
41824196
*/
41834197
if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
41844198
err = filemap_write_and_wait_range(mapping,
4185-
first_page_offset == 0 ? 0 : first_page_offset-1,
4186-
last_page_offset);
4199+
offset, offset + length - 1);
41874200

4188-
if (err)
4189-
return err;
4201+
if (err)
4202+
return err;
41904203
}
41914204

41924205
/* Now release the pages */
@@ -4249,6 +4262,26 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
42494262
}
42504263
}
42514264

4265+
4266+
/*
4267+
* If i_size is contained in the last page, we need to
4268+
* unmap and zero the partial page after i_size
4269+
*/
4270+
if (inode->i_size >> PAGE_CACHE_SHIFT == last_page &&
4271+
inode->i_size % PAGE_CACHE_SIZE != 0) {
4272+
4273+
page_len = PAGE_CACHE_SIZE -
4274+
(inode->i_size & (PAGE_CACHE_SIZE - 1));
4275+
4276+
if (page_len > 0) {
4277+
err = ext4_discard_partial_page_buffers(handle,
4278+
mapping, inode->i_size, page_len, 0);
4279+
4280+
if (err)
4281+
goto out;
4282+
}
4283+
}
4284+
42524285
/* If there are no blocks to remove, return now */
42534286
if (first_block >= last_block)
42544287
goto out;

0 commit comments

Comments
 (0)