From f3bbeb970e03cfc8e7b2d90702e3f471668b3ef8 Mon Sep 17 00:00:00 2001 From: Ka Ho Ng Date: Wed, 4 Aug 2021 23:57:48 +0800 Subject: [PATCH] FreeBSD: Implement hole-punching support This adds supports for hole-punching facilities in the FreeBSD kernel starting from __FreeBSD_version 1400032. Reviewed-by: Brian Behlendorf Reviewed-by: Alexander Motin Reviewed-by: Ryan Moeller Signed-off-by: Ka Ho Ng Sponsored-by: The FreeBSD Foundation Closes #12458 --- module/os/freebsd/zfs/zfs_vnops_os.c | 57 ++++++++++++++++++++++++++++ module/os/freebsd/zfs/zfs_znode.c | 10 +++-- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c index 846b4b6053..3083b53818 100644 --- a/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/module/os/freebsd/zfs/zfs_vnops_os.c @@ -5222,6 +5222,11 @@ zfs_freebsd_pathconf(struct vop_pathconf_args *ap) case _PC_NAME_MAX: *ap->a_retval = NAME_MAX; return (0); +#if __FreeBSD_version >= 1400032 + case _PC_DEALLOC_PRESENT: + *ap->a_retval = 1; + return (0); +#endif case _PC_PIPE_BUF: if (ap->a_vp->v_type == VDIR || ap->a_vp->v_type == VFIFO) { *ap->a_retval = PIPE_BUF; @@ -6057,6 +6062,55 @@ zfs_vptocnp(struct vop_vptocnp_args *ap) return (error); } +#if __FreeBSD_version >= 1400032 +static int +zfs_deallocate(struct vop_deallocate_args *ap) +{ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = zp->z_zfsvfs; + zilog_t *zilog; + off_t off, len, file_sz; + int error; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + /* + * Callers might not be able to detect properly that we are read-only, + * so check it explicitly here. + */ + if (zfs_is_readonly(zfsvfs)) { + ZFS_EXIT(zfsvfs); + return (SET_ERROR(EROFS)); + } + + zilog = zfsvfs->z_log; + off = *ap->a_offset; + len = *ap->a_len; + file_sz = zp->z_size; + if (off + len > file_sz) + len = file_sz - off; + /* Fast path for out-of-range request. */ + if (len <= 0) { + *ap->a_len = 0; + ZFS_EXIT(zfsvfs); + return (0); + } + + error = zfs_freesp(zp, off, len, O_RDWR, TRUE); + if (error == 0) { + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS || + (ap->a_ioflag & IO_SYNC) != 0) + zil_commit(zilog, zp->z_id); + *ap->a_offset = off + len; + *ap->a_len = 0; + } + + ZFS_EXIT(zfsvfs); + return (error); +} +#endif + struct vop_vector zfs_vnodeops; struct vop_vector zfs_fifoops; struct vop_vector zfs_shareops; @@ -6076,6 +6130,9 @@ struct vop_vector zfs_vnodeops = { #endif .vop_access = zfs_freebsd_access, .vop_allocate = VOP_EINVAL, +#if __FreeBSD_version >= 1400032 + .vop_deallocate = zfs_deallocate, +#endif .vop_lookup = zfs_cache_lookup, .vop_cachedlookup = zfs_freebsd_cachedlookup, .vop_getattr = zfs_freebsd_getattr, diff --git a/module/os/freebsd/zfs/zfs_znode.c b/module/os/freebsd/zfs/zfs_znode.c index 6a59c0eae7..9b48dcda1b 100644 --- a/module/os/freebsd/zfs/zfs_znode.c +++ b/module/os/freebsd/zfs/zfs_znode.c @@ -1476,12 +1476,16 @@ zfs_free_range(znode_t *zp, uint64_t off, uint64_t len) error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, off, len); if (error == 0) { +#if __FreeBSD_version >= 1400032 + vnode_pager_purge_range(ZTOV(zp), off, off + len); +#else /* - * In FreeBSD we cannot free block in the middle of a file, - * but only at the end of a file, so this code path should - * never happen. + * Before __FreeBSD_version 1400032 we cannot free block in the + * middle of a file, but only at the end of a file, so this code + * path should never happen. */ vnode_pager_setsize(ZTOV(zp), off); +#endif } zfs_rangelock_exit(lr);