aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMiao Xie <miaox@cn.fujitsu.com>2012-12-05 05:54:12 -0500
committerChris Mason <chris.mason@fusionio.com>2012-12-16 20:46:19 -0500
commit0061280d2c7240805cfd7b1f493da967c97c2f34 (patch)
tree3808aee5875cd38651a580fe825925de414f345c /fs
parent6347b3c433a4cff00eb2299c7f2c7d1d8b24c1fc (diff)
Btrfs: fix the page that is beyond EOF
Steps to reproduce: # mkfs.btrfs <disk> # mount <disk> <mnt> # dd if=/dev/zero of=<mnt>/<file> bs=512 seek=5 count=8 # fallocate -p -o 2048 -l 16384 <mnt>/<file> # dd if=/dev/zero of=<mnt>/<file> bs=4096 seek=3 count=8 conv=notrunc,nocreat # umount <mnt> # dmesg WARNING: at fs/btrfs/inode.c:7140 btrfs_destroy_inode+0x2eb/0x330 The reason is that we inputed a range which is beyond the end of the file. And because the end of this range was not page-aligned, we had to truncate the last page in this range, this operation is similar to a buffered file write. In other words, we reserved enough space and clear the data which was in the hole range on that page. But when we expanded that test file, write the data into the same page, we forgot that we have reserved enough space for the buffered write of that page because in most cases there is no page that is beyond the end of the file. As a result, we reserved the space twice. In fact, we needn't truncate the page if it is beyond the end of the file, just release the allocated space in that range. Fix the above problem by this way. Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/file.c16
1 files changed, 9 insertions, 7 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index d75412bf7c4a..700ffd266da3 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1859,9 +1859,9 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
1859 struct btrfs_path *path; 1859 struct btrfs_path *path;
1860 struct btrfs_block_rsv *rsv; 1860 struct btrfs_block_rsv *rsv;
1861 struct btrfs_trans_handle *trans; 1861 struct btrfs_trans_handle *trans;
1862 u64 mask = BTRFS_I(inode)->root->sectorsize - 1; 1862 u64 lockstart = round_up(offset, BTRFS_I(inode)->root->sectorsize);
1863 u64 lockstart = (offset + mask) & ~mask; 1863 u64 lockend = round_down(offset + len,
1864 u64 lockend = ((offset + len) & ~mask) - 1; 1864 BTRFS_I(inode)->root->sectorsize) - 1;
1865 u64 cur_offset = lockstart; 1865 u64 cur_offset = lockstart;
1866 u64 min_size = btrfs_calc_trunc_metadata_size(root, 1); 1866 u64 min_size = btrfs_calc_trunc_metadata_size(root, 1);
1867 u64 drop_end; 1867 u64 drop_end;
@@ -1896,10 +1896,12 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
1896 } 1896 }
1897 1897
1898 /* zero the front end of the last page */ 1898 /* zero the front end of the last page */
1899 ret = btrfs_truncate_page(inode, offset + len, 0, 1); 1899 if (offset + len < round_up(inode->i_size, PAGE_CACHE_SIZE)) {
1900 if (ret) { 1900 ret = btrfs_truncate_page(inode, offset + len, 0, 1);
1901 mutex_unlock(&inode->i_mutex); 1901 if (ret) {
1902 return ret; 1902 mutex_unlock(&inode->i_mutex);
1903 return ret;
1904 }
1903 } 1905 }
1904 1906
1905 if (lockend < lockstart) { 1907 if (lockend < lockstart) {