From: Zhang Yi <yi.zhang@huawei.com>
When performing buffered writes, we may need to split and convert an
unwritten extent into a written one during the end I/O process. However,
we do not reserve space specifically for these metadata changes, we only
reserve 2% of space or 4096 blocks. To address this, we use
EXT4_GET_BLOCKS_PRE_IO to potentially split extents in advance and
EXT4_GET_BLOCKS_METADATA_NOFAIL to utilize reserved space if necessary.
These two approaches can reduce the likelihood of running out of space
and losing data. However, these methods are merely best efforts, we
could still run out of space, and there is not much difference between
converting an extent during the writeback process and the end I/O
process, it won't increase the rick of losing data if we postpone the
conversion.
Therefore, also use EXT4_GET_BLOCKS_METADATA_NOFAIL in
ext4_convert_unwritten_extents_endio() to prepare for the buffered I/O
iomap conversion, which may perform extent conversion during the end I/O
process.
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
---
fs/ext4/extents.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 27eb2c1df012..e53959120b04 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3794,6 +3794,8 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
* illegal.
*/
if (ee_block != map->m_lblk || ee_len > map->m_len) {
+ int flags = EXT4_GET_BLOCKS_CONVERT |
+ EXT4_GET_BLOCKS_METADATA_NOFAIL;
#ifdef CONFIG_EXT4_DEBUG
ext4_warning(inode->i_sb, "Inode (%ld) finished: extent logical block %llu,"
" len %u; IO logical block %llu, len %u",
@@ -3801,7 +3803,7 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
(unsigned long long)map->m_lblk, map->m_len);
#endif
path = ext4_split_convert_extents(handle, inode, map, path,
- EXT4_GET_BLOCKS_CONVERT, NULL);
+ flags, NULL);
if (IS_ERR(path))
return path;
--
2.52.0
On Tue, Dec 23, 2025 at 09:17:56AM +0800, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> When performing buffered writes, we may need to split and convert an
> unwritten extent into a written one during the end I/O process. However,
> we do not reserve space specifically for these metadata changes, we only
> reserve 2% of space or 4096 blocks. To address this, we use
> EXT4_GET_BLOCKS_PRE_IO to potentially split extents in advance and
> EXT4_GET_BLOCKS_METADATA_NOFAIL to utilize reserved space if necessary.
>
> These two approaches can reduce the likelihood of running out of space
> and losing data. However, these methods are merely best efforts, we
> could still run out of space, and there is not much difference between
> converting an extent during the writeback process and the end I/O
> process, it won't increase the rick of losing data if we postpone the
^^^^ risk
Other than the minor typo above, feel free to add:
Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
> conversion.
>
> Therefore, also use EXT4_GET_BLOCKS_METADATA_NOFAIL in
> ext4_convert_unwritten_extents_endio() to prepare for the buffered I/O
> iomap conversion, which may perform extent conversion during the end I/O
> process.
>
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> Reviewed-by: Jan Kara <jack@suse.cz>
> ---
> fs/ext4/extents.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 27eb2c1df012..e53959120b04 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3794,6 +3794,8 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
> * illegal.
> */
> if (ee_block != map->m_lblk || ee_len > map->m_len) {
> + int flags = EXT4_GET_BLOCKS_CONVERT |
> + EXT4_GET_BLOCKS_METADATA_NOFAIL;
> #ifdef CONFIG_EXT4_DEBUG
> ext4_warning(inode->i_sb, "Inode (%ld) finished: extent logical block %llu,"
> " len %u; IO logical block %llu, len %u",
> @@ -3801,7 +3803,7 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
> (unsigned long long)map->m_lblk, map->m_len);
> #endif
> path = ext4_split_convert_extents(handle, inode, map, path,
> - EXT4_GET_BLOCKS_CONVERT, NULL);
> + flags, NULL);
> if (IS_ERR(path))
> return path;
>
> --
> 2.52.0
>
On 1/3/2026 9:42 PM, Ojaswin Mujoo wrote:
> On Tue, Dec 23, 2025 at 09:17:56AM +0800, Zhang Yi wrote:
>> From: Zhang Yi <yi.zhang@huawei.com>
>>
>> When performing buffered writes, we may need to split and convert an
>> unwritten extent into a written one during the end I/O process. However,
>> we do not reserve space specifically for these metadata changes, we only
>> reserve 2% of space or 4096 blocks. To address this, we use
>> EXT4_GET_BLOCKS_PRE_IO to potentially split extents in advance and
>> EXT4_GET_BLOCKS_METADATA_NOFAIL to utilize reserved space if necessary.
>>
>> These two approaches can reduce the likelihood of running out of space
>> and losing data. However, these methods are merely best efforts, we
>> could still run out of space, and there is not much difference between
>> converting an extent during the writeback process and the end I/O
>> process, it won't increase the rick of losing data if we postpone the
> ^^^^ risk
>
> Other than the minor typo above, feel free to add:
> Reviewed-by: Ojaswin Mujoo <ojaswin@linux.ibm.com>
Thank you for reviewing this series, I will revise it in v3.
Cheers,
Yi.
>
>> conversion.
>>
>> Therefore, also use EXT4_GET_BLOCKS_METADATA_NOFAIL in
>> ext4_convert_unwritten_extents_endio() to prepare for the buffered I/O
>> iomap conversion, which may perform extent conversion during the end I/O
>> process.
>>
>> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
>> Reviewed-by: Jan Kara <jack@suse.cz>
>
>> ---
>> fs/ext4/extents.c | 4 +++-
>> 1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
>> index 27eb2c1df012..e53959120b04 100644
>> --- a/fs/ext4/extents.c
>> +++ b/fs/ext4/extents.c
>> @@ -3794,6 +3794,8 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
>> * illegal.
>> */
>> if (ee_block != map->m_lblk || ee_len > map->m_len) {
>> + int flags = EXT4_GET_BLOCKS_CONVERT |
>> + EXT4_GET_BLOCKS_METADATA_NOFAIL;
>> #ifdef CONFIG_EXT4_DEBUG
>> ext4_warning(inode->i_sb, "Inode (%ld) finished: extent logical block %llu,"
>> " len %u; IO logical block %llu, len %u",
>> @@ -3801,7 +3803,7 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
>> (unsigned long long)map->m_lblk, map->m_len);
>> #endif
>> path = ext4_split_convert_extents(handle, inode, map, path,
>> - EXT4_GET_BLOCKS_CONVERT, NULL);
>> + flags, NULL);
>> if (IS_ERR(path))
>> return path;
>>
>> --
>> 2.52.0
>>
On 2025-12-23 09:17, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> When performing buffered writes, we may need to split and convert an
> unwritten extent into a written one during the end I/O process. However,
> we do not reserve space specifically for these metadata changes, we only
> reserve 2% of space or 4096 blocks. To address this, we use
> EXT4_GET_BLOCKS_PRE_IO to potentially split extents in advance and
> EXT4_GET_BLOCKS_METADATA_NOFAIL to utilize reserved space if necessary.
>
> These two approaches can reduce the likelihood of running out of space
> and losing data. However, these methods are merely best efforts, we
> could still run out of space, and there is not much difference between
> converting an extent during the writeback process and the end I/O
> process, it won't increase the rick of losing data if we postpone the
> conversion.
>
> Therefore, also use EXT4_GET_BLOCKS_METADATA_NOFAIL in
> ext4_convert_unwritten_extents_endio() to prepare for the buffered I/O
> iomap conversion, which may perform extent conversion during the end I/O
> process.
>
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> Reviewed-by: Jan Kara <jack@suse.cz>
Fair point, that is consistent with how
ext4_ext_handle_unwritten_extents() handles it.
Reviewed-by: Baokun Li <libaokun1@huawei.com>
> ---
> fs/ext4/extents.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 27eb2c1df012..e53959120b04 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3794,6 +3794,8 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
> * illegal.
> */
> if (ee_block != map->m_lblk || ee_len > map->m_len) {
> + int flags = EXT4_GET_BLOCKS_CONVERT |
> + EXT4_GET_BLOCKS_METADATA_NOFAIL;
> #ifdef CONFIG_EXT4_DEBUG
> ext4_warning(inode->i_sb, "Inode (%ld) finished: extent logical block %llu,"
> " len %u; IO logical block %llu, len %u",
> @@ -3801,7 +3803,7 @@ ext4_convert_unwritten_extents_endio(handle_t *handle, struct inode *inode,
> (unsigned long long)map->m_lblk, map->m_len);
> #endif
> path = ext4_split_convert_extents(handle, inode, map, path,
> - EXT4_GET_BLOCKS_CONVERT, NULL);
> + flags, NULL);
> if (IS_ERR(path))
> return path;
>
© 2016 - 2026 Red Hat, Inc.