mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-09 06:33:34 +00:00
ext4: cache extent hole in extent status tree for ext4_da_map_blocks()
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>
This commit is contained in:
parent
cbd7584e6e
commit
2f8e0a7c6c
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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" }, \
|
||||
|
Loading…
Reference in New Issue
Block a user