| From 6d882ea3b0931b43530d44149b79fcd4ffc13030 Mon Sep 17 00:00:00 2001 |
| From: Zhang Yi <yi.zhang@huawei.com> |
| Date: Sat, 29 Nov 2025 18:32:38 +0800 |
| Subject: ext4: drop extent cache after doing PARTIAL_VALID1 zeroout |
| |
| From: Zhang Yi <yi.zhang@huawei.com> |
| |
| commit 6d882ea3b0931b43530d44149b79fcd4ffc13030 upstream. |
| |
| When splitting an unwritten extent in the middle and converting it to |
| initialized in ext4_split_extent() with the EXT4_EXT_MAY_ZEROOUT and |
| EXT4_EXT_DATA_VALID2 flags set, it could leave a stale unwritten extent. |
| |
| Assume we have an unwritten file and buffered write in the middle of it |
| without dioread_nolock enabled, it will allocate blocks as written |
| extent. |
| |
| 0 A B N |
| [UUUUUUUUUUUU] on-disk extent U: unwritten extent |
| [UUUUUUUUUUUU] extent status tree |
| [--DDDDDDDD--] D: valid data |
| |<- ->| ----> this range needs to be initialized |
| |
| ext4_split_extent() first try to split this extent at B with |
| EXT4_EXT_DATA_PARTIAL_VALID1 and EXT4_EXT_MAY_ZEROOUT flag set, but |
| ext4_split_extent_at() failed to split this extent due to temporary lack |
| of space. It zeroout B to N and leave the entire extent as unwritten. |
| |
| 0 A B N |
| [UUUUUUUUUUUU] on-disk extent |
| [UUUUUUUUUUUU] extent status tree |
| [--DDDDDDDDZZ] Z: zeroed data |
| |
| ext4_split_extent() then try to split this extent at A with |
| EXT4_EXT_DATA_VALID2 flag set. This time, it split successfully and |
| leave an written extent from A to N. |
| |
| 0 A B N |
| [UUWWWWWWWWWW] on-disk extent W: written extent |
| [UUUUUUUUUUUU] extent status tree |
| [--DDDDDDDDZZ] |
| |
| Finally ext4_map_create_blocks() only insert extent A to B to the extent |
| status tree, and leave an stale unwritten extent in the status tree. |
| |
| 0 A B N |
| [UUWWWWWWWWWW] on-disk extent W: written extent |
| [UUWWWWWWWWUU] extent status tree |
| [--DDDDDDDDZZ] |
| |
| Fix this issue by always cached extent status entry after zeroing out |
| the second part. |
| |
| Signed-off-by: Zhang Yi <yi.zhang@huawei.com> |
| Reviewed-by: Baokun Li <libaokun1@huawei.com> |
| Cc: stable@kernel.org |
| Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com> |
| Message-ID: <20251129103247.686136-7-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 | 10 +++++++++- |
| 1 file changed, 9 insertions(+), 1 deletion(-) |
| |
| --- a/fs/ext4/extents.c |
| +++ b/fs/ext4/extents.c |
| @@ -3319,8 +3319,16 @@ static struct ext4_ext_path *ext4_split_ |
| * extent length and ext4_split_extent() split will the |
| * first half again. |
| */ |
| - if (split_flag & EXT4_EXT_DATA_PARTIAL_VALID1) |
| + if (split_flag & EXT4_EXT_DATA_PARTIAL_VALID1) { |
| + /* |
| + * Drop extent cache to prevent stale unwritten |
| + * extents remaining after zeroing out. |
| + */ |
| + ext4_es_remove_extent(inode, |
| + le32_to_cpu(zero_ex.ee_block), |
| + ext4_ext_get_actual_len(&zero_ex)); |
| goto fix_extent_len; |
| + } |
| |
| /* update the extent length and mark as initialized */ |
| ex->ee_len = cpu_to_le16(ee_len); |