|
|
20c2f18 |
From: Lukas Czerner <lczerner@redhat.com>
|
|
|
20c2f18 |
Date: Wed, 18 Feb 2015 17:49:28 +0100
|
|
|
20c2f18 |
Subject: [PATCH] ext4: Allocate entire range in zero range
|
|
|
20c2f18 |
|
|
|
20c2f18 |
Currently there is a bug in zero range code which causes zero range
|
|
|
20c2f18 |
calls to only allocate block aligned portion of the range, while
|
|
|
20c2f18 |
ignoring the rest in some cases.
|
|
|
20c2f18 |
|
|
|
20c2f18 |
In some cases, namely if the end of the range is past isize, we do
|
|
|
20c2f18 |
attempt to preallocate the last nonaligned block. However this might
|
|
|
20c2f18 |
cause kernel to BUG() in some carefully designed zero range requests on
|
|
|
20c2f18 |
setups where page size > block size.
|
|
|
20c2f18 |
|
|
|
20c2f18 |
Fix this problem by first preallocating the entire range, including the
|
|
|
20c2f18 |
nonaligned edges and converting the written extents to unwritten in the
|
|
|
20c2f18 |
next step. This approach will also give us the advantage of having the
|
|
|
20c2f18 |
range to be as linearly contiguous as possible.
|
|
|
20c2f18 |
|
|
|
20c2f18 |
Signed-off-by: Lukas Czerner <lczerner@redhat.com>
|
|
|
20c2f18 |
---
|
|
|
20c2f18 |
fs/ext4/extents.c | 31 +++++++++++++++++++------------
|
|
|
20c2f18 |
1 file changed, 19 insertions(+), 12 deletions(-)
|
|
|
20c2f18 |
|
|
|
20c2f18 |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
|
|
|
20c2f18 |
index 0b16fb4c06d3..e3bf236a36ac 100644
|
|
|
20c2f18 |
--- a/fs/ext4/extents.c
|
|
|
20c2f18 |
+++ b/fs/ext4/extents.c
|
|
|
20c2f18 |
@@ -4792,12 +4792,6 @@ static long ext4_zero_range(struct file *file, loff_t offset,
|
|
|
20c2f18 |
else
|
|
|
20c2f18 |
max_blocks -= lblk;
|
|
|
20c2f18 |
|
|
|
20c2f18 |
- flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT |
|
|
|
20c2f18 |
- EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
|
|
|
20c2f18 |
- EXT4_EX_NOCACHE;
|
|
|
20c2f18 |
- if (mode & FALLOC_FL_KEEP_SIZE)
|
|
|
20c2f18 |
- flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
|
|
|
20c2f18 |
-
|
|
|
20c2f18 |
mutex_lock(&inode->i_mutex);
|
|
|
20c2f18 |
|
|
|
20c2f18 |
/*
|
|
|
20c2f18 |
@@ -4814,15 +4808,28 @@ static long ext4_zero_range(struct file *file, loff_t offset,
|
|
|
20c2f18 |
ret = inode_newsize_ok(inode, new_size);
|
|
|
20c2f18 |
if (ret)
|
|
|
20c2f18 |
goto out_mutex;
|
|
|
20c2f18 |
- /*
|
|
|
20c2f18 |
- * If we have a partial block after EOF we have to allocate
|
|
|
20c2f18 |
- * the entire block.
|
|
|
20c2f18 |
- */
|
|
|
20c2f18 |
- if (partial_end)
|
|
|
20c2f18 |
- max_blocks += 1;
|
|
|
20c2f18 |
}
|
|
|
20c2f18 |
|
|
|
20c2f18 |
+ flags = EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT;
|
|
|
20c2f18 |
+ if (mode & FALLOC_FL_KEEP_SIZE)
|
|
|
20c2f18 |
+ flags |= EXT4_GET_BLOCKS_KEEP_SIZE;
|
|
|
20c2f18 |
+
|
|
|
20c2f18 |
+ /* Preallocate the range including the unaligned edges */
|
|
|
20c2f18 |
+ if (partial_begin || partial_end) {
|
|
|
20c2f18 |
+ ret = ext4_alloc_file_blocks(file,
|
|
|
20c2f18 |
+ round_down(offset, 1 << blkbits) >> blkbits,
|
|
|
20c2f18 |
+ (round_up((offset + len), 1 << blkbits) -
|
|
|
20c2f18 |
+ round_down(offset, 1 << blkbits)) >> blkbits,
|
|
|
20c2f18 |
+ new_size, flags, mode);
|
|
|
20c2f18 |
+ if (ret)
|
|
|
20c2f18 |
+ goto out_mutex;
|
|
|
20c2f18 |
+
|
|
|
20c2f18 |
+ }
|
|
|
20c2f18 |
+
|
|
|
20c2f18 |
+ /* Zero range excluding the unaligned edges */
|
|
|
20c2f18 |
if (max_blocks > 0) {
|
|
|
20c2f18 |
+ flags |= (EXT4_GET_BLOCKS_CONVERT_UNWRITTEN |
|
|
|
20c2f18 |
+ EXT4_EX_NOCACHE);
|
|
|
20c2f18 |
|
|
|
20c2f18 |
/* Now release the pages and zero block aligned part of pages*/
|
|
|
20c2f18 |
truncate_pagecache_range(inode, start, end - 1);
|
|
|
20c2f18 |
--
|
|
|
20c2f18 |
2.1.0
|
|
|
20c2f18 |
|