From fef51096beac86c5705de95f7abcefde7be2e30e Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Sun, 1 Sep 2024 12:31:56 +1000 Subject: [PATCH 1/2] zfs_file: rename zfs_file_fallocate to zfs_file_deallocate We only use it on a specific way: to punch a hole in (make sparse) a region of a file, in order to implement TRIM-like behaviour. So, call the op "deallocate", and move the Linux-style mode flags down into the Linux implementation, since they're an implementation detail. FreeBSD gets a no-op stub (for the moment). Sponsored-by: https://despairlabs.com/sponsor/ Signed-off-by: Rob Norris --- include/sys/zfs_file.h | 2 +- lib/libzpool/kernel.c | 24 +++++++++++++----------- module/os/freebsd/zfs/vdev_file.c | 11 ++--------- module/os/freebsd/zfs/zfs_file_os.c | 14 ++++++++++++++ module/os/linux/zfs/vdev_file.c | 9 ++------- module/os/linux/zfs/zfs_file_os.c | 19 ++++++++++--------- 6 files changed, 42 insertions(+), 37 deletions(-) diff --git a/include/sys/zfs_file.h b/include/sys/zfs_file.h index e944165adc40..64f116c21ba7 100644 --- a/include/sys/zfs_file.h +++ b/include/sys/zfs_file.h @@ -53,7 +53,7 @@ int zfs_file_pread(zfs_file_t *fp, void *buf, size_t len, loff_t off, int zfs_file_seek(zfs_file_t *fp, loff_t *offp, int whence); int zfs_file_getattr(zfs_file_t *fp, zfs_file_attr_t *zfattr); int zfs_file_fsync(zfs_file_t *fp, int flags); -int zfs_file_fallocate(zfs_file_t *fp, int mode, loff_t offset, loff_t len); +int zfs_file_deallocate(zfs_file_t *fp, loff_t offset, loff_t len); loff_t zfs_file_off(zfs_file_t *fp); int zfs_file_unlink(const char *); diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c index a3930ee07f73..68d22a431b96 100644 --- a/lib/libzpool/kernel.c +++ b/lib/libzpool/kernel.c @@ -1367,24 +1367,26 @@ zfs_file_fsync(zfs_file_t *fp, int flags) } /* - * fallocate - allocate or free space on disk + * deallocate - zero and/or deallocate file storage * * fp - file pointer - * mode (non-standard options for hole punching etc) - * offset - offset to start allocating or freeing from - * len - length to free / allocate - * - * OPTIONAL + * offset - offset to start zeroing or deallocating + * len - length to zero or deallocate */ int -zfs_file_fallocate(zfs_file_t *fp, int mode, loff_t offset, loff_t len) +zfs_file_deallocate(zfs_file_t *fp, loff_t offset, loff_t len) { -#ifdef __linux__ - return (fallocate(fp->f_fd, mode, offset, len)); + int rc; +#if defined(__linux__) + rc = fallocate(fp->f_fd, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, len); #else - (void) fp, (void) mode, (void) offset, (void) len; - return (EOPNOTSUPP); + (void) fp, (void) offset, (void) len; + rc = EOPNOTSUPP; #endif + if (rc) + return (SET_ERROR(rc)); + return (0); } /* diff --git a/module/os/freebsd/zfs/vdev_file.c b/module/os/freebsd/zfs/vdev_file.c index 869093afa3ed..6719c87f82e5 100644 --- a/module/os/freebsd/zfs/vdev_file.c +++ b/module/os/freebsd/zfs/vdev_file.c @@ -260,16 +260,9 @@ vdev_file_io_start(zio_t *zio) zio_execute(zio); return; } else if (zio->io_type == ZIO_TYPE_TRIM) { -#ifdef notyet - int mode = 0; - ASSERT3U(zio->io_size, !=, 0); - - /* XXX FreeBSD has no fallocate routine in file ops */ - zio->io_error = zfs_file_fallocate(vf->vf_file, - mode, zio->io_offset, zio->io_size); -#endif - zio->io_error = SET_ERROR(ENOTSUP); + zio->io_error = zfs_file_deallocate(vf->vf_file, + zio->io_offset, zio->io_size); zio_execute(zio); return; } diff --git a/module/os/freebsd/zfs/zfs_file_os.c b/module/os/freebsd/zfs/zfs_file_os.c index bdb8c4fac774..0a91ed47e144 100644 --- a/module/os/freebsd/zfs/zfs_file_os.c +++ b/module/os/freebsd/zfs/zfs_file_os.c @@ -285,6 +285,20 @@ zfs_file_fsync(zfs_file_t *fp, int flags) return (zfs_vop_fsync(fp->f_vnode)); } +/* + * deallocate - zero and/or deallocate file storage + * + * fp - file pointer + * offset - offset to start zeroing or deallocating + * len - length to zero or deallocate + */ +int +zfs_file_deallocate(zfs_file_t *fp, loff_t offset, loff_t len) +{ + (void) fp, (void) offset, (void) len; + return (SET_ERROR(EOPNOTSUPP)); +} + zfs_file_t * zfs_file_get(int fd) { diff --git a/module/os/linux/zfs/vdev_file.c b/module/os/linux/zfs/vdev_file.c index ac41a2615f16..4bffb6412ffd 100644 --- a/module/os/linux/zfs/vdev_file.c +++ b/module/os/linux/zfs/vdev_file.c @@ -274,14 +274,9 @@ vdev_file_io_start(zio_t *zio) zio_execute(zio); return; } else if (zio->io_type == ZIO_TYPE_TRIM) { - int mode = 0; - ASSERT3U(zio->io_size, !=, 0); -#ifdef __linux__ - mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; -#endif - zio->io_error = zfs_file_fallocate(vf->vf_file, - mode, zio->io_offset, zio->io_size); + zio->io_error = zfs_file_deallocate(vf->vf_file, + zio->io_offset, zio->io_size); zio_execute(zio); return; } diff --git a/module/os/linux/zfs/zfs_file_os.c b/module/os/linux/zfs/zfs_file_os.c index bc753614be27..d67f69527900 100644 --- a/module/os/linux/zfs/zfs_file_os.c +++ b/module/os/linux/zfs/zfs_file_os.c @@ -328,17 +328,14 @@ zfs_file_fsync(zfs_file_t *filp, int flags) } /* - * fallocate - allocate or free space on disk + * deallocate - zero and/or deallocate file storage * * fp - file pointer - * mode (non-standard options for hole punching etc) - * offset - offset to start allocating or freeing from - * len - length to free / allocate - * - * OPTIONAL + * offset - offset to start zeroing or deallocating + * len - length to zero or deallocate */ int -zfs_file_fallocate(zfs_file_t *fp, int mode, loff_t offset, loff_t len) +zfs_file_deallocate(zfs_file_t *fp, loff_t offset, loff_t len) { /* * May enter XFS which generates a warning when PF_FSTRANS is set. @@ -354,12 +351,16 @@ zfs_file_fallocate(zfs_file_t *fp, int mode, loff_t offset, loff_t len) */ int error = EOPNOTSUPP; if (fp->f_op->fallocate) - error = fp->f_op->fallocate(fp, mode, offset, len); + error = -fp->f_op->fallocate(fp, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, len); if (fstrans) current->flags |= __SPL_PF_FSTRANS; - return (error); + if (error) + return (SET_ERROR(error)); + + return (0); } /* From 6e05c1b606677cdfd29a20c7a39efac32df2642e Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Sun, 1 Sep 2024 12:34:17 +1000 Subject: [PATCH 2/2] zfs_file: implement zfs_file_deallocate for FreeBSD 14 FreeBSD 14 gained a `VOP_DEALLOCATE` VFS operation and a `fspacectl` syscall to use it. At minimum, these zero the given region, and if the underlying filesystem supports it, can make the region sparse. We can use this to get TRIM-like behaviour for file vdevs. Sponsored-by: https://despairlabs.com/sponsor/ Signed-off-by: Rob Norris --- lib/libzpool/kernel.c | 6 ++++++ module/os/freebsd/zfs/zfs_file_os.c | 14 +++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c index 68d22a431b96..4f4cf490f8e9 100644 --- a/lib/libzpool/kernel.c +++ b/lib/libzpool/kernel.c @@ -1380,6 +1380,12 @@ zfs_file_deallocate(zfs_file_t *fp, loff_t offset, loff_t len) #if defined(__linux__) rc = fallocate(fp->f_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, len); +#elif defined(__FreeBSD__) && (__FreeBSD_version >= 1400029) + struct spacectl_range rqsr = { + .r_offset = offset, + .r_len = len, + }; + rc = fspacectl(fp->f_fd, SPACECTL_DEALLOC, &rqsr, 0, &rqsr); #else (void) fp, (void) offset, (void) len; rc = EOPNOTSUPP; diff --git a/module/os/freebsd/zfs/zfs_file_os.c b/module/os/freebsd/zfs/zfs_file_os.c index 0a91ed47e144..f6ee391deddd 100644 --- a/module/os/freebsd/zfs/zfs_file_os.c +++ b/module/os/freebsd/zfs/zfs_file_os.c @@ -295,8 +295,20 @@ zfs_file_fsync(zfs_file_t *fp, int flags) int zfs_file_deallocate(zfs_file_t *fp, loff_t offset, loff_t len) { + int rc; +#if __FreeBSD_version >= 1400029 + struct thread *td; + + td = curthread; + rc = fo_fspacectl(fp, SPACECTL_DEALLOC, &offset, &len, 0, + td->td_ucred, td); +#else (void) fp, (void) offset, (void) len; - return (SET_ERROR(EOPNOTSUPP)); + rc = EOPNOTSUPP; +#endif + if (rc) + return (SET_ERROR(rc)); + return (0); } zfs_file_t *