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 >