aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllison Henderson <achender@linux.vnet.ibm.com>2011-09-03 11:55:59 -0400
committerTheodore Ts'o <tytso@mit.edu>2011-09-03 11:55:59 -0400
commitba06208a1315ab2d2217e09c79582b886c9f629e (patch)
tree08410f6be28c6811aa17b1cde65cc31e79e01e83
parent4e96b2dbbf1d7e81f22047a50f862555a6cb87cb (diff)
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 <achender@linux.vnet.ibm.com>
-rw-r--r--fs/ext4/extents.c61
1 files changed, 39 insertions, 22 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 57cf568a98ab..18f7e04a4fa3 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4162,17 +4162,14 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
4162 struct address_space *mapping = inode->i_mapping; 4162 struct address_space *mapping = inode->i_mapping;
4163 struct ext4_map_blocks map; 4163 struct ext4_map_blocks map;
4164 handle_t *handle; 4164 handle_t *handle;
4165 loff_t first_block_offset, last_block_offset, block_len; 4165 loff_t first_page, last_page, page_len;
4166 loff_t first_page, last_page, first_page_offset, last_page_offset; 4166 loff_t first_page_offset, last_page_offset;
4167 int ret, credits, blocks_released, err = 0; 4167 int ret, credits, blocks_released, err = 0;
4168 4168
4169 first_block = (offset + sb->s_blocksize - 1) >> 4169 first_block = (offset + sb->s_blocksize - 1) >>
4170 EXT4_BLOCK_SIZE_BITS(sb); 4170 EXT4_BLOCK_SIZE_BITS(sb);
4171 last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb); 4171 last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);
4172 4172
4173 first_block_offset = first_block << EXT4_BLOCK_SIZE_BITS(sb);
4174 last_block_offset = last_block << EXT4_BLOCK_SIZE_BITS(sb);
4175
4176 first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; 4173 first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
4177 last_page = (offset + length) >> PAGE_CACHE_SHIFT; 4174 last_page = (offset + length) >> PAGE_CACHE_SHIFT;
4178 4175
@@ -4211,24 +4208,44 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
4211 goto out; 4208 goto out;
4212 4209
4213 /* 4210 /*
4214 * Now we need to zero out the un block aligned data. 4211 * Now we need to zero out the non-page-aligned data in the
4215 * If the file is smaller than a block, just 4212 * pages at the start and tail of the hole, and unmap the buffer
4216 * zero out the middle 4213 * heads for the block aligned regions of the page that were
4214 * completely zeroed.
4217 */ 4215 */
4218 if (first_block > last_block) 4216 if (first_page > last_page) {
4219 ext4_block_zero_page_range(handle, mapping, offset, length); 4217 /*
4220 else { 4218 * If the file space being truncated is contained within a page
4221 /* zero out the head of the hole before the first block */ 4219 * just zero out and unmap the middle of that page
4222 block_len = first_block_offset - offset; 4220 */
4223 if (block_len > 0) 4221 err = ext4_discard_partial_page_buffers(handle,
4224 ext4_block_zero_page_range(handle, mapping, 4222 mapping, offset, length, 0);
4225 offset, block_len); 4223
4226 4224 if (err)
4227 /* zero out the tail of the hole after the last block */ 4225 goto out;
4228 block_len = offset + length - last_block_offset; 4226 } else {
4229 if (block_len > 0) { 4227 /*
4230 ext4_block_zero_page_range(handle, mapping, 4228 * zero out and unmap the partial page that contains
4231 last_block_offset, block_len); 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;
4232 } 4249 }
4233 } 4250 }
4234 4251