Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f8e0a7c authored by Zheng Liu's avatar Zheng Liu Committed by Theodore Ts'o
Browse files

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: default avatarZheng Liu <wenqing.lz@taobao.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent cbd7584e
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -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
+16 −15
Original line number Diff line number Diff line
@@ -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,7 +4370,6 @@ 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);
		goto out2;
	}
+2 −4
Original line number Diff line number Diff line
@@ -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) {
+1 −2
Original line number Diff line number Diff line
@@ -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" },			\