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

Ojaswin Mujoo <[email protected]> Fri, 31 Jul 2026 17:31:11 +0530
Newsgroups org.kernel.vger.linux-ext4,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On Wed, Jul 29, 2026 at 04:59:17PM +0800, Zhang Yi wrote:
> From: Zhang Yi <[email protected]>
> 
> 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 <[email protected]>

Looks good Zhang, feel free to add:

Reviewed-by: Ojaswin Mujoo <[email protected]>

Regards,
ojaswin

> ---
>  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
>