Skip to content

Commit ba06208

Browse files
achendertytso
authored andcommitted
ext4: fix xfstests 75, 112, 127 punch hole failure
This patch addresses a bug found by xfstests 75, 112, 127 when blocksize = 1k This bug happens because the punch hole code only zeros out non block aligned regions of the page. This means that if the blocks are smaller than a page, then the block aligned regions of the page inside the hole are left un-zeroed, and their buffer heads are still mapped. This bug is corrected by using ext4_discard_partial_page_buffers to properly zero the partial page at the head and tail of the hole, and unmap the corresponding buffer heads This patch also addresses a bug reported by Lukas while working on a new patch to add discard support for loop devices using punch hole. The bug happened because of the first and last block number needed to be cast to a larger data type before calculating the byte offset, but since now we only need the byte offsets of the pages, we no longer even need to be calculating the byte offsets of the blocks. The code to do the block offset calculations is removed in this patch. Signed-off-by: Allison Henderson <[email protected]>
1 parent 4e96b2d commit ba06208

File tree

1 file changed

+39
-22
lines changed

1 file changed

+39
-22
lines changed

fs/ext4/extents.c

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4162,17 +4162,14 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
41624162
struct address_space *mapping = inode->i_mapping;
41634163
struct ext4_map_blocks map;
41644164
handle_t *handle;
4165-
loff_t first_block_offset, last_block_offset, block_len;
4166-
loff_t first_page, last_page, first_page_offset, last_page_offset;
4165+
loff_t first_page, last_page, page_len;
4166+
loff_t first_page_offset, last_page_offset;
41674167
int ret, credits, blocks_released, err = 0;
41684168

41694169
first_block = (offset + sb->s_blocksize - 1) >>
41704170
EXT4_BLOCK_SIZE_BITS(sb);
41714171
last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);
41724172

4173-
first_block_offset = first_block << EXT4_BLOCK_SIZE_BITS(sb);
4174-
last_block_offset = last_block << EXT4_BLOCK_SIZE_BITS(sb);
4175-
41764173
first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
41774174
last_page = (offset + length) >> PAGE_CACHE_SHIFT;
41784175

@@ -4211,24 +4208,44 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
42114208
goto out;
42124209

42134210
/*
4214-
* Now we need to zero out the un block aligned data.
4215-
* If the file is smaller than a block, just
4216-
* zero out the middle
4211+
* Now we need to zero out the non-page-aligned data in the
4212+
* pages at the start and tail of the hole, and unmap the buffer
4213+
* heads for the block aligned regions of the page that were
4214+
* completely zeroed.
42174215
*/
4218-
if (first_block > last_block)
4219-
ext4_block_zero_page_range(handle, mapping, offset, length);
4220-
else {
4221-
/* zero out the head of the hole before the first block */
4222-
block_len = first_block_offset - offset;
4223-
if (block_len > 0)
4224-
ext4_block_zero_page_range(handle, mapping,
4225-
offset, block_len);
4226-
4227-
/* zero out the tail of the hole after the last block */
4228-
block_len = offset + length - last_block_offset;
4229-
if (block_len > 0) {
4230-
ext4_block_zero_page_range(handle, mapping,
4231-
last_block_offset, block_len);
4216+
if (first_page > last_page) {
4217+
/*
4218+
* If the file space being truncated is contained within a page
4219+
* just zero out and unmap the middle of that page
4220+
*/
4221+
err = ext4_discard_partial_page_buffers(handle,
4222+
mapping, offset, length, 0);
4223+
4224+
if (err)
4225+
goto out;
4226+
} else {
4227+
/*
4228+
* zero out and unmap the partial page that contains
4229+
* the start of the hole
4230+
*/
4231+
page_len = first_page_offset - offset;
4232+
if (page_len > 0) {
4233+
err = ext4_discard_partial_page_buffers(handle, mapping,
4234+
offset, page_len, 0);
4235+
if (err)
4236+
goto out;
4237+
}
4238+
4239+
/*
4240+
* zero out and unmap the partial page that contains
4241+
* the end of the hole
4242+
*/
4243+
page_len = offset + length - last_page_offset;
4244+
if (page_len > 0) {
4245+
err = ext4_discard_partial_page_buffers(handle, mapping,
4246+
last_page_offset, page_len, 0);
4247+
if (err)
4248+
goto out;
42324249
}
42334250
}
42344251

0 commit comments

Comments
 (0)