Merge upstream openzfs 'zfs-2.2-release' into truenas/zfs-2.2-release
This commit is contained in:
commit
9920b576fe
2
META
2
META
|
@ -6,5 +6,5 @@ Release: rc2
|
|||
Release-Tags: relext
|
||||
License: CDDL
|
||||
Author: OpenZFS
|
||||
Linux-Maximum: 6.3
|
||||
Linux-Maximum: 6.4
|
||||
Linux-Minimum: 3.10
|
||||
|
|
|
@ -609,8 +609,6 @@ zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data)
|
|||
*/
|
||||
if (nvlist_lookup_string(nvl, dp->dd_prop, &path) != 0 ||
|
||||
strcmp(dp->dd_compare, path) != 0) {
|
||||
zed_log_msg(LOG_INFO, " %s: no match (%s != vdev %s)",
|
||||
__func__, dp->dd_compare, path);
|
||||
return;
|
||||
}
|
||||
if (dp->dd_new_vdev_guid != 0 && dp->dd_new_vdev_guid != guid) {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
dnl #
|
||||
dnl # EL7 have backported copy_file_range and clone_file_range and
|
||||
dnl # added them to an "extended" file_operations struct.
|
||||
dnl #
|
||||
dnl # We're testing for both functions in one here, because they will only
|
||||
dnl # ever appear together and we don't want to match a similar method in
|
||||
dnl # some future vendor kernel.
|
||||
dnl #
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_FILE_OPERATIONS_EXTEND], [
|
||||
ZFS_LINUX_TEST_SRC([vfs_file_operations_extend], [
|
||||
#include <linux/fs.h>
|
||||
|
||||
static ssize_t test_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) {
|
||||
(void) src_file; (void) src_off;
|
||||
(void) dst_file; (void) dst_off;
|
||||
(void) len; (void) flags;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int test_clone_file_range(struct file *src_file,
|
||||
loff_t src_off, struct file *dst_file, loff_t dst_off,
|
||||
u64 len) {
|
||||
(void) src_file; (void) src_off;
|
||||
(void) dst_file; (void) dst_off;
|
||||
(void) len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static const struct file_operations_extend
|
||||
fops __attribute__ ((unused)) = {
|
||||
.kabi_fops = {},
|
||||
.copy_file_range = test_copy_file_range,
|
||||
.clone_file_range = test_clone_file_range,
|
||||
};
|
||||
],[])
|
||||
])
|
||||
AC_DEFUN([ZFS_AC_KERNEL_VFS_FILE_OPERATIONS_EXTEND], [
|
||||
AC_MSG_CHECKING([whether file_operations_extend takes \
|
||||
.copy_file_range() and .clone_file_range()])
|
||||
ZFS_LINUX_TEST_RESULT([vfs_file_operations_extend], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(HAVE_VFS_FILE_OPERATIONS_EXTEND, 1,
|
||||
[file_operations_extend takes .copy_file_range()
|
||||
and .clone_file_range()])
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
])
|
|
@ -0,0 +1,164 @@
|
|||
dnl #
|
||||
dnl # The *_file_range APIs have a long history:
|
||||
dnl #
|
||||
dnl # 2.6.29: BTRFS_IOC_CLONE and BTRFS_IOC_CLONE_RANGE ioctl introduced
|
||||
dnl # 3.12: BTRFS_IOC_FILE_EXTENT_SAME ioctl introduced
|
||||
dnl #
|
||||
dnl # 4.5: copy_file_range() syscall introduced, added to VFS
|
||||
dnl # 4.5: BTRFS_IOC_CLONE and BTRFS_IOC_CLONE_RANGE renamed to FICLONE ands
|
||||
dnl # FICLONERANGE, added to VFS as clone_file_range()
|
||||
dnl # 4.5: BTRFS_IOC_FILE_EXTENT_SAME renamed to FIDEDUPERANGE, added to VFS
|
||||
dnl # as dedupe_file_range()
|
||||
dnl #
|
||||
dnl # 4.20: VFS clone_file_range() and dedupe_file_range() replaced by
|
||||
dnl # remap_file_range()
|
||||
dnl #
|
||||
dnl # 5.3: VFS copy_file_range() expected to do its own fallback,
|
||||
dnl # generic_copy_file_range() added to support it
|
||||
dnl #
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_COPY_FILE_RANGE], [
|
||||
ZFS_LINUX_TEST_SRC([vfs_copy_file_range], [
|
||||
#include <linux/fs.h>
|
||||
|
||||
static ssize_t test_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) {
|
||||
(void) src_file; (void) src_off;
|
||||
(void) dst_file; (void) dst_off;
|
||||
(void) len; (void) flags;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static const struct file_operations
|
||||
fops __attribute__ ((unused)) = {
|
||||
.copy_file_range = test_copy_file_range,
|
||||
};
|
||||
],[])
|
||||
])
|
||||
AC_DEFUN([ZFS_AC_KERNEL_VFS_COPY_FILE_RANGE], [
|
||||
AC_MSG_CHECKING([whether fops->copy_file_range() is available])
|
||||
ZFS_LINUX_TEST_RESULT([vfs_copy_file_range], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(HAVE_VFS_COPY_FILE_RANGE, 1,
|
||||
[fops->copy_file_range() is available])
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_GENERIC_COPY_FILE_RANGE], [
|
||||
ZFS_LINUX_TEST_SRC([generic_copy_file_range], [
|
||||
#include <linux/fs.h>
|
||||
], [
|
||||
struct file *src_file __attribute__ ((unused)) = NULL;
|
||||
loff_t src_off __attribute__ ((unused)) = 0;
|
||||
struct file *dst_file __attribute__ ((unused)) = NULL;
|
||||
loff_t dst_off __attribute__ ((unused)) = 0;
|
||||
size_t len __attribute__ ((unused)) = 0;
|
||||
unsigned int flags __attribute__ ((unused)) = 0;
|
||||
generic_copy_file_range(src_file, src_off, dst_file, dst_off,
|
||||
len, flags);
|
||||
])
|
||||
])
|
||||
AC_DEFUN([ZFS_AC_KERNEL_VFS_GENERIC_COPY_FILE_RANGE], [
|
||||
AC_MSG_CHECKING([whether generic_copy_file_range() is available])
|
||||
ZFS_LINUX_TEST_RESULT_SYMBOL([generic_copy_file_range],
|
||||
[generic_copy_file_range], [fs/read_write.c], [
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_VFS_GENERIC_COPY_FILE_RANGE, 1,
|
||||
[generic_copy_file_range() is available])
|
||||
],[
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_CLONE_FILE_RANGE], [
|
||||
ZFS_LINUX_TEST_SRC([vfs_clone_file_range], [
|
||||
#include <linux/fs.h>
|
||||
|
||||
static int test_clone_file_range(struct file *src_file,
|
||||
loff_t src_off, struct file *dst_file, loff_t dst_off,
|
||||
u64 len) {
|
||||
(void) src_file; (void) src_off;
|
||||
(void) dst_file; (void) dst_off;
|
||||
(void) len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static const struct file_operations
|
||||
fops __attribute__ ((unused)) = {
|
||||
.clone_file_range = test_clone_file_range,
|
||||
};
|
||||
],[])
|
||||
])
|
||||
AC_DEFUN([ZFS_AC_KERNEL_VFS_CLONE_FILE_RANGE], [
|
||||
AC_MSG_CHECKING([whether fops->clone_file_range() is available])
|
||||
ZFS_LINUX_TEST_RESULT([vfs_clone_file_range], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(HAVE_VFS_CLONE_FILE_RANGE, 1,
|
||||
[fops->clone_file_range() is available])
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_DEDUPE_FILE_RANGE], [
|
||||
ZFS_LINUX_TEST_SRC([vfs_dedupe_file_range], [
|
||||
#include <linux/fs.h>
|
||||
|
||||
static int test_dedupe_file_range(struct file *src_file,
|
||||
loff_t src_off, struct file *dst_file, loff_t dst_off,
|
||||
u64 len) {
|
||||
(void) src_file; (void) src_off;
|
||||
(void) dst_file; (void) dst_off;
|
||||
(void) len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static const struct file_operations
|
||||
fops __attribute__ ((unused)) = {
|
||||
.dedupe_file_range = test_dedupe_file_range,
|
||||
};
|
||||
],[])
|
||||
])
|
||||
AC_DEFUN([ZFS_AC_KERNEL_VFS_DEDUPE_FILE_RANGE], [
|
||||
AC_MSG_CHECKING([whether fops->dedupe_file_range() is available])
|
||||
ZFS_LINUX_TEST_RESULT([vfs_dedupe_file_range], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(HAVE_VFS_DEDUPE_FILE_RANGE, 1,
|
||||
[fops->dedupe_file_range() is available])
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_VFS_REMAP_FILE_RANGE], [
|
||||
ZFS_LINUX_TEST_SRC([vfs_remap_file_range], [
|
||||
#include <linux/fs.h>
|
||||
|
||||
static loff_t test_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) {
|
||||
(void) src_file; (void) src_off;
|
||||
(void) dst_file; (void) dst_off;
|
||||
(void) len; (void) flags;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static const struct file_operations
|
||||
fops __attribute__ ((unused)) = {
|
||||
.remap_file_range = test_remap_file_range,
|
||||
};
|
||||
],[])
|
||||
])
|
||||
|
||||
AC_DEFUN([ZFS_AC_KERNEL_VFS_REMAP_FILE_RANGE], [
|
||||
AC_MSG_CHECKING([whether fops->remap_file_range() is available])
|
||||
ZFS_LINUX_TEST_RESULT([vfs_remap_file_range], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(HAVE_VFS_REMAP_FILE_RANGE, 1,
|
||||
[fops->remap_file_range() is available])
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
])
|
|
@ -116,6 +116,12 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
|
|||
ZFS_AC_KERNEL_SRC_VFS_RW_ITERATE
|
||||
ZFS_AC_KERNEL_SRC_VFS_GENERIC_WRITE_CHECKS
|
||||
ZFS_AC_KERNEL_SRC_VFS_IOV_ITER
|
||||
ZFS_AC_KERNEL_SRC_VFS_COPY_FILE_RANGE
|
||||
ZFS_AC_KERNEL_SRC_VFS_GENERIC_COPY_FILE_RANGE
|
||||
ZFS_AC_KERNEL_SRC_VFS_REMAP_FILE_RANGE
|
||||
ZFS_AC_KERNEL_SRC_VFS_CLONE_FILE_RANGE
|
||||
ZFS_AC_KERNEL_SRC_VFS_DEDUPE_FILE_RANGE
|
||||
ZFS_AC_KERNEL_SRC_VFS_FILE_OPERATIONS_EXTEND
|
||||
ZFS_AC_KERNEL_SRC_KMAP_ATOMIC_ARGS
|
||||
ZFS_AC_KERNEL_SRC_FOLLOW_DOWN_ONE
|
||||
ZFS_AC_KERNEL_SRC_MAKE_REQUEST_FN
|
||||
|
@ -249,6 +255,12 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
|
|||
ZFS_AC_KERNEL_VFS_RW_ITERATE
|
||||
ZFS_AC_KERNEL_VFS_GENERIC_WRITE_CHECKS
|
||||
ZFS_AC_KERNEL_VFS_IOV_ITER
|
||||
ZFS_AC_KERNEL_VFS_COPY_FILE_RANGE
|
||||
ZFS_AC_KERNEL_VFS_GENERIC_COPY_FILE_RANGE
|
||||
ZFS_AC_KERNEL_VFS_REMAP_FILE_RANGE
|
||||
ZFS_AC_KERNEL_VFS_CLONE_FILE_RANGE
|
||||
ZFS_AC_KERNEL_VFS_DEDUPE_FILE_RANGE
|
||||
ZFS_AC_KERNEL_VFS_FILE_OPERATIONS_EXTEND
|
||||
ZFS_AC_KERNEL_KMAP_ATOMIC_ARGS
|
||||
ZFS_AC_KERNEL_FOLLOW_DOWN_ONE
|
||||
ZFS_AC_KERNEL_MAKE_REQUEST_FN
|
||||
|
|
|
@ -52,7 +52,11 @@ extern const struct inode_operations zpl_special_inode_operations;
|
|||
|
||||
/* zpl_file.c */
|
||||
extern const struct address_space_operations zpl_address_space_operations;
|
||||
#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
|
||||
extern const struct file_operations_extend zpl_file_operations;
|
||||
#else
|
||||
extern const struct file_operations zpl_file_operations;
|
||||
#endif
|
||||
extern const struct file_operations zpl_dir_file_operations;
|
||||
|
||||
/* zpl_super.c */
|
||||
|
@ -189,6 +193,55 @@ zpl_dir_emit_dots(struct file *file, zpl_dir_context_t *ctx)
|
|||
}
|
||||
#endif /* HAVE_VFS_ITERATE */
|
||||
|
||||
|
||||
/* zpl_file_range.c */
|
||||
|
||||
/* handlers for file_operations of the same name */
|
||||
extern 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);
|
||||
extern 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);
|
||||
extern int zpl_clone_file_range(struct file *src_file, loff_t src_off,
|
||||
struct file *dst_file, loff_t dst_off, uint64_t len);
|
||||
extern int zpl_dedupe_file_range(struct file *src_file, loff_t src_off,
|
||||
struct file *dst_file, loff_t dst_off, uint64_t len);
|
||||
|
||||
/* compat for FICLONE/FICLONERANGE/FIDEDUPERANGE ioctls */
|
||||
typedef struct {
|
||||
int64_t fcr_src_fd;
|
||||
uint64_t fcr_src_offset;
|
||||
uint64_t fcr_src_length;
|
||||
uint64_t fcr_dest_offset;
|
||||
} zfs_ioc_compat_file_clone_range_t;
|
||||
|
||||
typedef struct {
|
||||
int64_t fdri_dest_fd;
|
||||
uint64_t fdri_dest_offset;
|
||||
uint64_t fdri_bytes_deduped;
|
||||
int32_t fdri_status;
|
||||
uint32_t fdri_reserved;
|
||||
} zfs_ioc_compat_dedupe_range_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t fdr_src_offset;
|
||||
uint64_t fdr_src_length;
|
||||
uint16_t fdr_dest_count;
|
||||
uint16_t fdr_reserved1;
|
||||
uint32_t fdr_reserved2;
|
||||
zfs_ioc_compat_dedupe_range_info_t fdr_info[];
|
||||
} zfs_ioc_compat_dedupe_range_t;
|
||||
|
||||
#define ZFS_IOC_COMPAT_FICLONE _IOW(0x94, 9, int)
|
||||
#define ZFS_IOC_COMPAT_FICLONERANGE \
|
||||
_IOW(0x94, 13, zfs_ioc_compat_file_clone_range_t)
|
||||
#define ZFS_IOC_COMPAT_FIDEDUPERANGE \
|
||||
_IOWR(0x94, 54, zfs_ioc_compat_dedupe_range_t)
|
||||
|
||||
extern long zpl_ioctl_ficlone(struct file *filp, void *arg);
|
||||
extern long zpl_ioctl_ficlonerange(struct file *filp, void *arg);
|
||||
extern long zpl_ioctl_fideduperange(struct file *filp, void *arg);
|
||||
|
||||
|
||||
#if defined(HAVE_INODE_TIMESTAMP_TRUNCATE)
|
||||
#define zpl_inode_timestamp_truncate(ts, ip) timestamp_truncate(ts, ip)
|
||||
#elif defined(HAVE_INODE_TIMESPEC64_TIMES)
|
||||
|
|
|
@ -462,6 +462,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 \
|
||||
|
|
|
@ -2110,6 +2110,9 @@ zfs_init(void)
|
|||
zfs_znode_init();
|
||||
dmu_objset_register_type(DMU_OST_ZFS, zpl_get_file_info);
|
||||
register_filesystem(&zpl_fs_type);
|
||||
#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
|
||||
register_fo_extend(&zpl_file_operations);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2120,6 +2123,9 @@ zfs_fini(void)
|
|||
*/
|
||||
taskq_wait(system_delay_taskq);
|
||||
taskq_wait(system_taskq);
|
||||
#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
|
||||
unregister_fo_extend(&zpl_file_operations);
|
||||
#endif
|
||||
unregister_filesystem(&zpl_fs_type);
|
||||
zfs_znode_fini();
|
||||
zfsctl_fini();
|
||||
|
|
|
@ -415,7 +415,11 @@ zfs_inode_set_ops(zfsvfs_t *zfsvfs, struct inode *ip)
|
|||
switch (ip->i_mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
ip->i_op = &zpl_inode_operations;
|
||||
#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
|
||||
ip->i_fop = &zpl_file_operations.kabi_fops;
|
||||
#else
|
||||
ip->i_fop = &zpl_file_operations;
|
||||
#endif
|
||||
ip->i_mapping->a_ops = &zpl_address_space_operations;
|
||||
break;
|
||||
|
||||
|
@ -455,7 +459,11 @@ zfs_inode_set_ops(zfsvfs_t *zfsvfs, struct inode *ip)
|
|||
/* Assume the inode is a file and attempt to continue */
|
||||
ip->i_mode = S_IFREG | 0644;
|
||||
ip->i_op = &zpl_inode_operations;
|
||||
#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
|
||||
ip->i_fop = &zpl_file_operations.kabi_fops;
|
||||
#else
|
||||
ip->i_fop = &zpl_file_operations;
|
||||
#endif
|
||||
ip->i_mapping->a_ops = &zpl_address_space_operations;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1257,6 +1257,12 @@ zpl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
return (zpl_ioctl_getdosflags(filp, (void *)arg));
|
||||
case ZFS_IOC_SETDOSFLAGS:
|
||||
return (zpl_ioctl_setdosflags(filp, (void *)arg));
|
||||
case ZFS_IOC_COMPAT_FICLONE:
|
||||
return (zpl_ioctl_ficlone(filp, (void *)arg));
|
||||
case ZFS_IOC_COMPAT_FICLONERANGE:
|
||||
return (zpl_ioctl_ficlonerange(filp, (void *)arg));
|
||||
case ZFS_IOC_COMPAT_FIDEDUPERANGE:
|
||||
return (zpl_ioctl_fideduperange(filp, (void *)arg));
|
||||
default:
|
||||
return (-ENOTTY);
|
||||
}
|
||||
|
@ -1283,7 +1289,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,
|
||||
|
@ -1306,7 +1311,12 @@ const struct address_space_operations zpl_address_space_operations = {
|
|||
#endif
|
||||
};
|
||||
|
||||
#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
|
||||
const struct file_operations_extend zpl_file_operations = {
|
||||
.kabi_fops = {
|
||||
#else
|
||||
const struct file_operations zpl_file_operations = {
|
||||
#endif
|
||||
.open = zpl_open,
|
||||
.release = zpl_release,
|
||||
.llseek = zpl_llseek,
|
||||
|
@ -1333,6 +1343,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_CLONE_FILE_RANGE
|
||||
.clone_file_range = zpl_clone_file_range,
|
||||
#endif
|
||||
#ifdef HAVE_VFS_REMAP_FILE_RANGE
|
||||
.remap_file_range = zpl_remap_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
|
||||
|
@ -1340,6 +1362,11 @@ const struct file_operations zpl_file_operations = {
|
|||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = zpl_compat_ioctl,
|
||||
#endif
|
||||
#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
|
||||
}, /* kabi_fops */
|
||||
.copy_file_range = zpl_copy_file_range,
|
||||
.clone_file_range = zpl_clone_file_range,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct file_operations zpl_dir_file_operations = {
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
#if defined(HAVE_VFS_COPY_FILE_RANGE) || \
|
||||
defined(HAVE_VFS_FILE_OPERATIONS_EXTEND)
|
||||
/*
|
||||
* 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 || HAVE_VFS_FILE_OPERATIONS_EXTEND */
|
||||
|
||||
#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 */
|
||||
|
||||
#if defined(HAVE_VFS_CLONE_FILE_RANGE) || \
|
||||
defined(HAVE_VFS_FILE_OPERATIONS_EXTEND)
|
||||
/*
|
||||
* 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 || HAVE_VFS_FILE_OPERATIONS_EXTEND */
|
||||
|
||||
#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 */
|
||||
|
||||
/* Entry point for FICLONE, before Linux 4.5. */
|
||||
long
|
||||
zpl_ioctl_ficlone(struct file *dst_file, void *arg)
|
||||
{
|
||||
unsigned long sfd = (unsigned long)arg;
|
||||
|
||||
struct file *src_file = fget(sfd);
|
||||
if (src_file == NULL)
|
||||
return (-EBADF);
|
||||
|
||||
if (dst_file->f_op != src_file->f_op)
|
||||
return (-EXDEV);
|
||||
|
||||
size_t len = i_size_read(file_inode(src_file));
|
||||
|
||||
ssize_t ret =
|
||||
__zpl_clone_file_range(src_file, 0, dst_file, 0, len);
|
||||
|
||||
fput(src_file);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -EOPNOTSUPP)
|
||||
return (-ENOTTY);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (ret != len)
|
||||
return (-EINVAL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Entry point for FICLONERANGE, before Linux 4.5. */
|
||||
long
|
||||
zpl_ioctl_ficlonerange(struct file *dst_file, void __user *arg)
|
||||
{
|
||||
zfs_ioc_compat_file_clone_range_t fcr;
|
||||
|
||||
if (copy_from_user(&fcr, arg, sizeof (fcr)))
|
||||
return (-EFAULT);
|
||||
|
||||
struct file *src_file = fget(fcr.fcr_src_fd);
|
||||
if (src_file == NULL)
|
||||
return (-EBADF);
|
||||
|
||||
if (dst_file->f_op != src_file->f_op)
|
||||
return (-EXDEV);
|
||||
|
||||
size_t len = fcr.fcr_src_length;
|
||||
if (len == 0)
|
||||
len = i_size_read(file_inode(src_file)) - fcr.fcr_src_offset;
|
||||
|
||||
ssize_t ret = __zpl_clone_file_range(src_file, fcr.fcr_src_offset,
|
||||
dst_file, fcr.fcr_dest_offset, len);
|
||||
|
||||
fput(src_file);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -EOPNOTSUPP)
|
||||
return (-ENOTTY);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (ret != len)
|
||||
return (-EINVAL);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Entry point for FIDEDUPERANGE, before Linux 4.5. */
|
||||
long
|
||||
zpl_ioctl_fideduperange(struct file *filp, void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
|
||||
/* No support for dedup yet */
|
||||
return (-ENOTTY);
|
||||
}
|
|
@ -680,7 +680,7 @@ brt_vdev_realloc(brt_t *brt, brt_vdev_t *brtvd)
|
|||
size = (vdev_get_min_asize(vd) - 1) / brt->brt_rangesize + 1;
|
||||
spa_config_exit(brt->brt_spa, SCL_VDEV, FTAG);
|
||||
|
||||
entcount = kmem_zalloc(sizeof (entcount[0]) * size, KM_SLEEP);
|
||||
entcount = vmem_zalloc(sizeof (entcount[0]) * size, KM_SLEEP);
|
||||
nblocks = BRT_RANGESIZE_TO_NBLOCKS(size);
|
||||
bitmap = kmem_zalloc(BT_SIZEOFMAP(nblocks), KM_SLEEP);
|
||||
|
||||
|
@ -709,7 +709,7 @@ brt_vdev_realloc(brt_t *brt, brt_vdev_t *brtvd)
|
|||
sizeof (entcount[0]) * MIN(size, brtvd->bv_size));
|
||||
memcpy(bitmap, brtvd->bv_bitmap, MIN(BT_SIZEOFMAP(nblocks),
|
||||
BT_SIZEOFMAP(brtvd->bv_nblocks)));
|
||||
kmem_free(brtvd->bv_entcount,
|
||||
vmem_free(brtvd->bv_entcount,
|
||||
sizeof (entcount[0]) * brtvd->bv_size);
|
||||
kmem_free(brtvd->bv_bitmap, BT_SIZEOFMAP(brtvd->bv_nblocks));
|
||||
}
|
||||
|
@ -792,7 +792,7 @@ brt_vdev_dealloc(brt_t *brt, brt_vdev_t *brtvd)
|
|||
ASSERT(RW_WRITE_HELD(&brt->brt_lock));
|
||||
ASSERT(brtvd->bv_initiated);
|
||||
|
||||
kmem_free(brtvd->bv_entcount, sizeof (uint16_t) * brtvd->bv_size);
|
||||
vmem_free(brtvd->bv_entcount, sizeof (uint16_t) * brtvd->bv_size);
|
||||
brtvd->bv_entcount = NULL;
|
||||
kmem_free(brtvd->bv_bitmap, BT_SIZEOFMAP(brtvd->bv_nblocks));
|
||||
brtvd->bv_bitmap = NULL;
|
||||
|
|
|
@ -2701,7 +2701,7 @@ dmu_buf_will_clone(dmu_buf_t *db_fake, dmu_tx_t *tx)
|
|||
*/
|
||||
mutex_enter(&db->db_mtx);
|
||||
VERIFY(!dbuf_undirty(db, tx));
|
||||
ASSERT(list_head(&db->db_dirty_records) == NULL);
|
||||
ASSERT0(dbuf_find_dirty_eq(db, tx->tx_txg));
|
||||
if (db->db_buf != NULL) {
|
||||
arc_buf_destroy(db->db_buf, db);
|
||||
db->db_buf = NULL;
|
||||
|
@ -4457,6 +4457,15 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
|
|||
} else if (db->db_state == DB_FILL) {
|
||||
/* This buffer was freed and is now being re-filled */
|
||||
ASSERT(db->db.db_data != dr->dt.dl.dr_data);
|
||||
} else if (db->db_state == DB_READ) {
|
||||
/*
|
||||
* This buffer has a clone we need to write, and an in-flight
|
||||
* read on the BP we're about to clone. Its safe to issue the
|
||||
* write here because the read has already been issued and the
|
||||
* contents won't change.
|
||||
*/
|
||||
ASSERT(dr->dt.dl.dr_brtwrite &&
|
||||
dr->dt.dl.dr_override_state == DR_OVERRIDDEN);
|
||||
} else {
|
||||
ASSERT(db->db_state == DB_CACHED || db->db_state == DB_NOFILL);
|
||||
}
|
||||
|
|
|
@ -1212,7 +1212,7 @@ zfs_clone_range(znode_t *inzp, uint64_t *inoffp, znode_t *outzp,
|
|||
gid = KGID_TO_SGID(ZTOGID(outzp));
|
||||
projid = outzp->z_projid;
|
||||
|
||||
bps = kmem_alloc(sizeof (bps[0]) * maxblocks, KM_SLEEP);
|
||||
bps = vmem_alloc(sizeof (bps[0]) * maxblocks, KM_SLEEP);
|
||||
|
||||
/*
|
||||
* Clone the file in reasonable size chunks. Each chunk is cloned
|
||||
|
@ -1330,7 +1330,7 @@ zfs_clone_range(znode_t *inzp, uint64_t *inoffp, znode_t *outzp,
|
|||
done += size;
|
||||
}
|
||||
|
||||
kmem_free(bps, sizeof (bps[0]) * maxblocks);
|
||||
vmem_free(bps, sizeof (bps[0]) * maxblocks);
|
||||
zfs_znode_update_vfs(outzp);
|
||||
|
||||
unlock:
|
||||
|
|
|
@ -151,6 +151,7 @@ static kmem_cache_t *zil_lwb_cache;
|
|||
static kmem_cache_t *zil_zcw_cache;
|
||||
|
||||
static void zil_lwb_commit(zilog_t *zilog, lwb_t *lwb, itx_t *itx);
|
||||
static void zil_lwb_write_issue(zilog_t *zilog, lwb_t *lwb);
|
||||
static itx_t *zil_itx_clone(itx_t *oitx);
|
||||
|
||||
static int
|
||||
|
@ -1768,7 +1769,7 @@ static uint_t zil_maxblocksize = SPA_OLD_MAXBLOCKSIZE;
|
|||
* Has to be called under zl_issuer_lock to chain more lwbs.
|
||||
*/
|
||||
static lwb_t *
|
||||
zil_lwb_write_close(zilog_t *zilog, lwb_t *lwb)
|
||||
zil_lwb_write_close(zilog_t *zilog, lwb_t *lwb, list_t *ilwbs)
|
||||
{
|
||||
lwb_t *nlwb = NULL;
|
||||
zil_chain_t *zilc;
|
||||
|
@ -1870,6 +1871,27 @@ zil_lwb_write_close(zilog_t *zilog, lwb_t *lwb)
|
|||
|
||||
dmu_tx_commit(tx);
|
||||
|
||||
/*
|
||||
* We need to acquire the config lock for the lwb to issue it later.
|
||||
* However, if we already have a queue of closed parent lwbs already
|
||||
* holding the config lock (but not yet issued), we can't block here
|
||||
* waiting on the lock or we will deadlock. In that case we must
|
||||
* first issue to parent IOs before waiting on the lock.
|
||||
*/
|
||||
if (ilwbs && !list_is_empty(ilwbs)) {
|
||||
if (!spa_config_tryenter(spa, SCL_STATE, lwb, RW_READER)) {
|
||||
lwb_t *tlwb;
|
||||
while ((tlwb = list_remove_head(ilwbs)) != NULL)
|
||||
zil_lwb_write_issue(zilog, tlwb);
|
||||
spa_config_enter(spa, SCL_STATE, lwb, RW_READER);
|
||||
}
|
||||
} else {
|
||||
spa_config_enter(spa, SCL_STATE, lwb, RW_READER);
|
||||
}
|
||||
|
||||
if (ilwbs)
|
||||
list_insert_tail(ilwbs, lwb);
|
||||
|
||||
/*
|
||||
* If there was an allocation failure then nlwb will be null which
|
||||
* forces a txg_wait_synced().
|
||||
|
@ -1933,7 +1955,7 @@ zil_lwb_write_issue(zilog_t *zilog, lwb_t *lwb)
|
|||
ZIL_STAT_INCR(zilog, zil_itx_metaslab_normal_alloc,
|
||||
BP_GET_LSIZE(&lwb->lwb_blk));
|
||||
}
|
||||
spa_config_enter(zilog->zl_spa, SCL_STATE, lwb, RW_READER);
|
||||
ASSERT(spa_config_held(zilog->zl_spa, SCL_STATE, RW_READER));
|
||||
zil_lwb_add_block(lwb, &lwb->lwb_blk);
|
||||
lwb->lwb_issued_timestamp = gethrtime();
|
||||
zio_nowait(lwb->lwb_root_zio);
|
||||
|
@ -2037,8 +2059,7 @@ cont:
|
|||
lwb_sp < zil_max_waste_space(zilog) &&
|
||||
(dlen % max_log_data == 0 ||
|
||||
lwb_sp < reclen + dlen % max_log_data))) {
|
||||
list_insert_tail(ilwbs, lwb);
|
||||
lwb = zil_lwb_write_close(zilog, lwb);
|
||||
lwb = zil_lwb_write_close(zilog, lwb, ilwbs);
|
||||
if (lwb == NULL)
|
||||
return (NULL);
|
||||
zil_lwb_write_open(zilog, lwb);
|
||||
|
@ -2937,8 +2958,7 @@ zil_process_commit_list(zilog_t *zilog, zil_commit_waiter_t *zcw, list_t *ilwbs)
|
|||
zfs_commit_timeout_pct / 100;
|
||||
if (sleep < zil_min_commit_timeout ||
|
||||
lwb->lwb_sz - lwb->lwb_nused < lwb->lwb_sz / 8) {
|
||||
list_insert_tail(ilwbs, lwb);
|
||||
lwb = zil_lwb_write_close(zilog, lwb);
|
||||
lwb = zil_lwb_write_close(zilog, lwb, ilwbs);
|
||||
zilog->zl_cur_used = 0;
|
||||
if (lwb == NULL) {
|
||||
while ((lwb = list_remove_head(ilwbs))
|
||||
|
@ -3096,7 +3116,7 @@ zil_commit_waiter_timeout(zilog_t *zilog, zil_commit_waiter_t *zcw)
|
|||
* since we've reached the commit waiter's timeout and it still
|
||||
* hasn't been issued.
|
||||
*/
|
||||
lwb_t *nlwb = zil_lwb_write_close(zilog, lwb);
|
||||
lwb_t *nlwb = zil_lwb_write_close(zilog, lwb, NULL);
|
||||
|
||||
ASSERT3S(lwb->lwb_state, !=, LWB_STATE_OPENED);
|
||||
|
||||
|
@ -3921,13 +3941,11 @@ zil_suspend(const char *osname, void **cookiep)
|
|||
return (error);
|
||||
zilog = dmu_objset_zil(os);
|
||||
|
||||
mutex_enter(&zilog->zl_issuer_lock);
|
||||
mutex_enter(&zilog->zl_lock);
|
||||
zh = zilog->zl_header;
|
||||
|
||||
if (zh->zh_flags & ZIL_REPLAY_NEEDED) { /* unplayed log */
|
||||
mutex_exit(&zilog->zl_lock);
|
||||
mutex_exit(&zilog->zl_issuer_lock);
|
||||
dmu_objset_rele(os, suspend_tag);
|
||||
return (SET_ERROR(EBUSY));
|
||||
}
|
||||
|
@ -3941,7 +3959,6 @@ zil_suspend(const char *osname, void **cookiep)
|
|||
if (cookiep == NULL && !zilog->zl_suspending &&
|
||||
(zilog->zl_suspend > 0 || BP_IS_HOLE(&zh->zh_log))) {
|
||||
mutex_exit(&zilog->zl_lock);
|
||||
mutex_exit(&zilog->zl_issuer_lock);
|
||||
dmu_objset_rele(os, suspend_tag);
|
||||
return (0);
|
||||
}
|
||||
|
@ -3950,7 +3967,6 @@ zil_suspend(const char *osname, void **cookiep)
|
|||
dsl_pool_rele(dmu_objset_pool(os), suspend_tag);
|
||||
|
||||
zilog->zl_suspend++;
|
||||
mutex_exit(&zilog->zl_issuer_lock);
|
||||
|
||||
if (zilog->zl_suspend > 1) {
|
||||
/*
|
||||
|
|
|
@ -34,6 +34,15 @@ tags = ['functional', 'acl', 'posix-sa']
|
|||
tests = ['atime_003_pos', 'root_relatime_on']
|
||||
tags = ['functional', 'atime']
|
||||
|
||||
[tests/functional/block_cloning:Linux]
|
||||
tests = ['block_cloning_copyfilerange', 'block_cloning_copyfilerange_partial',
|
||||
'block_cloning_ficlone', 'block_cloning_ficlonerange',
|
||||
'block_cloning_ficlonerange_partial',
|
||||
'block_cloning_disabled_copyfilerange', 'block_cloning_disabled_ficlone',
|
||||
'block_cloning_disabled_ficlonerange',
|
||||
'block_cloning_copyfilerange_cross_dataset']
|
||||
tags = ['functional', 'block_cloning']
|
||||
|
||||
[tests/functional/chattr:Linux]
|
||||
tests = ['chattr_001_pos', 'chattr_002_neg']
|
||||
tags = ['functional', 'chattr']
|
||||
|
|
|
@ -134,6 +134,12 @@ ci_reason = 'CI runner doesn\'t have all requirements'
|
|||
#
|
||||
idmap_reason = 'Idmapped mount needs kernel 5.12+'
|
||||
|
||||
#
|
||||
# copy_file_range() is not supported by all kernels
|
||||
#
|
||||
cfr_reason = 'Kernel copy_file_range support required'
|
||||
cfr_cross_reason = 'copy_file_range(2) cross-filesystem needs kernel 5.3+'
|
||||
|
||||
#
|
||||
# These tests are known to fail, thus we use this list to prevent these
|
||||
# failures from failing the job as a whole; only unexpected failures
|
||||
|
@ -288,6 +294,14 @@ elif sys.platform.startswith('linux'):
|
|||
'idmap_mount/idmap_mount_003': ['SKIP', idmap_reason],
|
||||
'idmap_mount/idmap_mount_004': ['SKIP', idmap_reason],
|
||||
'idmap_mount/idmap_mount_005': ['SKIP', idmap_reason],
|
||||
'block_cloning/block_cloning_disabled_copyfilerange':
|
||||
['SKIP', cfr_reason],
|
||||
'block_cloning/block_cloning_copyfilerange':
|
||||
['SKIP', cfr_reason],
|
||||
'block_cloning/block_cloning_copyfilerange_partial':
|
||||
['SKIP', cfr_reason],
|
||||
'block_cloning/block_cloning_copyfilerange_cross_dataset':
|
||||
['SKIP', cfr_cross_reason],
|
||||
})
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/badsend
|
||||
/btree_test
|
||||
/chg_usr_exec
|
||||
/clonefile
|
||||
/devname2devid
|
||||
/dir_rd_update
|
||||
/draid
|
||||
|
|
|
@ -119,6 +119,7 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/renameat2
|
|||
scripts_zfs_tests_bin_PROGRAMS += %D%/xattrtest
|
||||
scripts_zfs_tests_bin_PROGRAMS += %D%/zed_fd_spill-zedlet
|
||||
scripts_zfs_tests_bin_PROGRAMS += %D%/idmap_util
|
||||
scripts_zfs_tests_bin_PROGRAMS += %D%/clonefile
|
||||
|
||||
%C%_idmap_util_LDADD = libspl.la
|
||||
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (c) 2023, Rob Norris <robn@despairlabs.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is to test the availability and behaviour of copy_file_range,
|
||||
* FICLONE, FICLONERANGE and FIDEDUPERANGE in the Linux kernel. It should
|
||||
* compile and run even if these features aren't exposed through the libc.
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef __NR_copy_file_range
|
||||
#if defined(__x86_64__)
|
||||
#define __NR_copy_file_range (326)
|
||||
#elif defined(__i386__)
|
||||
#define __NR_copy_file_range (377)
|
||||
#elif defined(__s390__)
|
||||
#define __NR_copy_file_range (375)
|
||||
#elif defined(__arm__)
|
||||
#define __NR_copy_file_range (391)
|
||||
#elif defined(__aarch64__)
|
||||
#define __NR_copy_file_range (285)
|
||||
#elif defined(__powerpc__)
|
||||
#define __NR_copy_file_range (379)
|
||||
#else
|
||||
#error "no definition of __NR_copy_file_range for this platform"
|
||||
#endif
|
||||
#endif /* __NR_copy_file_range */
|
||||
|
||||
ssize_t
|
||||
copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int)
|
||||
__attribute__((weak));
|
||||
|
||||
static inline ssize_t
|
||||
cf_copy_file_range(int sfd, loff_t *soff, int dfd, loff_t *doff,
|
||||
size_t len, unsigned int flags)
|
||||
{
|
||||
if (copy_file_range)
|
||||
return (copy_file_range(sfd, soff, dfd, doff, len, flags));
|
||||
return (
|
||||
syscall(__NR_copy_file_range, sfd, soff, dfd, doff, len, flags));
|
||||
}
|
||||
|
||||
/* Define missing FICLONE */
|
||||
#ifdef FICLONE
|
||||
#define CF_FICLONE FICLONE
|
||||
#else
|
||||
#define CF_FICLONE _IOW(0x94, 9, int)
|
||||
#endif
|
||||
|
||||
/* Define missing FICLONERANGE and support structs */
|
||||
#ifdef FICLONERANGE
|
||||
#define CF_FICLONERANGE FICLONERANGE
|
||||
typedef struct file_clone_range cf_file_clone_range_t;
|
||||
#else
|
||||
typedef struct {
|
||||
int64_t src_fd;
|
||||
uint64_t src_offset;
|
||||
uint64_t src_length;
|
||||
uint64_t dest_offset;
|
||||
} cf_file_clone_range_t;
|
||||
#define CF_FICLONERANGE _IOW(0x94, 13, cf_file_clone_range_t)
|
||||
#endif
|
||||
|
||||
/* Define missing FIDEDUPERANGE and support structs */
|
||||
#ifdef FIDEDUPERANGE
|
||||
#define CF_FIDEDUPERANGE FIDEDUPERANGE
|
||||
#define CF_FILE_DEDUPE_RANGE_SAME FILE_DEDUPE_RANGE_SAME
|
||||
#define CF_FILE_DEDUPE_RANGE_DIFFERS FILE_DEDUPE_RANGE_DIFFERS
|
||||
typedef struct file_dedupe_range_info cf_file_dedupe_range_info_t;
|
||||
typedef struct file_dedupe_range cf_file_dedupe_range_t;
|
||||
#else
|
||||
typedef struct {
|
||||
int64_t dest_fd;
|
||||
uint64_t dest_offset;
|
||||
uint64_t bytes_deduped;
|
||||
int32_t status;
|
||||
uint32_t reserved;
|
||||
} cf_file_dedupe_range_info_t;
|
||||
typedef struct {
|
||||
uint64_t src_offset;
|
||||
uint64_t src_length;
|
||||
uint16_t dest_count;
|
||||
uint16_t reserved1;
|
||||
uint32_t reserved2;
|
||||
cf_file_dedupe_range_info_t info[0];
|
||||
} cf_file_dedupe_range_t;
|
||||
#define CF_FIDEDUPERANGE _IOWR(0x94, 54, cf_file_dedupe_range_t)
|
||||
#define CF_FILE_DEDUPE_RANGE_SAME (0)
|
||||
#define CF_FILE_DEDUPE_RANGE_DIFFERS (1)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CF_MODE_NONE,
|
||||
CF_MODE_CLONE,
|
||||
CF_MODE_CLONERANGE,
|
||||
CF_MODE_COPYFILERANGE,
|
||||
CF_MODE_DEDUPERANGE,
|
||||
} cf_mode_t;
|
||||
|
||||
static int
|
||||
usage(void)
|
||||
{
|
||||
printf(
|
||||
"usage:\n"
|
||||
" FICLONE:\n"
|
||||
" clonefile -c <src> <dst>\n"
|
||||
" FICLONERANGE:\n"
|
||||
" clonefile -r <src> <dst> <soff> <doff> <len>\n"
|
||||
" copy_file_range:\n"
|
||||
" clonefile -f <src> <dst> <soff> <doff> <len>\n"
|
||||
" FIDEDUPERANGE:\n"
|
||||
" clonefile -d <src> <dst> <soff> <doff> <len>\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
int do_clone(int sfd, int dfd);
|
||||
int do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
|
||||
int do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
|
||||
int do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len);
|
||||
|
||||
int quiet = 0;
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
cf_mode_t mode = CF_MODE_NONE;
|
||||
|
||||
char c;
|
||||
while ((c = getopt(argc, argv, "crfdq")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
mode = CF_MODE_CLONE;
|
||||
break;
|
||||
case 'r':
|
||||
mode = CF_MODE_CLONERANGE;
|
||||
break;
|
||||
case 'f':
|
||||
mode = CF_MODE_COPYFILERANGE;
|
||||
break;
|
||||
case 'd':
|
||||
mode = CF_MODE_DEDUPERANGE;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == CF_MODE_NONE || (argc-optind) < 2 ||
|
||||
(mode != CF_MODE_CLONE && (argc-optind) < 5))
|
||||
return (usage());
|
||||
|
||||
loff_t soff = 0, doff = 0;
|
||||
size_t len = 0;
|
||||
if (mode != CF_MODE_CLONE) {
|
||||
soff = strtoull(argv[optind+2], NULL, 10);
|
||||
if (soff == ULLONG_MAX) {
|
||||
fprintf(stderr, "invalid source offset");
|
||||
return (1);
|
||||
}
|
||||
doff = strtoull(argv[optind+3], NULL, 10);
|
||||
if (doff == ULLONG_MAX) {
|
||||
fprintf(stderr, "invalid dest offset");
|
||||
return (1);
|
||||
}
|
||||
len = strtoull(argv[optind+4], NULL, 10);
|
||||
if (len == ULLONG_MAX) {
|
||||
fprintf(stderr, "invalid length");
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
|
||||
int sfd = open(argv[optind], O_RDONLY);
|
||||
if (sfd < 0) {
|
||||
fprintf(stderr, "open: %s: %s\n",
|
||||
argv[optind], strerror(errno));
|
||||
return (1);
|
||||
}
|
||||
|
||||
int dfd = open(argv[optind+1], O_WRONLY|O_CREAT,
|
||||
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
||||
if (sfd < 0) {
|
||||
fprintf(stderr, "open: %s: %s\n",
|
||||
argv[optind+1], strerror(errno));
|
||||
close(sfd);
|
||||
return (1);
|
||||
}
|
||||
|
||||
int err;
|
||||
switch (mode) {
|
||||
case CF_MODE_CLONE:
|
||||
err = do_clone(sfd, dfd);
|
||||
break;
|
||||
case CF_MODE_CLONERANGE:
|
||||
err = do_clonerange(sfd, dfd, soff, doff, len);
|
||||
break;
|
||||
case CF_MODE_COPYFILERANGE:
|
||||
err = do_copyfilerange(sfd, dfd, soff, doff, len);
|
||||
break;
|
||||
case CF_MODE_DEDUPERANGE:
|
||||
err = do_deduperange(sfd, dfd, soff, doff, len);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
off_t spos = lseek(sfd, 0, SEEK_CUR);
|
||||
off_t slen = lseek(sfd, 0, SEEK_END);
|
||||
off_t dpos = lseek(dfd, 0, SEEK_CUR);
|
||||
off_t dlen = lseek(dfd, 0, SEEK_END);
|
||||
|
||||
fprintf(stderr, "file offsets: src=%lu/%lu; dst=%lu/%lu\n", spos, slen,
|
||||
dpos, dlen);
|
||||
|
||||
close(dfd);
|
||||
close(sfd);
|
||||
|
||||
return (err == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
int
|
||||
do_clone(int sfd, int dfd)
|
||||
{
|
||||
fprintf(stderr, "using FICLONE\n");
|
||||
int err = ioctl(dfd, CF_FICLONE, sfd);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "ioctl(FICLONE): %s\n", strerror(errno));
|
||||
return (err);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
|
||||
{
|
||||
fprintf(stderr, "using FICLONERANGE\n");
|
||||
cf_file_clone_range_t fcr = {
|
||||
.src_fd = sfd,
|
||||
.src_offset = soff,
|
||||
.src_length = len,
|
||||
.dest_offset = doff,
|
||||
};
|
||||
int err = ioctl(dfd, CF_FICLONERANGE, &fcr);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "ioctl(FICLONERANGE): %s\n", strerror(errno));
|
||||
return (err);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
|
||||
{
|
||||
fprintf(stderr, "using copy_file_range\n");
|
||||
ssize_t copied = cf_copy_file_range(sfd, &soff, dfd, &doff, len, 0);
|
||||
if (copied < 0) {
|
||||
fprintf(stderr, "copy_file_range: %s\n", strerror(errno));
|
||||
return (1);
|
||||
}
|
||||
if (copied != len) {
|
||||
fprintf(stderr, "copy_file_range: copied less than requested: "
|
||||
"requested=%lu; copied=%lu\n", len, copied);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
|
||||
{
|
||||
fprintf(stderr, "using FIDEDUPERANGE\n");
|
||||
|
||||
char buf[sizeof (cf_file_dedupe_range_t)+
|
||||
sizeof (cf_file_dedupe_range_info_t)] = {0};
|
||||
cf_file_dedupe_range_t *fdr = (cf_file_dedupe_range_t *)&buf[0];
|
||||
cf_file_dedupe_range_info_t *fdri =
|
||||
(cf_file_dedupe_range_info_t *)
|
||||
&buf[sizeof (cf_file_dedupe_range_t)];
|
||||
|
||||
fdr->src_offset = soff;
|
||||
fdr->src_length = len;
|
||||
fdr->dest_count = 1;
|
||||
|
||||
fdri->dest_fd = dfd;
|
||||
fdri->dest_offset = doff;
|
||||
|
||||
int err = ioctl(sfd, CF_FIDEDUPERANGE, fdr);
|
||||
if (err != 0)
|
||||
fprintf(stderr, "ioctl(FIDEDUPERANGE): %s\n", strerror(errno));
|
||||
|
||||
if (fdri->status < 0) {
|
||||
fprintf(stderr, "dedup failed: %s\n", strerror(-fdri->status));
|
||||
err = -1;
|
||||
} else if (fdri->status == CF_FILE_DEDUPE_RANGE_DIFFERS) {
|
||||
fprintf(stderr, "dedup failed: range differs\n");
|
||||
err = -1;
|
||||
}
|
||||
|
||||
return (err);
|
||||
}
|
|
@ -182,6 +182,7 @@ export ZFS_FILES='zdb
|
|||
export ZFSTEST_FILES='badsend
|
||||
btree_test
|
||||
chg_usr_exec
|
||||
clonefile
|
||||
devname2devid
|
||||
dir_rd_update
|
||||
draid
|
||||
|
|
|
@ -90,6 +90,7 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \
|
|||
functional/alloc_class/alloc_class.kshlib \
|
||||
functional/atime/atime.cfg \
|
||||
functional/atime/atime_common.kshlib \
|
||||
functional/block_cloning/block_cloning.kshlib \
|
||||
functional/cache/cache.cfg \
|
||||
functional/cache/cache.kshlib \
|
||||
functional/cachefile/cachefile.cfg \
|
||||
|
@ -437,6 +438,17 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
|||
functional/atime/root_atime_on.ksh \
|
||||
functional/atime/root_relatime_on.ksh \
|
||||
functional/atime/setup.ksh \
|
||||
functional/block_cloning/cleanup.ksh \
|
||||
functional/block_cloning/setup.ksh \
|
||||
functional/block_cloning/block_cloning_copyfilerange_cross_dataset.ksh \
|
||||
functional/block_cloning/block_cloning_copyfilerange.ksh \
|
||||
functional/block_cloning/block_cloning_copyfilerange_partial.ksh \
|
||||
functional/block_cloning/block_cloning_disabled_copyfilerange.ksh \
|
||||
functional/block_cloning/block_cloning_disabled_ficlone.ksh \
|
||||
functional/block_cloning/block_cloning_disabled_ficlonerange.ksh \
|
||||
functional/block_cloning/block_cloning_ficlone.ksh \
|
||||
functional/block_cloning/block_cloning_ficlonerange.ksh \
|
||||
functional/block_cloning/block_cloning_ficlonerange_partial.ksh \
|
||||
functional/bootfs/bootfs_001_pos.ksh \
|
||||
functional/bootfs/bootfs_002_neg.ksh \
|
||||
functional/bootfs/bootfs_003_pos.ksh \
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
function have_same_content
|
||||
{
|
||||
typeset hash1=$(cat $1 | md5sum)
|
||||
typeset hash2=$(cat $2 | md5sum)
|
||||
|
||||
log_must [ "$hash1" = "$hash2" ]
|
||||
}
|
||||
|
||||
function unique_blocks
|
||||
{
|
||||
typeset zdbout=${TMPDIR:-$TEST_BASE_DIR}/zdbout.$$
|
||||
zdb -vvvvv $1 -O $2 | \
|
||||
awk '/ L0 / { print ++l " " $3 " " $7 }' > $zdbout.a
|
||||
zdb -vvvvv $3 -O $4 | \
|
||||
awk '/ L0 / { print ++l " " $3 " " $7 }' > $zdbout.b
|
||||
echo $(sort $zdbout.a $zdbout.b | uniq -d | cut -f1 -d' ')
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
if [[ $(linux_version) -lt $(linux_version "4.5") ]]; then
|
||||
log_unsupported "copy_file_range not available before Linux 4.5"
|
||||
fi
|
||||
|
||||
claim="The copy_file_range syscall can clone whole files."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must clonefile -f /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "1 2 3 4" ]
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,65 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
if [[ $(linux_version) -lt $(linux_version "5.3") ]]; then
|
||||
log_unsupported "copy_file_range can't copy cross-filesystem before Linux 5.3"
|
||||
fi
|
||||
|
||||
claim="The copy_file_range syscall can clone across datasets."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
|
||||
|
||||
log_must zfs create $TESTPOOL/$TESTFS1
|
||||
log_must zfs create $TESTPOOL/$TESTFS2
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS1/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must \
|
||||
clonefile -f /$TESTPOOL/$TESTFS1/file1 /$TESTPOOL/$TESTFS2/file2 0 0 524288
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/$TESTFS1/file1 /$TESTPOOL/$TESTFS2/file2
|
||||
|
||||
typeset blocks=$(unique_blocks \
|
||||
$TESTPOOL/$TESTFS1 file1 $TESTPOOL/$TESTFS2 file2)
|
||||
log_must [ "$blocks" = "1 2 3 4" ]
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,68 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
if [[ $(linux_version) -lt $(linux_version "4.5") ]]; then
|
||||
log_unsupported "copy_file_range not available before Linux 4.5"
|
||||
fi
|
||||
|
||||
claim="The copy_file_range syscall can clone parts of a file."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must dd if=/$TESTPOOL/file1 of=/$TESTPOOL/file2 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "" ]
|
||||
|
||||
log_must clonefile -f /$TESTPOOL/file1 /$TESTPOOL/file2 131072 131072 262144
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "2 3" ]
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,60 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
if [[ $(linux_version) -lt $(linux_version "4.5") ]]; then
|
||||
log_unsupported "copy_file_range not available before Linux 4.5"
|
||||
fi
|
||||
|
||||
claim="The copy_file_range syscall copies files when block cloning is disabled."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=disabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must clonefile -f /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "" ]
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,50 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
claim="The FICLONE ioctl fails when block cloning is disabled."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=disabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_mustnot clonefile -c /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,50 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
claim="The FICLONERANGE ioctl fails when block cloning is disabled."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=disabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_mustnot clonefile -r /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,56 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
claim="The FICLONE ioctl can clone files."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must clonefile -c /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "1 2 3 4" ]
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,56 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
claim="The FICLONERANGE ioctl can clone whole files."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must clonefile -r /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "1 2 3 4" ]
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
claim="The FICLONERANGE ioctl can clone parts of a file."
|
||||
|
||||
log_assert $claim
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
|
||||
|
||||
log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must dd if=/$TESTPOOL/file1 of=/$TESTPOOL/file2 bs=128K count=4
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "" ]
|
||||
|
||||
log_must clonefile -r /$TESTPOOL/file1 /$TESTPOOL/file2 131072 131072 262144
|
||||
log_must sync_pool $TESTPOOL
|
||||
|
||||
log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2
|
||||
|
||||
typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2)
|
||||
log_must [ "$blocks" = "2 3" ]
|
||||
|
||||
log_pass $claim
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
default_cleanup_noexit
|
||||
|
||||
log_pass
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/ksh -p
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
|
||||
|
||||
if ! command -v clonefile > /dev/null ; then
|
||||
log_unsupported "clonefile program required to test block cloning"
|
||||
fi
|
||||
|
||||
verify_runnable "global"
|
||||
|
||||
log_pass
|
Loading…
Reference in New Issue