From: Zhang Yi Make ext4_map_blocks() start its own transaction handle when the caller does not provide one. The handle is started after the lookup path confirms that allocation is actually needed, and is stopped at the unified out exit path. This avoids unnecessarily starting a handle for pure mapping queries. This prepares for the buffered iomap writeback conversion, which improves performance for fragile overwrite cases. Suggested-by: Jan Kara Link: https://lore.kernel.org/linux-ext4/i536qqwj5eyulec3r2ki2ycnelqdd4bkpat2drn7t72t6p622k@ktueynysgo3j/ Signed-off-by: Zhang Yi Reviewed-by: Ojaswin Mujoo --- fs/ext4/inode.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 64d223eda0ffe..553b767148c7b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -686,6 +686,11 @@ int ext4_map_create_blocks(handle_t *handle, struct inode *inode, * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping * based files * + * ext4_map_blocks() may also be called with a NULL @handle. In that case + * it starts its own transaction handle only when block allocation is + * actually needed, i.e. after the lookup confirms the blocks are not yet + * mapped, and stops it before returning. + * * On success, it returns the number of blocks being mapped or allocated. * If flags doesn't contain EXT4_GET_BLOCKS_CREATE the blocks are * pre-allocated and unwritten, the resulting @map is marked as unwritten. @@ -703,6 +708,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, struct extent_status es; int retval; int ret = 0; + bool internal_handle = false; unsigned int orig_mlen; #ifdef ES_AGGRESSIVE_TEST struct ext4_map_blocks orig_map; @@ -787,13 +793,13 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, retval = ext4_map_query_blocks(handle, inode, map, flags); up_read((&EXT4_I(inode)->i_data_sem)); if (retval < 0) - return retval; + goto out; found: if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { ret = check_block_validity(inode, map); if (ret != 0) - return ret; + goto out; } /* If it is only a block(s) look up */ @@ -813,8 +819,17 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, * ext4_ext_map_blocks() */ if (!(flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) - return retval; + goto out; + if (!handle) { + handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, + ext4_chunk_trans_blocks(inode, orig_mlen)); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + goto out; + } + internal_handle = true; + } ext4_fc_track_inode(handle, inode); /* @@ -845,12 +860,12 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, if (retval < 0) ext_debug(inode, "failed with err %d\n", retval); if (retval <= 0) - return retval; + goto out; if (map->m_flags & EXT4_MAP_MAPPED) { ret = check_block_validity(inode, map); if (ret != 0) - return ret; + goto out; /* * Inodes with freshly allocated blocks where contents will be @@ -872,12 +887,16 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ret = ext4_jbd2_inode_add_write(handle, inode, start_byte, length); if (ret) - return ret; + goto out; } } ext4_fc_track_range(handle, inode, map->m_lblk, map->m_lblk + map->m_len - 1); - return retval; +out: + if (internal_handle) + ext4_journal_stop(handle); + + return ret ? ret : retval; } /* -- 2.52.0