ext4: cache extent hole in extent status tree for ext4_da_map_blocks()
authorZheng Liu <wenqing.lz@taobao.com>
Tue, 25 Nov 2014 16:44:37 +0000 (11:44 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 25 Nov 2014 16:44:37 +0000 (11:44 -0500)
Currently extent status tree doesn't cache extent hole when a write
looks up in extent tree to make sure whether a block has been allocated
or not.  In this case, we don't put extent hole in extent cache because
later this extent might be removed and a new delayed extent might be
added back.  But it will cause a defect when we do a lot of writes.  If
we don't put extent hole in extent cache, the following writes also need
to access extent tree to look at whether or not a block has been
allocated.  It brings a cache miss.  This commit fixes this defect.
Also if the inode doesn't have any extent, this extent hole will be
cached as well.

Cc: Andreas Dilger <adilger.kernel@dilger.ca>
Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/ext4.h
fs/ext4/extents.c
fs/ext4/inode.c
include/trace/events/ext4.h

index 7b3f3b1decff3c9555e4714b98062090e51d7363..98da4cda9d18a5b94cd272a3cf8e5c6249285334 100644 (file)
@@ -556,10 +556,8 @@ enum {
 #define EXT4_GET_BLOCKS_KEEP_SIZE              0x0080
        /* Do not take i_data_sem locking in ext4_map_blocks */
 #define EXT4_GET_BLOCKS_NO_LOCK                        0x0100
-       /* Do not put hole in extent cache */
-#define EXT4_GET_BLOCKS_NO_PUT_HOLE            0x0200
        /* Convert written extents to unwritten */
-#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN      0x0400
+#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN      0x0200
 
 /*
  * The bit position of these flags must not overlap with any of the
index 7ef2f11aca568497653e211853d2ee2cf2134e58..1ee24d74270f155e4ba11d137db752691c1074fd 100644 (file)
@@ -2306,16 +2306,16 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
                                ext4_lblk_t block)
 {
        int depth = ext_depth(inode);
-       unsigned long len = 0;
-       ext4_lblk_t lblock = 0;
+       ext4_lblk_t len;
+       ext4_lblk_t lblock;
        struct ext4_extent *ex;
+       struct extent_status es;
 
        ex = path[depth].p_ext;
        if (ex == NULL) {
-               /*
-                * there is no extent yet, so gap is [0;-] and we
-                * don't cache it
-                */
+               /* there is no extent yet, so gap is [0;-] */
+               lblock = 0;
+               len = EXT_MAX_BLOCKS;
                ext_debug("cache gap(whole file):");
        } else if (block < le32_to_cpu(ex->ee_block)) {
                lblock = block;
@@ -2324,9 +2324,6 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
                                block,
                                le32_to_cpu(ex->ee_block),
                                 ext4_ext_get_actual_len(ex));
-               if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1))
-                       ext4_es_insert_extent(inode, lblock, len, ~0,
-                                             EXTENT_STATUS_HOLE);
        } else if (block >= le32_to_cpu(ex->ee_block)
                        + ext4_ext_get_actual_len(ex)) {
                ext4_lblk_t next;
@@ -2340,14 +2337,19 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
                                block);
                BUG_ON(next == lblock);
                len = next - lblock;
-               if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1))
-                       ext4_es_insert_extent(inode, lblock, len, ~0,
-                                             EXTENT_STATUS_HOLE);
        } else {
                BUG();
        }
 
-       ext_debug(" -> %u:%lu\n", lblock, len);
+       ext4_es_find_delayed_extent_range(inode, lblock, lblock + len - 1, &es);
+       if (es.es_len) {
+               /* There's delayed extent containing lblock? */
+               if (es.es_lblk <= lblock)
+                       return;
+               len = min(es.es_lblk - lblock, len);
+       }
+       ext_debug(" -> %u:%u\n", lblock, len);
+       ext4_es_insert_extent(inode, lblock, len, ~0, EXTENT_STATUS_HOLE);
 }
 
 /*
@@ -4368,8 +4370,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
                 * put just found gap into cache to speed up
                 * subsequent requests
                 */
-               if ((flags & EXT4_GET_BLOCKS_NO_PUT_HOLE) == 0)
-                       ext4_ext_put_gap_in_cache(inode, path, map->m_lblk);
+               ext4_ext_put_gap_in_cache(inode, path, map->m_lblk);
                goto out2;
        }
 
index 2315e45161eef12d499b9aec7b889ff6c1cfb66c..d5a46a8df70b742c79557242e737a6e3b442d148 100644 (file)
@@ -1432,11 +1432,9 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
        if (ext4_has_inline_data(inode))
                retval = 0;
        else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
-               retval = ext4_ext_map_blocks(NULL, inode, map,
-                                            EXT4_GET_BLOCKS_NO_PUT_HOLE);
+               retval = ext4_ext_map_blocks(NULL, inode, map, 0);
        else
-               retval = ext4_ind_map_blocks(NULL, inode, map,
-                                            EXT4_GET_BLOCKS_NO_PUT_HOLE);
+               retval = ext4_ind_map_blocks(NULL, inode, map, 0);
 
 add_delayed:
        if (retval == 0) {
index bb7dcbe996523dd723cd99f77b0189937db69f5b..cd37a584ee887fd26434acb71d55f97751611653 100644 (file)
@@ -43,8 +43,7 @@ struct extent_status;
        { EXT4_GET_BLOCKS_METADATA_NOFAIL,      "METADATA_NOFAIL" },    \
        { EXT4_GET_BLOCKS_NO_NORMALIZE,         "NO_NORMALIZE" },       \
        { EXT4_GET_BLOCKS_KEEP_SIZE,            "KEEP_SIZE" },          \
-       { EXT4_GET_BLOCKS_NO_LOCK,              "NO_LOCK" },            \
-       { EXT4_GET_BLOCKS_NO_PUT_HOLE,          "NO_PUT_HOLE" })
+       { EXT4_GET_BLOCKS_NO_LOCK,              "NO_LOCK" })
 
 #define show_mflags(flags) __print_flags(flags, "",    \
        { EXT4_MAP_NEW,         "N" },                  \