ext4: zero out small extents when writing to prealloc area.
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Thu, 17 Apr 2008 14:38:59 +0000 (10:38 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 17 Apr 2008 14:38:59 +0000 (10:38 -0400)
If the preallocated area is small zero out the full extent
instead of splitting them. This should avoid the "write
every alternate block" problem that could grow the number
of extents dramatically.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Mingming Cao <cmm@us.ibm.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
fs/ext4/extents.c

index d279ea8c4661fd4fa574bf00d49e637be2cea2d2..668d82e494dd081ea36c5cbfd381b83474ca707c 100644 (file)
@@ -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