| From 8b4b19a2f96348d70bfa306ef7d4a13b0bcbea79 Mon Sep 17 00:00:00 2001 |
| From: Zhang Yi <yi.zhang@huawei.com> |
| Date: Sat, 29 Nov 2025 18:32:37 +0800 |
| Subject: ext4: don't cache extent during splitting extent |
| |
| From: Zhang Yi <yi.zhang@huawei.com> |
| |
| commit 8b4b19a2f96348d70bfa306ef7d4a13b0bcbea79 upstream. |
| |
| Caching extents during the splitting process is risky, as it may result |
| in stale extents remaining in the status tree. Moreover, in most cases, |
| the corresponding extent block entries are likely already cached before |
| the split happens, making caching here not particularly useful. |
| |
| Assume we have an unwritten extent, and then DIO writes the first half. |
| |
| [UUUUUUUUUUUUUUUU] on-disk extent U: unwritten extent |
| [UUUUUUUUUUUUUUUU] extent status tree |
| |<- ->| ----> dio write this range |
| |
| First, when ext4_split_extent_at() splits this extent, it truncates the |
| existing extent and then inserts a new one. During this process, this |
| extent status entry may be shrunk, and calls to ext4_find_extent() and |
| ext4_cache_extents() may occur, which could potentially insert the |
| truncated range as a hole into the extent status tree. After the split |
| is completed, this hole is not replaced with the correct status. |
| |
| [UUUUUUU|UUUUUUUU] on-disk extent U: unwritten extent |
| [UUUUUUU|HHHHHHHH] extent status tree H: hole |
| |
| Then, the outer calling functions will not correct this remaining hole |
| extent either. Finally, if we perform a delayed buffer write on this |
| latter part, it will re-insert the delayed extent and cause an error in |
| space accounting. |
| |
| In adition, if the unwritten extent cache is not shrunk during the |
| splitting, ext4_cache_extents() also conflicts with existing extents |
| when caching extents. In the future, we will add checks when caching |
| extents, which will trigger a warning. Therefore, Do not cache extents |
| that are being split. |
| |
| Signed-off-by: Zhang Yi <yi.zhang@huawei.com> |
| Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com> |
| Reviewed-by: Baokun Li <libaokun1@huawei.com> |
| Cc: stable@kernel.org |
| Message-ID: <20251129103247.686136-6-yi.zhang@huaweicloud.com> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/extents.c | 6 ++++++ |
| 1 file changed, 6 insertions(+) |
| |
| --- a/fs/ext4/extents.c |
| +++ b/fs/ext4/extents.c |
| @@ -3173,6 +3173,9 @@ static int ext4_split_extent_at(handle_t |
| BUG_ON((split_flag & (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)) == |
| (EXT4_EXT_DATA_VALID1 | EXT4_EXT_DATA_VALID2)); |
| |
| + /* Do not cache extents that are in the process of being modified. */ |
| + flags |= EXT4_EX_NOCACHE; |
| + |
| ext_debug(inode, "logical block %llu\n", (unsigned long long)split); |
| |
| ext4_ext_show_leaf(inode, path); |
| @@ -3343,6 +3346,9 @@ static int ext4_split_extent(handle_t *h |
| ee_len = ext4_ext_get_actual_len(ex); |
| unwritten = ext4_ext_is_unwritten(ex); |
| |
| + /* Do not cache extents that are in the process of being modified. */ |
| + flags |= EXT4_EX_NOCACHE; |
| + |
| if (map->m_lblk + map->m_len < ee_block + ee_len) { |
| split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT; |
| flags1 = flags | EXT4_GET_BLOCKS_PRE_IO; |