summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/ext4/extents.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index d279ea8c4661..668d82e494dd 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2212,6 +2212,8 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
return ret;
}
+#define EXT4_EXT_ZERO_LEN 7
+
/*
* This function is called by ext4_ext_get_blocks() if someone tries to write
* to an uninitialized extent. It may result in splitting the uninitialized
@@ -2254,6 +2256,18 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
err = ext4_ext_get_access(handle, inode, path + depth);
if (err)
goto out;
+ /* If extent has less than 2*EXT4_EXT_ZERO_LEN zerout directly */
+ if (ee_len <= 2*EXT4_EXT_ZERO_LEN) {
+ err = ext4_ext_zeroout(inode, &orig_ex);
+ if (err)
+ goto fix_extent_len;
+ /* update the extent length and mark as initialized */
+ ex->ee_block = orig_ex.ee_block;
+ ex->ee_len = orig_ex.ee_len;
+ ext4_ext_store_pblock(ex, ext_pblock(&orig_ex));
+ ext4_ext_dirty(handle, inode, path + depth);
+ return le16_to_cpu(ex->ee_len);
+ }
/* ex1: ee_block to iblock - 1 : uninitialized */
if (iblock > ee_block) {
@@ -2272,6 +2286,38 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
/* ex3: to ee_block + ee_len : uninitialised */
if (allocated > max_blocks) {
unsigned int newdepth;
+ /* If extent has less than EXT4_EXT_ZERO_LEN zerout directly */
+ if (allocated <= EXT4_EXT_ZERO_LEN) {
+ /* Mark first half uninitialized.
+ * Mark second half initialized and zero out the
+ * initialized extent
+ */
+ ex->ee_block = orig_ex.ee_block;
+ ex->ee_len = cpu_to_le16(ee_len - allocated);
+ ext4_ext_mark_uninitialized(ex);
+ ext4_ext_store_pblock(ex, ext_pblock(&orig_ex));
+ ext4_ext_dirty(handle, inode, path + depth);
+
+ ex3 = &newex;
+ ex3->ee_block = cpu_to_le32(iblock);
+ ext4_ext_store_pblock(ex3, newblock);
+ ex3->ee_len = cpu_to_le16(allocated);
+ err = ext4_ext_insert_extent(handle, inode, path, ex3);
+ if (err == -ENOSPC) {
+ err = ext4_ext_zeroout(inode, &orig_ex);
+ if (err)
+ goto fix_extent_len;
+ ex->ee_block = orig_ex.ee_block;
+ ex->ee_len = orig_ex.ee_len;
+ ext4_ext_store_pblock(ex, ext_pblock(&orig_ex));
+ ext4_ext_dirty(handle, inode, path + depth);
+ return le16_to_cpu(ex->ee_len);
+
+ } else if (err)
+ goto fix_extent_len;
+
+ return allocated;
+ }
ex3 = &newex;
ex3->ee_block = cpu_to_le32(iblock + max_blocks);
ext4_ext_store_pblock(ex3, newblock + max_blocks);
@@ -2320,6 +2366,23 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
goto out;
}
allocated = max_blocks;
+
+ /* If extent has less than EXT4_EXT_ZERO_LEN and we are trying
+ * to insert a extent in the middle zerout directly
+ * otherwise give the extent a chance to merge to left
+ */
+ if (le16_to_cpu(orig_ex.ee_len) <= EXT4_EXT_ZERO_LEN &&
+ iblock != ee_block) {
+ err = ext4_ext_zeroout(inode, &orig_ex);
+ if (err)
+ goto fix_extent_len;
+ /* update the extent length and mark as initialized */
+ ex->ee_block = orig_ex.ee_block;
+ ex->ee_len = orig_ex.ee_len;
+ ext4_ext_store_pblock(ex, ext_pblock(&orig_ex));
+ ext4_ext_dirty(handle, inode, path + depth);
+ return le16_to_cpu(ex->ee_len);
+ }
}
/*
* If there was a change of depth as part of the