Re: [PATCH v2] btrfs: skip hole detection during full fsync for files without holes

Qu Wenruo <[email protected]> Fri, 31 Jul 2026 18:57:06 +0930
Newsgroups org.kernel.vger.linux-btrfs
Message-ID <[email protected]>

在 2026/7/31 18:49, [email protected] 写道:
> From: Filipe Manana <[email protected]>
> 
> If we the no-holes feature is enabled (a default since btrfs-progs 5.15),
> when doing a full fsync we always iterate of all leaves in the subvolume
> root that contain file extent items in order to detect holes between them.
> 
> This can take a lot of time for files with a large number of extents.
> But if we know there are no prealloc extents and the amount of space
> (uncompressed space) is greater than or equals to the i_size of the
> inode, then we cannot have holes and therefore avoid searching for
> them. So skip the search if those conditions are met.
> 
> The following test script was used:
> 
>    $ cat test.sh
>    #!/bin/bash
> 
>    MNT=/mnt/nullb0
>    DEV=/dev/nullb0
> 
>    umount $MNT &> /dev/null
>    mkfs.btrfs -f $DEV
>    mount $DEV $MNT
> 
>    # 256M gives 64K extents of 4K each.
>    FILE_SIZE=$((256 * 1024 * 1024))
>    touch $MNT/foobar
> 
>    for ((i = 0; i < $FILE_SIZE; i += 8192)); do
>        xfs_io -c "pwrite -S 0xab $i 4K" $MNT/foobar > /dev/null
>    done
> 
>    xfs_io -c "fsync" $MNT/foobar
> 
>    for ((i = 4096; i < $FILE_SIZE; i += 8192)); do
>       xfs_io -c "pwrite -S 0xab $i 4K" $MNT/foobar > /dev/null
>    done
> 
>    # unmount and mount, clear caches and ensure the next fsync is a
>    # full sync.
>    umount $MNT
>    mount $DEV $MNT
> 
>    # Do some change to the file in order to fsync.
>    xfs_io -c "pwrite -S 0xcd 0 4K" $MNT/foobar > /dev/null
> 
>    T0=$(date +%s%N)
>    xfs_io -c "fsync" $MNT/foobar
>    T1=$(date +%s%N)
> 
>    echo
>    echo "Took $(( (T1 - T0) / 1000 ))us"
> 
>    umount $MNT
> 
> Before this change:
> 
>    Took 28721us
> 
> After this change:
> 
>    Took 5453us
> 
> That's about 5.3x times faster.
> 
> Signed-off-by: Filipe Manana <[email protected]>

Reviewed-by: Qu Wenruo <[email protected]>

Thanks,
Qu

> ---
> 
> V2: Support the case where i_size is not sector size aligned and so
>      i_size <= nbytes.
> 
>   fs/btrfs/tree-log.c | 9 +++++++++
>   1 file changed, 9 insertions(+)
> 
> diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
> index 47046dd14997..88b6258f2e3f 100644
> --- a/fs/btrfs/tree-log.c
> +++ b/fs/btrfs/tree-log.c
> @@ -5591,6 +5591,15 @@ static int btrfs_log_holes(struct btrfs_trans_handle *trans,
>   	if (!btrfs_fs_incompat(fs_info, NO_HOLES) || i_size == 0)
>   		return 0;
>   
> +	/*
> +	 * If there are no prealloc extents (which can be located past i_size),
> +	 * and disk space used is greater than or equals to i_size, then there
> +	 * are no holes.
> +	 */
> +	if (!(inode->flags & BTRFS_INODE_PREALLOC) &&
> +	    i_size <= inode_get_bytes(&inode->vfs_inode))
> +		return 0;
> +
>   	key.objectid = ino;
>   	key.type = BTRFS_EXTENT_DATA_KEY;
>   	key.offset = 0;