diff options
author | Rob Norris <[email protected]> | 2023-06-27 23:44:53 +1000 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2023-07-26 08:46:58 -0700 |
commit | 5d12545da8c112aa813560950f39315956338963 (patch) | |
tree | a0c33d5f555bce52a49afbcab4fb1e93729e9079 /module | |
parent | a3ea8c8ee6d01aba0d15b4619b8641376fef57e2 (diff) |
linux: implement filesystem-side copy/clone functions
This implements the Linux VFS ops required to service the file
copy/clone APIs:
.copy_file_range (4.5+)
.clone_file_range (4.5-4.19)
.dedupe_file_range (4.5-4.19)
.remap_file_range (4.20+)
Note that dedupe_file_range() and remap_file_range(REMAP_FILE_DEDUP) are
hooked up here, but are not implemented yet.
Reviewed-by: Brian Behlendorf <[email protected]>
Reviewed-by: Kay Pedersen <[email protected]>
Signed-off-by: Rob Norris <[email protected]>
Sponsored-By: OpenDrives Inc.
Sponsored-By: Klara Inc.
Closes #15050
Diffstat (limited to 'module')
-rw-r--r-- | module/Kbuild.in | 1 | ||||
-rw-r--r-- | module/os/linux/zfs/zpl_file.c | 13 | ||||
-rw-r--r-- | module/os/linux/zfs/zpl_file_range.c | 183 |
3 files changed, 196 insertions, 1 deletions
diff --git a/module/Kbuild.in b/module/Kbuild.in index 485331ac6..c13217159 100644 --- a/module/Kbuild.in +++ b/module/Kbuild.in @@ -461,6 +461,7 @@ ZFS_OBJS_OS := \ zpl_ctldir.o \ zpl_export.o \ zpl_file.o \ + zpl_file_range.o \ zpl_inode.o \ zpl_super.o \ zpl_xattr.o \ diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c index e690525d3..92b603e98 100644 --- a/module/os/linux/zfs/zpl_file.c +++ b/module/os/linux/zfs/zpl_file.c @@ -1283,7 +1283,6 @@ zpl_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } #endif /* CONFIG_COMPAT */ - const struct address_space_operations zpl_address_space_operations = { #ifdef HAVE_VFS_READPAGES .readpages = zpl_readpages, @@ -1333,6 +1332,18 @@ const struct file_operations zpl_file_operations = { .aio_fsync = zpl_aio_fsync, #endif .fallocate = zpl_fallocate, +#ifdef HAVE_VFS_COPY_FILE_RANGE + .copy_file_range = zpl_copy_file_range, +#endif +#ifdef HAVE_VFS_REMAP_FILE_RANGE + .remap_file_range = zpl_remap_file_range, +#endif +#ifdef HAVE_VFS_CLONE_FILE_RANGE + .clone_file_range = zpl_clone_file_range, +#endif +#ifdef HAVE_VFS_DEDUPE_FILE_RANGE + .dedupe_file_range = zpl_dedupe_file_range, +#endif #ifdef HAVE_FILE_FADVISE .fadvise = zpl_fadvise, #endif diff --git a/module/os/linux/zfs/zpl_file_range.c b/module/os/linux/zfs/zpl_file_range.c new file mode 100644 index 000000000..db387a748 --- /dev/null +++ b/module/os/linux/zfs/zpl_file_range.c @@ -0,0 +1,183 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or https://opensource.org/licenses/CDDL-1.0. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2023, Klara Inc. + */ + +#ifdef CONFIG_COMPAT +#include <linux/compat.h> +#endif +#include <linux/fs.h> +#include <sys/file.h> +#include <sys/zfs_znode.h> +#include <sys/zfs_vnops.h> +#include <sys/zfeature.h> + +/* + * Clone part of a file via block cloning. + * + * Note that we are not required to update file offsets; the kernel will take + * care of that depending on how it was called. + */ +static ssize_t +__zpl_clone_file_range(struct file *src_file, loff_t src_off, + struct file *dst_file, loff_t dst_off, size_t len) +{ + struct inode *src_i = file_inode(src_file); + struct inode *dst_i = file_inode(dst_file); + uint64_t src_off_o = (uint64_t)src_off; + uint64_t dst_off_o = (uint64_t)dst_off; + uint64_t len_o = (uint64_t)len; + cred_t *cr = CRED(); + fstrans_cookie_t cookie; + int err; + + if (!spa_feature_is_enabled( + dmu_objset_spa(ITOZSB(dst_i)->z_os), SPA_FEATURE_BLOCK_CLONING)) + return (-EOPNOTSUPP); + + if (src_i != dst_i) + spl_inode_lock_shared(src_i); + spl_inode_lock(dst_i); + + crhold(cr); + cookie = spl_fstrans_mark(); + + err = -zfs_clone_range(ITOZ(src_i), &src_off_o, ITOZ(dst_i), + &dst_off_o, &len_o, cr); + + spl_fstrans_unmark(cookie); + crfree(cr); + + spl_inode_unlock(dst_i); + if (src_i != dst_i) + spl_inode_unlock_shared(src_i); + + if (err < 0) + return (err); + + return ((ssize_t)len_o); +} + +#ifdef HAVE_VFS_COPY_FILE_RANGE +/* + * Entry point for copy_file_range(). Copy len bytes from src_off in src_file + * to dst_off in dst_file. We are permitted to do this however we like, so we + * try to just clone the blocks, and if we can't support it, fall back to the + * kernel's generic byte copy function. + */ +ssize_t +zpl_copy_file_range(struct file *src_file, loff_t src_off, + struct file *dst_file, loff_t dst_off, size_t len, unsigned int flags) +{ + ssize_t ret; + + if (flags != 0) + return (-EINVAL); + + /* Try to do it via zfs_clone_range() */ + ret =__zpl_clone_file_range(src_file, src_off, + dst_file, dst_off, len); + +#ifdef HAVE_VFS_GENERIC_COPY_FILE_RANGE + /* + * Since Linux 5.3 the filesystem driver is responsible for executing + * an appropriate fallback, and a generic fallback function is provided. + */ + if (ret == -EOPNOTSUPP || ret == -EXDEV) + ret = generic_copy_file_range(src_file, src_off, dst_file, + dst_off, len, flags); +#endif /* HAVE_VFS_GENERIC_COPY_FILE_RANGE */ + + return (ret); +} +#endif /* HAVE_VFS_COPY_FILE_RANGE */ + +#ifdef HAVE_VFS_REMAP_FILE_RANGE +/* + * Entry point for FICLONE/FICLONERANGE/FIDEDUPERANGE. + * + * FICLONE and FICLONERANGE are basically the same as copy_file_range(), except + * that they must clone - they cannot fall back to copying. FICLONE is exactly + * FICLONERANGE, for the entire file. We don't need to try to tell them apart; + * the kernel will sort that out for us. + * + * FIDEDUPERANGE is for turning a non-clone into a clone, that is, compare the + * range in both files and if they're the same, arrange for them to be backed + * by the same storage. + */ +loff_t +zpl_remap_file_range(struct file *src_file, loff_t src_off, + struct file *dst_file, loff_t dst_off, loff_t len, unsigned int flags) +{ + if (flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_CAN_SHORTEN)) + return (-EINVAL); + + /* + * REMAP_FILE_CAN_SHORTEN lets us know we can clone less than the given + * range if we want. Its designed for filesystems that make data past + * EOF available, and don't want it to be visible in both files. ZFS + * doesn't do that, so we just turn the flag off. + */ + flags &= ~REMAP_FILE_CAN_SHORTEN; + + if (flags & REMAP_FILE_DEDUP) + /* No support for dedup yet */ + return (-EOPNOTSUPP); + + /* Zero length means to clone everything to the end of the file */ + if (len == 0) + len = i_size_read(file_inode(src_file)) - src_off; + + return (__zpl_clone_file_range(src_file, src_off, + dst_file, dst_off, len)); +} +#endif /* HAVE_VFS_REMAP_FILE_RANGE */ + +#ifdef HAVE_VFS_CLONE_FILE_RANGE +/* + * Entry point for FICLONE and FICLONERANGE, before Linux 4.20. + */ +int +zpl_clone_file_range(struct file *src_file, loff_t src_off, + struct file *dst_file, loff_t dst_off, uint64_t len) +{ + /* Zero length means to clone everything to the end of the file */ + if (len == 0) + len = i_size_read(file_inode(src_file)) - src_off; + + return (__zpl_clone_file_range(src_file, src_off, + dst_file, dst_off, len)); +} +#endif /* HAVE_VFS_CLONE_FILE_RANGE */ + +#ifdef HAVE_VFS_DEDUPE_FILE_RANGE +/* + * Entry point for FIDEDUPERANGE, before Linux 4.20. + */ +int +zpl_dedupe_file_range(struct file *src_file, loff_t src_off, + struct file *dst_file, loff_t dst_off, uint64_t len) +{ + /* No support for dedup yet */ + return (-EOPNOTSUPP); +} +#endif /* HAVE_VFS_DEDUPE_FILE_RANGE */ |