aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2014-09-01 14:32:09 -0400
committerTheodore Ts'o <tytso@mit.edu>2014-09-01 14:32:09 -0400
commit713e8dde3e71e92db2d8cc8459d236ce1fb576ce (patch)
tree775c6493db0b4a15cd9e33d86f8981b3e27332df /fs/ext4
parent19008f6dfa16d23afcd09dceaa598bb6da8de4b1 (diff)
ext4: fix ZERO_RANGE bug hidden by flag aliasing
We accidently aliased EXT4_EX_NOCACHE and EXT4_GET_CONVERT_UNWRITTEN falgs, which apparently was hiding a bug that was unmasked when this flag aliasing issue was addressed (see the subsequent commit). The reproduction case was: fsx -N 10000 -l 500000 -r 4096 -t 4096 -w 4096 -Z -R -W /vdb/junk ... which would cause fsx to report corruption in the data file. The fix we have is a bit of an overkill, but I'd much rather be conservative for now, and we can optimize ZERO_RANGE_FL handling later. The fact that we need to zap the extent_status cache for the inode is unfortunate, but correctness is far more important than performance. Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: Namjae Jeon <namjae.jeon@samsung.com>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/extents.c21
1 files changed, 14 insertions, 7 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index d00937336f19..bf205f72be35 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4802,7 +4802,8 @@ static long ext4_zero_range(struct file *file, loff_t offset,
4802 max_blocks -= lblk; 4802 max_blocks -= lblk;
4803 4803
4804 flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT | 4804 flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT |
4805 EXT4_GET_BLOCKS_CONVERT_UNWRITTEN; 4805 EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
4806 EXT4_EX_NOCACHE;
4806 if (mode & FALLOC_FL_KEEP_SIZE) 4807 if (mode & FALLOC_FL_KEEP_SIZE)
4807 flags |= EXT4_GET_BLOCKS_KEEP_SIZE; 4808 flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
4808 4809
@@ -4840,15 +4841,21 @@ static long ext4_zero_range(struct file *file, loff_t offset,
4840 ext4_inode_block_unlocked_dio(inode); 4841 ext4_inode_block_unlocked_dio(inode);
4841 inode_dio_wait(inode); 4842 inode_dio_wait(inode);
4842 4843
4844 ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
4845 flags, mode);
4846 if (ret)
4847 goto out_dio;
4843 /* 4848 /*
4844 * Remove entire range from the extent status tree. 4849 * Remove entire range from the extent status tree.
4850 *
4851 * ext4_es_remove_extent(inode, lblk, max_blocks) is
4852 * NOT sufficient. I'm not sure why this is the case,
4853 * but let's be conservative and remove the extent
4854 * status tree for the entire inode. There should be
4855 * no outstanding delalloc extents thanks to the
4856 * filemap_write_and_wait_range() call above.
4845 */ 4857 */
4846 ret = ext4_es_remove_extent(inode, lblk, max_blocks); 4858 ret = ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
4847 if (ret)
4848 goto out_dio;
4849
4850 ret = ext4_alloc_file_blocks(file, lblk, max_blocks, new_size,
4851 flags, mode);
4852 if (ret) 4859 if (ret)
4853 goto out_dio; 4860 goto out_dio;
4854 } 4861 }