Skip to content

Commit 6430dea

Browse files
zhangyi089tytso
authored andcommitted
ext4: correct the hole length returned by ext4_map_blocks()
In ext4_map_blocks(), if we can't find a range of mapping in the extents cache, we are calling ext4_ext_map_blocks() to search the real path and ext4_ext_determine_hole() to determine the hole range. But if the querying range was partially or completely overlaped by a delalloc extent, we can't find it in the real extent path, so the returned hole length could be incorrect. Fortunately, ext4_ext_put_gap_in_cache() have already handle delalloc extent, but it searches start from the expanded hole_start, doesn't start from the querying range, so the delalloc extent found could not be the one that overlaped the querying range, plus, it also didn't adjust the hole length. Let's just remove ext4_ext_put_gap_in_cache(), handle delalloc and insert adjusted hole extent in ext4_ext_determine_hole(). Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Suggested-by: Jan Kara <jack@suse.cz> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20240127015825.1608160-4-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent acf795d commit 6430dea

File tree

1 file changed

+70
-41
lines changed

1 file changed

+70
-41
lines changed

fs/ext4/extents.c

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,7 +2229,7 @@ static int ext4_fill_es_cache_info(struct inode *inode,
22292229

22302230

22312231
/*
2232-
* ext4_ext_determine_hole - determine hole around given block
2232+
* ext4_ext_find_hole - find hole around given block according to the given path
22332233
* @inode: inode we lookup in
22342234
* @path: path in extent tree to @lblk
22352235
* @lblk: pointer to logical block around which we want to determine hole
@@ -2241,9 +2241,9 @@ static int ext4_fill_es_cache_info(struct inode *inode,
22412241
* The function returns the length of a hole starting at @lblk. We update @lblk
22422242
* to the beginning of the hole if we managed to find it.
22432243
*/
2244-
static ext4_lblk_t ext4_ext_determine_hole(struct inode *inode,
2245-
struct ext4_ext_path *path,
2246-
ext4_lblk_t *lblk)
2244+
static ext4_lblk_t ext4_ext_find_hole(struct inode *inode,
2245+
struct ext4_ext_path *path,
2246+
ext4_lblk_t *lblk)
22472247
{
22482248
int depth = ext_depth(inode);
22492249
struct ext4_extent *ex;
@@ -2270,30 +2270,6 @@ static ext4_lblk_t ext4_ext_determine_hole(struct inode *inode,
22702270
return len;
22712271
}
22722272

2273-
/*
2274-
* ext4_ext_put_gap_in_cache:
2275-
* calculate boundaries of the gap that the requested block fits into
2276-
* and cache this gap
2277-
*/
2278-
static void
2279-
ext4_ext_put_gap_in_cache(struct inode *inode, ext4_lblk_t hole_start,
2280-
ext4_lblk_t hole_len)
2281-
{
2282-
struct extent_status es;
2283-
2284-
ext4_es_find_extent_range(inode, &ext4_es_is_delayed, hole_start,
2285-
hole_start + hole_len - 1, &es);
2286-
if (es.es_len) {
2287-
/* There's delayed extent containing lblock? */
2288-
if (es.es_lblk <= hole_start)
2289-
return;
2290-
hole_len = min(es.es_lblk - hole_start, hole_len);
2291-
}
2292-
ext_debug(inode, " -> %u:%u\n", hole_start, hole_len);
2293-
ext4_es_insert_extent(inode, hole_start, hole_len, ~0,
2294-
EXTENT_STATUS_HOLE);
2295-
}
2296-
22972273
/*
22982274
* ext4_ext_rm_idx:
22992275
* removes index from the index block.
@@ -4062,6 +4038,69 @@ static int get_implied_cluster_alloc(struct super_block *sb,
40624038
return 0;
40634039
}
40644040

4041+
/*
4042+
* Determine hole length around the given logical block, first try to
4043+
* locate and expand the hole from the given @path, and then adjust it
4044+
* if it's partially or completely converted to delayed extents, insert
4045+
* it into the extent cache tree if it's indeed a hole, finally return
4046+
* the length of the determined extent.
4047+
*/
4048+
static ext4_lblk_t ext4_ext_determine_insert_hole(struct inode *inode,
4049+
struct ext4_ext_path *path,
4050+
ext4_lblk_t lblk)
4051+
{
4052+
ext4_lblk_t hole_start, len;
4053+
struct extent_status es;
4054+
4055+
hole_start = lblk;
4056+
len = ext4_ext_find_hole(inode, path, &hole_start);
4057+
again:
4058+
ext4_es_find_extent_range(inode, &ext4_es_is_delayed, hole_start,
4059+
hole_start + len - 1, &es);
4060+
if (!es.es_len)
4061+
goto insert_hole;
4062+
4063+
/*
4064+
* There's a delalloc extent in the hole, handle it if the delalloc
4065+
* extent is in front of, behind and straddle the queried range.
4066+
*/
4067+
if (lblk >= es.es_lblk + es.es_len) {
4068+
/*
4069+
* The delalloc extent is in front of the queried range,
4070+
* find again from the queried start block.
4071+
*/
4072+
len -= lblk - hole_start;
4073+
hole_start = lblk;
4074+
goto again;
4075+
} else if (in_range(lblk, es.es_lblk, es.es_len)) {
4076+
/*
4077+
* The delalloc extent containing lblk, it must have been
4078+
* added after ext4_map_blocks() checked the extent status
4079+
* tree, adjust the length to the delalloc extent's after
4080+
* lblk.
4081+
*/
4082+
len = es.es_lblk + es.es_len - lblk;
4083+
return len;
4084+
} else {
4085+
/*
4086+
* The delalloc extent is partially or completely behind
4087+
* the queried range, update hole length until the
4088+
* beginning of the delalloc extent.
4089+
*/
4090+
len = min(es.es_lblk - hole_start, len);
4091+
}
4092+
4093+
insert_hole:
4094+
/* Put just found gap into cache to speed up subsequent requests */
4095+
ext_debug(inode, " -> %u:%u\n", hole_start, len);
4096+
ext4_es_insert_extent(inode, hole_start, len, ~0, EXTENT_STATUS_HOLE);
4097+
4098+
/* Update hole_len to reflect hole size after lblk */
4099+
if (hole_start != lblk)
4100+
len -= lblk - hole_start;
4101+
4102+
return len;
4103+
}
40654104

40664105
/*
40674106
* Block allocation/map/preallocation routine for extents based files
@@ -4179,22 +4218,12 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
41794218
* we couldn't try to create block if create flag is zero
41804219
*/
41814220
if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
4182-
ext4_lblk_t hole_start, hole_len;
4221+
ext4_lblk_t len;
41834222

4184-
hole_start = map->m_lblk;
4185-
hole_len = ext4_ext_determine_hole(inode, path, &hole_start);
4186-
/*
4187-
* put just found gap into cache to speed up
4188-
* subsequent requests
4189-
*/
4190-
ext4_ext_put_gap_in_cache(inode, hole_start, hole_len);
4223+
len = ext4_ext_determine_insert_hole(inode, path, map->m_lblk);
41914224

4192-
/* Update hole_len to reflect hole size after map->m_lblk */
4193-
if (hole_start != map->m_lblk)
4194-
hole_len -= map->m_lblk - hole_start;
41954225
map->m_pblk = 0;
4196-
map->m_len = min_t(unsigned int, map->m_len, hole_len);
4197-
4226+
map->m_len = min_t(unsigned int, map->m_len, len);
41984227
goto out;
41994228
}
42004229

0 commit comments

Comments
 (0)