ext4: export converted block count from ext4_convert_unwritten_extents()

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@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20260729085918.3336221-2-yi.zhang@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
Zhang Yi 2026-07-29 16:59:17 +08:00 committed by Theodore Ts'o
parent c87abbab61
commit 5b3dcb924a
3 changed files with 38 additions and 21 deletions

View File

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

View File

@ -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;
}

View File

@ -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;
/*