1 ext4: cache extent hole in extent status tree for ext4_da_map_blocks()
3 From: Zheng Liu <wenqing.lz@taobao.com>
5 Currently extent status tree doesn't cache extent hole when a write
6 looks up in extent tree to make sure whether a block has been allocated
7 or not. In this case, we don't put extent hole in extent cache because
8 later this extent might be removed and a new delayed extent might be
9 added back. But it will cause a defect when we do a lot of writes. If
10 we don't put extent hole in extent cache, the following writes also need
11 to access extent tree to look at whether or not a block has been
12 allocated. It brings a cache miss. This commit fixes this defect.
13 Also if the inode doesn't have any extent, this extent hole will be
16 Cc: Andreas Dilger <adilger.kernel@dilger.ca>
17 Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
18 Signed-off-by: Jan Kara <jack@suse.cz>
19 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
21 fs/ext4/ext4.h | 4 +---
22 fs/ext4/extents.c | 31 ++++++++++++++++---------------
23 fs/ext4/inode.c | 6 ++----
24 include/trace/events/ext4.h | 3 +--
25 4 files changed, 20 insertions(+), 24 deletions(-)
27 diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
28 index 7b3f3b1..98da4cd 100644
31 @@ -556,10 +556,8 @@ enum {
32 #define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080
33 /* Do not take i_data_sem locking in ext4_map_blocks */
34 #define EXT4_GET_BLOCKS_NO_LOCK 0x0100
35 - /* Do not put hole in extent cache */
36 -#define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200
37 /* Convert written extents to unwritten */
38 -#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0400
39 +#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0200
42 * The bit position of these flags must not overlap with any of the
43 diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
44 index 7ef2f11..1ee24d7 100644
45 --- a/fs/ext4/extents.c
46 +++ b/fs/ext4/extents.c
47 @@ -2306,16 +2306,16 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
50 int depth = ext_depth(inode);
51 - unsigned long len = 0;
52 - ext4_lblk_t lblock = 0;
55 struct ext4_extent *ex;
56 + struct extent_status es;
58 ex = path[depth].p_ext;
61 - * there is no extent yet, so gap is [0;-] and we
64 + /* there is no extent yet, so gap is [0;-] */
66 + len = EXT_MAX_BLOCKS;
67 ext_debug("cache gap(whole file):");
68 } else if (block < le32_to_cpu(ex->ee_block)) {
70 @@ -2324,9 +2324,6 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
72 le32_to_cpu(ex->ee_block),
73 ext4_ext_get_actual_len(ex));
74 - if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1))
75 - ext4_es_insert_extent(inode, lblock, len, ~0,
76 - EXTENT_STATUS_HOLE);
77 } else if (block >= le32_to_cpu(ex->ee_block)
78 + ext4_ext_get_actual_len(ex)) {
80 @@ -2340,14 +2337,19 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
82 BUG_ON(next == lblock);
84 - if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1))
85 - ext4_es_insert_extent(inode, lblock, len, ~0,
86 - EXTENT_STATUS_HOLE);
91 - ext_debug(" -> %u:%lu\n", lblock, len);
92 + ext4_es_find_delayed_extent_range(inode, lblock, lblock + len - 1, &es);
94 + /* There's delayed extent containing lblock? */
95 + if (es.es_lblk <= lblock)
97 + len = min(es.es_lblk - lblock, len);
99 + ext_debug(" -> %u:%u\n", lblock, len);
100 + ext4_es_insert_extent(inode, lblock, len, ~0, EXTENT_STATUS_HOLE);
104 @@ -4368,8 +4370,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
105 * put just found gap into cache to speed up
106 * subsequent requests
108 - if ((flags & EXT4_GET_BLOCKS_NO_PUT_HOLE) == 0)
109 - ext4_ext_put_gap_in_cache(inode, path, map->m_lblk);
110 + ext4_ext_put_gap_in_cache(inode, path, map->m_lblk);
114 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
115 index ddd8e43..7bd272f 100644
116 --- a/fs/ext4/inode.c
117 +++ b/fs/ext4/inode.c
118 @@ -1432,11 +1432,9 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
119 if (ext4_has_inline_data(inode))
121 else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
122 - retval = ext4_ext_map_blocks(NULL, inode, map,
123 - EXT4_GET_BLOCKS_NO_PUT_HOLE);
124 + retval = ext4_ext_map_blocks(NULL, inode, map, 0);
126 - retval = ext4_ind_map_blocks(NULL, inode, map,
127 - EXT4_GET_BLOCKS_NO_PUT_HOLE);
128 + retval = ext4_ind_map_blocks(NULL, inode, map, 0);
132 diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h
133 index 06343e1..d71b00d3 100644
134 --- a/include/trace/events/ext4.h
135 +++ b/include/trace/events/ext4.h
136 @@ -43,8 +43,7 @@ struct extent_status;
137 { EXT4_GET_BLOCKS_METADATA_NOFAIL, "METADATA_NOFAIL" }, \
138 { EXT4_GET_BLOCKS_NO_NORMALIZE, "NO_NORMALIZE" }, \
139 { EXT4_GET_BLOCKS_KEEP_SIZE, "KEEP_SIZE" }, \
140 - { EXT4_GET_BLOCKS_NO_LOCK, "NO_LOCK" }, \
141 - { EXT4_GET_BLOCKS_NO_PUT_HOLE, "NO_PUT_HOLE" })
142 + { EXT4_GET_BLOCKS_NO_LOCK, "NO_LOCK" })
144 #define show_mflags(flags) __print_flags(flags, "", \
145 { EXT4_MAP_NEW, "N" }, \