Re: [PATCH 1/2] ext4: export converted block count from ext4_convert_unwritten_extents()

From: Jan Kara

Date: Wed Jul 29 2026 - 05:41:58 EST


On Wed 29-07-26 16:59:17, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@xxxxxxxxxx>
>
> ext4_convert_unwritten_extents() currently returns only a success or a
> failure indication. A zero return means all requested blocks were
> converted, and a negative value means the conversion failed. However,
> some blocks may have already been converted when the function fails
> partway through, and callers have no way to learn how many were done.
>
> The WRITE_ZEROES caller in ext4_alloc_file_blocks() needs this
> information to decide whether to add the inode to the orphan list before
> updating i_disksize to cover the already-converted written extents, so
> that a crash before i_disksize catches up can be recovered via orphan
> truncation.
>
> Switch the function to pass out the number of converted blocks through
> the new output parameter @converted, which will be used by later
> patches.
>
> Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@xxxxxxx>

Honza

> ---
> fs/ext4/ext4.h | 3 ++-
> fs/ext4/extents.c | 53 ++++++++++++++++++++++++++++++-----------------
> fs/ext4/file.c | 3 ++-
> 3 files changed, 38 insertions(+), 21 deletions(-)
>
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 4e3b3165ee8f..f36d84ff0d5e 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -3901,7 +3901,8 @@ extern void ext4_ext_release(struct super_block *);
> extern long ext4_fallocate(struct file *file, int mode, loff_t offset,
> loff_t len);
> extern int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode,
> - loff_t offset, ssize_t len);
> + loff_t offset, ssize_t len,
> + ext4_lblk_t *converted);
> extern int ext4_convert_unwritten_extents_atomic(handle_t *handle,
> struct inode *inode, loff_t offset, ssize_t len);
> extern int ext4_convert_unwritten_io_end_vec(handle_t *handle,
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index d5f87a7f6c05..1ab1a6e2ed83 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -4664,7 +4664,7 @@ static int ext4_alloc_file_blocks(struct file *file, loff_t offset, loff_t len,
> if (likely(!ret))
> ret = ext4_convert_unwritten_extents(NULL,
> inode, (loff_t)map.m_lblk << blkbits,
> - (loff_t)map.m_len << blkbits);
> + (loff_t)map.m_len << blkbits, NULL);
> if (ret)
> break;
> }
> @@ -5051,21 +5051,26 @@ int ext4_convert_unwritten_extents_atomic(handle_t *handle, struct inode *inode,
> * all unwritten extents within this range will be converted to
> * written extents.
> *
> - * This function is called from the direct IO end io call back
> - * function, to convert the fallocated extents after IO is completed.
> - * Returns 0 on success.
> + * This function is called from the direct/buffered I/O end io call back
> + * function and FALLOC_FL_WRITE_ZEROES, to convert the fallocated
> + * unwritten extents after data I/O is completed.
> + *
> + * Returns 0 on full success, or a negative error code on partial
> + * success or failure. The number of blocks converted is returned via
> + * @converted.
> */
> int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode,
> - loff_t offset, ssize_t len)
> + loff_t offset, ssize_t len,
> + ext4_lblk_t *converted)
> {
> - unsigned int max_blocks;
> + ext4_lblk_t max_blocks, conv_blocks = 0;
> int ret = 0, ret2 = 0, ret3 = 0;
> struct ext4_map_blocks map;
> unsigned int blkbits = inode->i_blkbits;
> unsigned int credits = 0;
>
> map.m_lblk = offset >> blkbits;
> - max_blocks = EXT4_MAX_BLOCKS(len, offset, blkbits);
> + map.m_len = max_blocks = EXT4_MAX_BLOCKS(len, offset, blkbits);
>
> if (!handle) {
> /*
> @@ -5073,9 +5078,8 @@ int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode,
> */
> credits = ext4_chunk_trans_blocks(inode, max_blocks);
> }
> - while (ret >= 0 && ret < max_blocks) {
> - map.m_lblk += ret;
> - map.m_len = (max_blocks -= ret);
> +
> + while (max_blocks) {
> if (credits) {
> handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS,
> credits);
> @@ -5092,23 +5096,34 @@ int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode,
> ret = ext4_map_blocks(handle, inode, &map,
> EXT4_GET_BLOCKS_IO_CONVERT_EXT |
> EXT4_EX_NOCACHE);
> - if (ret <= 0)
> + if (ret <= 0) {
> ext4_warning(inode->i_sb,
> - "inode #%llu: block %u: len %u: "
> - "ext4_ext_map_blocks returned %d",
> - inode->i_ino, map.m_lblk,
> - map.m_len, ret);
> + "inode #%llu: block %u: len %u: ext4_map_blocks returned %d",
> + inode->i_ino, map.m_lblk, map.m_len, ret);
> + if (unlikely(ret == 0))
> + ret = -EINVAL;
> + } else {
> + conv_blocks += map.m_len;
> + }
> +
> ret2 = ext4_mark_inode_dirty(handle, inode);
> if (credits) {
> ret3 = ext4_journal_stop(handle);
> if (unlikely(ret3))
> ret2 = ret3;
> }
> -
> - if (ret <= 0 || ret2)
> + ret = ret < 0 ? ret : ret2;
> + if (ret)
> break;
> +
> + map.m_lblk += map.m_len;
> + map.m_len = (max_blocks -= map.m_len);
> }
> - return ret > 0 ? ret2 : ret;
> + /* Converted some or all blocks successfully? */
> + if (converted)
> + *converted = conv_blocks;
> +
> + return ret;
> }
>
> int ext4_convert_unwritten_io_end_vec(handle_t *handle, ext4_io_end_t *io_end)
> @@ -5131,7 +5146,7 @@ int ext4_convert_unwritten_io_end_vec(handle_t *handle, ext4_io_end_t *io_end)
> list_for_each_entry(io_end_vec, &io_end->list_vec, list) {
> ret = ext4_convert_unwritten_extents(handle, io_end->inode,
> io_end_vec->offset,
> - io_end_vec->size);
> + io_end_vec->size, NULL);
> if (ret)
> break;
> }
> diff --git a/fs/ext4/file.c b/fs/ext4/file.c
> index 9a16071b719d..45e16799d0c2 100644
> --- a/fs/ext4/file.c
> +++ b/fs/ext4/file.c
> @@ -436,7 +436,8 @@ static int ext4_dio_write_end_io(struct kiocb *iocb, ssize_t size,
> error = ext4_convert_unwritten_extents_atomic(NULL, inode, pos,
> size);
> else if (!error && size && flags & IOMAP_DIO_UNWRITTEN)
> - error = ext4_convert_unwritten_extents(NULL, inode, pos, size);
> + error = ext4_convert_unwritten_extents(NULL, inode, pos, size,
> + NULL);
> if (error)
> return error;
> /*
> --
> 2.52.0
>
--
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR