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

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

=E5=9C=A8 2026/7/31 02:11, [email protected] =E5=86=99=E9=81=93:
> From: Filipe Manana <[email protected]>
>=20
> 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 the=
m.
>=20
> 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) matches 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.
>=20
> The following test script was used:
>=20
>    $ cat test.sh
>    #!/bin/bash
>=20
>    MNT=3D/mnt/nullb0
>    DEV=3D/dev/nullb0
>=20
>    MOUNT_OPTIONS=3D"-o ssd"
>    MKFS_OPTIONS=3D""
>=20
>    umount $MNT &> /dev/null
>    mkfs.btrfs -f $MKFS_OPTIONS $DEV
>    mount $MOUNT_OPTIONS $DEV $MNT
>=20
>    # 256M gives 64K extents of 4K each.
>    FILE_SIZE=3D$((256 * 1024 * 1024))
>    touch $MNT/foobar
>=20
>    for ((i =3D 0; i < $FILE_SIZE; i +=3D 8192)); do
>        xfs_io -c "pwrite -S 0xab $i 4K" $MNT/foobar > /dev/null
>    done
>=20
>    xfs_io -c "fsync" $MNT/foobar
>=20
>    for ((i =3D 4096; i < $FILE_SIZE; i +=3D 8192)); do
>       xfs_io -c "pwrite -S 0xab $i 4K" $MNT/foobar > /dev/null
>    done
>=20
>    # unmount and mount, clear caches and ensure the next fsync is a
>    # full sync.
>    umount $MNT
>    mount $MOUNT_OPTIONS $DEV $MNT
>=20
>    # Do some change to the file in order to fsync.
>    xfs_io -c "pwrite -S 0xcd 0 4K" $MNT/foobar > /dev/null
>=20
>    T0=3D$(date +%s%N)
>    xfs_io -c "fsync" $MNT/foobar
>    T1=3D$(date +%s%N)
>=20
>    echo
>    echo "Took $(( (T1 - T0) / 1000 ))us"
>=20
>    umount $MNT
>=20
> Before this change:
>=20
>    Took 28721us
>=20
> After this change:
>=20
>    Took 5453us
>=20
> That's about 5.3x times faster.
>=20
> Signed-off-by: Filipe Manana <[email protected]>
> ---
>   fs/btrfs/tree-log.c | 8 ++++++++
>   1 file changed, 8 insertions(+)
>=20
> diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
> index 47046dd14997..226dd3053564 100644
> --- a/fs/btrfs/tree-log.c
> +++ b/fs/btrfs/tree-log.c
> @@ -5591,6 +5591,14 @@ static int btrfs_log_holes(struct btrfs_trans_han=
dle *trans,
>   	if (!btrfs_fs_incompat(fs_info, NO_HOLES) || i_size =3D=3D 0)
>   		return 0;
>  =20
> +	/*
> +	 * If there are no prealloc extents (which can be located past i_size)=
,
> +	 * and disk space used matches the i_size, then there are no holes.
> +	 */

I'm wondering if i_size < nbytes, can we still skip the hole scan?

Thanks,
Qu

> +	if (!(inode->flags & BTRFS_INODE_PREALLOC) &&
> +	    i_size =3D=3D inode_get_bytes(&inode->vfs_inode))
> +		return 0;
> +
>   	key.objectid =3D ino;
>   	key.type =3D BTRFS_EXTENT_DATA_KEY;
>   	key.offset =3D 0;