diff --git a/config/kernel-vfs-extended-file_range.m4 b/config/kernel-vfs-extended-file_range.m4 new file mode 100644 index 000000000000..a2622313129e --- /dev/null +++ b/config/kernel-vfs-extended-file_range.m4 @@ -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 + + 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]) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index b17ccfdeec92..1487fa2e7793 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -121,6 +121,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ 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 @@ -259,6 +260,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ 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 diff --git a/include/os/linux/zfs/sys/zpl.h b/include/os/linux/zfs/sys/zpl.h index dec938b48ff3..5d11c7910143 100644 --- a/include/os/linux/zfs/sys/zpl.h +++ b/include/os/linux/zfs/sys/zpl.h @@ -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 */ diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c index 94184732f9cb..59efbe0978d7 100644 --- a/module/os/linux/zfs/zfs_vfsops.c +++ b/module/os/linux/zfs/zfs_vfsops.c @@ -2109,6 +2109,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 @@ -2119,6 +2122,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(); diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c index 02b1af3edc4f..335ae3460c58 100644 --- a/module/os/linux/zfs/zfs_znode.c +++ b/module/os/linux/zfs/zfs_znode.c @@ -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; } diff --git a/module/os/linux/zfs/zpl_file.c b/module/os/linux/zfs/zpl_file.c index 87a248af8303..73526db731c4 100644 --- a/module/os/linux/zfs/zpl_file.c +++ b/module/os/linux/zfs/zpl_file.c @@ -1311,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, @@ -1341,12 +1346,12 @@ const struct file_operations zpl_file_operations = { #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_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 @@ -1357,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 = { diff --git a/module/os/linux/zfs/zpl_file_range.c b/module/os/linux/zfs/zpl_file_range.c index aad502a8092e..18efebfc1dec 100644 --- a/module/os/linux/zfs/zpl_file_range.c +++ b/module/os/linux/zfs/zpl_file_range.c @@ -77,7 +77,8 @@ __zpl_clone_file_range(struct file *src_file, loff_t src_off, return ((ssize_t)len_o); } -#ifdef HAVE_VFS_COPY_FILE_RANGE +#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 @@ -94,7 +95,7 @@ zpl_copy_file_range(struct file *src_file, loff_t src_off, return (-EINVAL); /* Try to do it via zfs_clone_range() */ - ret =__zpl_clone_file_range(src_file, src_off, + ret = __zpl_clone_file_range(src_file, src_off, dst_file, dst_off, len); #ifdef HAVE_VFS_GENERIC_COPY_FILE_RANGE @@ -109,7 +110,7 @@ zpl_copy_file_range(struct file *src_file, loff_t src_off, return (ret); } -#endif /* HAVE_VFS_COPY_FILE_RANGE */ +#endif /* HAVE_VFS_COPY_FILE_RANGE || HAVE_VFS_FILE_OPERATIONS_EXTEND */ #ifdef HAVE_VFS_REMAP_FILE_RANGE /* @@ -152,7 +153,8 @@ zpl_remap_file_range(struct file *src_file, loff_t src_off, } #endif /* HAVE_VFS_REMAP_FILE_RANGE */ -#ifdef HAVE_VFS_CLONE_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. */ @@ -167,7 +169,7 @@ zpl_clone_file_range(struct file *src_file, loff_t src_off, return (__zpl_clone_file_range(src_file, src_off, dst_file, dst_off, len)); } -#endif /* HAVE_VFS_CLONE_FILE_RANGE */ +#endif /* HAVE_VFS_CLONE_FILE_RANGE || HAVE_VFS_FILE_OPERATIONS_EXTEND */ #ifdef HAVE_VFS_DEDUPE_FILE_RANGE /*