]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ext4: fix xfstests 75, 112, 127 punch hole failure
authorAllison Henderson <achender@linux.vnet.ibm.com>
Mon, 22 Aug 2011 19:00:53 +0000 (15:00 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 22 Aug 2011 19:00:53 +0000 (15:00 -0400)
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

Signed-off-by: Allison Henderson <achender@linux.vnet.ibm.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/extents.c

index 57cf568a98ab652afcfe3581d093d5f2b5758689..6f653af2d898b30b759794fca802a5370d24dc8a 100644 (file)
@@ -4162,7 +4162,7 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
        struct address_space *mapping = inode->i_mapping;
        struct ext4_map_blocks map;
        handle_t *handle;
-       loff_t first_block_offset, last_block_offset, block_len;
+       loff_t first_block_offset, last_block_offset, page_len;
        loff_t first_page, last_page, first_page_offset, last_page_offset;
        int ret, credits, blocks_released, err = 0;
 
@@ -4211,24 +4211,36 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
                goto out;
 
        /*
-        * Now we need to zero out the un block aligned data.
-        * If the file is smaller than a block, just
-        * zero out the middle
+        * Now we need to zero out the non-page-aligned data in the
+        * pages at the start and tail of the hole, and unmap the buffer
+        * heads for the block aligned regions of the page that were
+        * completely zeroed.
         */
-       if (first_block > last_block)
-               ext4_block_zero_page_range(handle, mapping, offset, length);
-       else {
-               /* zero out the head of the hole before the first block */
-               block_len  = first_block_offset - offset;
-               if (block_len > 0)
-                       ext4_block_zero_page_range(handle, mapping,
-                                                  offset, block_len);
-
-               /* zero out the tail of the hole after the last block */
-               block_len = offset + length - last_block_offset;
-               if (block_len > 0) {
-                       ext4_block_zero_page_range(handle, mapping,
-                                       last_block_offset, block_len);
+       if (first_page > last_page) {
+               /*
+                * If the file space being truncated is contained within a page
+                * just zero out and unmap the middle of that page
+                */
+               ext4_discard_partial_page_buffers(handle,
+                       mapping, offset, length, 0);
+       } else {
+               /*
+                * zero out and unmap the partial page that contains
+                * the start of the hole
+                */
+               page_len  = first_page_offset - offset;
+               if (page_len > 0)
+                       ext4_discard_partial_page_buffers(handle, mapping,
+                                                  offset, page_len, 0);
+
+               /*
+                * zero out and unmap the partial page that contains
+                * the end of the hole
+                */
+               page_len = offset + length - last_page_offset;
+               if (page_len > 0) {
+                       ext4_discard_partial_page_buffers(handle, mapping,
+                                       last_page_offset, page_len, 0);
                }
        }