OSDN Git Service

vfs: combine the clone and dedupe into a single remap_file_range
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 29 Oct 2018 23:41:21 +0000 (10:41 +1100)
committerDave Chinner <david@fromorbit.com>
Mon, 29 Oct 2018 23:41:21 +0000 (10:41 +1100)
Combine the clone_file_range and dedupe_file_range operations into a
single remap_file_range file operation dispatch since they're
fundamentally the same operation.  The differences between the two can
be made in the prep functions.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <david@fromorbit.com>
12 files changed:
Documentation/filesystems/porting
Documentation/filesystems/vfs.txt
fs/btrfs/ctree.h
fs/btrfs/file.c
fs/btrfs/ioctl.c
fs/cifs/cifsfs.c
fs/nfs/nfs4file.c
fs/ocfs2/file.c
fs/overlayfs/file.c
fs/read_write.c
fs/xfs/xfs_file.c
include/linux/fs.h

index 7b7b845..e6d4466 100644 (file)
@@ -622,3 +622,8 @@ in your dentry operations instead.
        alloc_file_clone(file, flags, ops) does not affect any caller's references.
        On success you get a new struct file sharing the mount/dentry with the
        original, on failure - ERR_PTR().
+--
+[mandatory]
+       ->clone_file_range() and ->dedupe_file_range have been replaced with
+       ->remap_file_range().  See Documentation/filesystems/vfs.txt for more
+       information.
index a6c6a8a..6f5babf 100644 (file)
@@ -883,8 +883,9 @@ struct file_operations {
        unsigned (*mmap_capabilities)(struct file *);
 #endif
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int);
-       int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64);
-       int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t, u64);
+       int (*remap_file_range)(struct file *file_in, loff_t pos_in,
+                               struct file *file_out, loff_t pos_out,
+                               u64 len, unsigned int remap_flags);
        int (*fadvise)(struct file *, loff_t, loff_t, int);
 };
 
@@ -960,11 +961,16 @@ otherwise noted.
 
   copy_file_range: called by the copy_file_range(2) system call.
 
-  clone_file_range: called by the ioctl(2) system call for FICLONERANGE and
-       FICLONE commands.
-
-  dedupe_file_range: called by the ioctl(2) system call for FIDEDUPERANGE
-       command.
+  remap_file_range: called by the ioctl(2) system call for FICLONERANGE and
+       FICLONE and FIDEDUPERANGE commands to remap file ranges.  An
+       implementation should remap len bytes at pos_in of the source file into
+       the dest file at pos_out.  Implementations must handle callers passing
+       in len == 0; this means "remap to the end of the source file".  The
+       return value should be zero if all bytes were remapped, or the usual
+       negative error code if the remapping did not succeed completely.
+       The remap_flags parameter accepts REMAP_FILE_* flags.  If
+       REMAP_FILE_DEDUP is set then the implementation must only remap if the
+       requested file ranges have identical contents.
 
   fadvise: possibly called by the fadvise64() system call.
 
index 2cddfe7..124a056 100644 (file)
@@ -3218,9 +3218,6 @@ void btrfs_get_block_group_info(struct list_head *groups_list,
                                struct btrfs_ioctl_space_info *space);
 void btrfs_update_ioctl_balance_args(struct btrfs_fs_info *fs_info,
                               struct btrfs_ioctl_balance_args *bargs);
-int btrfs_dedupe_file_range(struct file *src_file, loff_t src_loff,
-                           struct file *dst_file, loff_t dst_loff,
-                           u64 olen);
 
 /* file.c */
 int __init btrfs_auto_defrag_init(void);
@@ -3250,8 +3247,9 @@ int btrfs_dirty_pages(struct inode *inode, struct page **pages,
                      size_t num_pages, loff_t pos, size_t write_bytes,
                      struct extent_state **cached);
 int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end);
-int btrfs_clone_file_range(struct file *file_in, loff_t pos_in,
-                          struct file *file_out, loff_t pos_out, u64 len);
+int btrfs_remap_file_range(struct file *file_in, loff_t pos_in,
+                          struct file *file_out, loff_t pos_out, u64 len,
+                          unsigned int remap_flags);
 
 /* tree-defrag.c */
 int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
index 2be00e8..9a963f0 100644 (file)
@@ -3269,8 +3269,7 @@ const struct file_operations btrfs_file_operations = {
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = btrfs_compat_ioctl,
 #endif
-       .clone_file_range = btrfs_clone_file_range,
-       .dedupe_file_range = btrfs_dedupe_file_range,
+       .remap_file_range = btrfs_remap_file_range,
 };
 
 void __cold btrfs_auto_defrag_exit(void)
index d60b6ca..bfd99c6 100644 (file)
@@ -3627,26 +3627,6 @@ out_unlock:
        return ret;
 }
 
-int btrfs_dedupe_file_range(struct file *src_file, loff_t src_loff,
-                           struct file *dst_file, loff_t dst_loff,
-                           u64 olen)
-{
-       struct inode *src = file_inode(src_file);
-       struct inode *dst = file_inode(dst_file);
-       u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
-
-       if (WARN_ON_ONCE(bs < PAGE_SIZE)) {
-               /*
-                * Btrfs does not support blocksize < page_size. As a
-                * result, btrfs_cmp_data() won't correctly handle
-                * this situation without an update.
-                */
-               return -EINVAL;
-       }
-
-       return btrfs_extent_same(src, src_loff, olen, dst, dst_loff);
-}
-
 static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
                                     struct inode *inode,
                                     u64 endoff,
@@ -4348,9 +4328,30 @@ out_unlock:
        return ret;
 }
 
-int btrfs_clone_file_range(struct file *src_file, loff_t off,
-               struct file *dst_file, loff_t destoff, u64 len)
+int btrfs_remap_file_range(struct file *src_file, loff_t off,
+               struct file *dst_file, loff_t destoff, u64 len,
+               unsigned int remap_flags)
 {
+       if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
+               return -EINVAL;
+
+       if (remap_flags & REMAP_FILE_DEDUP) {
+               struct inode *src = file_inode(src_file);
+               struct inode *dst = file_inode(dst_file);
+               u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize;
+
+               if (WARN_ON_ONCE(bs < PAGE_SIZE)) {
+                       /*
+                        * Btrfs does not support blocksize < page_size. As a
+                        * result, btrfs_cmp_data() won't correctly handle
+                        * this situation without an update.
+                        */
+                       return -EINVAL;
+               }
+
+               return btrfs_extent_same(src, off, len, dst, destoff);
+       }
+
        return btrfs_clone_files(dst_file, src_file, off, len, destoff);
 }
 
index 7065426..e8144d0 100644 (file)
@@ -975,8 +975,9 @@ const struct inode_operations cifs_symlink_inode_ops = {
        .listxattr = cifs_listxattr,
 };
 
-static int cifs_clone_file_range(struct file *src_file, loff_t off,
-               struct file *dst_file, loff_t destoff, u64 len)
+static int cifs_remap_file_range(struct file *src_file, loff_t off,
+               struct file *dst_file, loff_t destoff, u64 len,
+               unsigned int remap_flags)
 {
        struct inode *src_inode = file_inode(src_file);
        struct inode *target_inode = file_inode(dst_file);
@@ -986,6 +987,9 @@ static int cifs_clone_file_range(struct file *src_file, loff_t off,
        unsigned int xid;
        int rc;
 
+       if (remap_flags & ~REMAP_FILE_ADVISORY)
+               return -EINVAL;
+
        cifs_dbg(FYI, "clone range\n");
 
        xid = get_xid();
@@ -1134,7 +1138,7 @@ const struct file_operations cifs_file_ops = {
        .llseek = cifs_llseek,
        .unlocked_ioctl = cifs_ioctl,
        .copy_file_range = cifs_copy_file_range,
-       .clone_file_range = cifs_clone_file_range,
+       .remap_file_range = cifs_remap_file_range,
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
 };
@@ -1153,7 +1157,7 @@ const struct file_operations cifs_file_strict_ops = {
        .llseek = cifs_llseek,
        .unlocked_ioctl = cifs_ioctl,
        .copy_file_range = cifs_copy_file_range,
-       .clone_file_range = cifs_clone_file_range,
+       .remap_file_range = cifs_remap_file_range,
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
 };
@@ -1172,7 +1176,7 @@ const struct file_operations cifs_file_direct_ops = {
        .splice_write = iter_file_splice_write,
        .unlocked_ioctl  = cifs_ioctl,
        .copy_file_range = cifs_copy_file_range,
-       .clone_file_range = cifs_clone_file_range,
+       .remap_file_range = cifs_remap_file_range,
        .llseek = cifs_llseek,
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
@@ -1191,7 +1195,7 @@ const struct file_operations cifs_file_nobrl_ops = {
        .llseek = cifs_llseek,
        .unlocked_ioctl = cifs_ioctl,
        .copy_file_range = cifs_copy_file_range,
-       .clone_file_range = cifs_clone_file_range,
+       .remap_file_range = cifs_remap_file_range,
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
 };
@@ -1209,7 +1213,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
        .llseek = cifs_llseek,
        .unlocked_ioctl = cifs_ioctl,
        .copy_file_range = cifs_copy_file_range,
-       .clone_file_range = cifs_clone_file_range,
+       .remap_file_range = cifs_remap_file_range,
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
 };
@@ -1227,7 +1231,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
        .splice_write = iter_file_splice_write,
        .unlocked_ioctl  = cifs_ioctl,
        .copy_file_range = cifs_copy_file_range,
-       .clone_file_range = cifs_clone_file_range,
+       .remap_file_range = cifs_remap_file_range,
        .llseek = cifs_llseek,
        .setlease = cifs_setlease,
        .fallocate = cifs_fallocate,
@@ -1239,7 +1243,7 @@ const struct file_operations cifs_dir_ops = {
        .read    = generic_read_dir,
        .unlocked_ioctl  = cifs_ioctl,
        .copy_file_range = cifs_copy_file_range,
-       .clone_file_range = cifs_clone_file_range,
+       .remap_file_range = cifs_remap_file_range,
        .llseek = generic_file_llseek,
        .fsync = cifs_dir_fsync,
 };
index 4288a6e..ae5780c 100644 (file)
@@ -180,8 +180,9 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
        return nfs42_proc_allocate(filep, offset, len);
 }
 
-static int nfs42_clone_file_range(struct file *src_file, loff_t src_off,
-               struct file *dst_file, loff_t dst_off, u64 count)
+static int nfs42_remap_file_range(struct file *src_file, loff_t src_off,
+               struct file *dst_file, loff_t dst_off, u64 count,
+               unsigned int remap_flags)
 {
        struct inode *dst_inode = file_inode(dst_file);
        struct nfs_server *server = NFS_SERVER(dst_inode);
@@ -190,6 +191,9 @@ static int nfs42_clone_file_range(struct file *src_file, loff_t src_off,
        bool same_inode = false;
        int ret;
 
+       if (remap_flags & ~REMAP_FILE_ADVISORY)
+               return -EINVAL;
+
        /* check alignment w.r.t. clone_blksize */
        ret = -EINVAL;
        if (bs) {
@@ -262,7 +266,7 @@ const struct file_operations nfs4_file_operations = {
        .copy_file_range = nfs4_copy_file_range,
        .llseek         = nfs4_file_llseek,
        .fallocate      = nfs42_fallocate,
-       .clone_file_range = nfs42_clone_file_range,
+       .remap_file_range = nfs42_remap_file_range,
 #else
        .llseek         = nfs_file_llseek,
 #endif
index 9fa35cb..0b757a2 100644 (file)
@@ -2527,24 +2527,18 @@ out:
        return offset;
 }
 
-static int ocfs2_file_clone_range(struct file *file_in,
+static int ocfs2_remap_file_range(struct file *file_in,
                                  loff_t pos_in,
                                  struct file *file_out,
                                  loff_t pos_out,
-                                 u64 len)
+                                 u64 len,
+                                 unsigned int remap_flags)
 {
-       return ocfs2_reflink_remap_range(file_in, pos_in, file_out, pos_out,
-                                        len, false);
-}
+       if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
+               return -EINVAL;
 
-static int ocfs2_file_dedupe_range(struct file *file_in,
-                                  loff_t pos_in,
-                                  struct file *file_out,
-                                  loff_t pos_out,
-                                  u64 len)
-{
        return ocfs2_reflink_remap_range(file_in, pos_in, file_out, pos_out,
-                                         len, true);
+                                        len, remap_flags & REMAP_FILE_DEDUP);
 }
 
 const struct inode_operations ocfs2_file_iops = {
@@ -2586,8 +2580,7 @@ const struct file_operations ocfs2_fops = {
        .splice_read    = generic_file_splice_read,
        .splice_write   = iter_file_splice_write,
        .fallocate      = ocfs2_fallocate,
-       .clone_file_range = ocfs2_file_clone_range,
-       .dedupe_file_range = ocfs2_file_dedupe_range,
+       .remap_file_range = ocfs2_remap_file_range,
 };
 
 const struct file_operations ocfs2_dops = {
@@ -2633,8 +2626,7 @@ const struct file_operations ocfs2_fops_no_plocks = {
        .splice_read    = generic_file_splice_read,
        .splice_write   = iter_file_splice_write,
        .fallocate      = ocfs2_fallocate,
-       .clone_file_range = ocfs2_file_clone_range,
-       .dedupe_file_range = ocfs2_file_dedupe_range,
+       .remap_file_range = ocfs2_remap_file_range,
 };
 
 const struct file_operations ocfs2_dops_no_plocks = {
index 986313d..fffb36f 100644 (file)
@@ -489,26 +489,31 @@ static ssize_t ovl_copy_file_range(struct file *file_in, loff_t pos_in,
                            OVL_COPY);
 }
 
-static int ovl_clone_file_range(struct file *file_in, loff_t pos_in,
-                               struct file *file_out, loff_t pos_out, u64 len)
+static int ovl_remap_file_range(struct file *file_in, loff_t pos_in,
+                               struct file *file_out, loff_t pos_out,
+                               u64 len, unsigned int remap_flags)
 {
-       return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0,
-                           OVL_CLONE);
-}
+       enum ovl_copyop op;
+
+       if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
+               return -EINVAL;
+
+       if (remap_flags & REMAP_FILE_DEDUP)
+               op = OVL_DEDUPE;
+       else
+               op = OVL_CLONE;
 
-static int ovl_dedupe_file_range(struct file *file_in, loff_t pos_in,
-                                struct file *file_out, loff_t pos_out, u64 len)
-{
        /*
         * Don't copy up because of a dedupe request, this wouldn't make sense
         * most of the time (data would be duplicated instead of deduplicated).
         */
-       if (!ovl_inode_upper(file_inode(file_in)) ||
-           !ovl_inode_upper(file_inode(file_out)))
+       if (op == OVL_DEDUPE &&
+           (!ovl_inode_upper(file_inode(file_in)) ||
+            !ovl_inode_upper(file_inode(file_out))))
                return -EPERM;
 
        return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0,
-                           OVL_DEDUPE);
+                           op);
 }
 
 const struct file_operations ovl_file_operations = {
@@ -525,6 +530,5 @@ const struct file_operations ovl_file_operations = {
        .compat_ioctl   = ovl_compat_ioctl,
 
        .copy_file_range        = ovl_copy_file_range,
-       .clone_file_range       = ovl_clone_file_range,
-       .dedupe_file_range      = ovl_dedupe_file_range,
+       .remap_file_range       = ovl_remap_file_range,
 };
index 734c566..766bdcb 100644 (file)
@@ -1588,9 +1588,9 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
         * Try cloning first, this is supported by more file systems, and
         * more efficient if both clone and copy are supported (e.g. NFS).
         */
-       if (file_in->f_op->clone_file_range) {
-               ret = file_in->f_op->clone_file_range(file_in, pos_in,
-                               file_out, pos_out, len);
+       if (file_in->f_op->remap_file_range) {
+               ret = file_in->f_op->remap_file_range(file_in, pos_in,
+                               file_out, pos_out, len, 0);
                if (ret == 0) {
                        ret = len;
                        goto done;
@@ -1849,7 +1849,7 @@ int do_clone_file_range(struct file *file_in, loff_t pos_in,
            (file_out->f_flags & O_APPEND))
                return -EBADF;
 
-       if (!file_in->f_op->clone_file_range)
+       if (!file_in->f_op->remap_file_range)
                return -EOPNOTSUPP;
 
        ret = remap_verify_area(file_in, pos_in, len, false);
@@ -1860,8 +1860,8 @@ int do_clone_file_range(struct file *file_in, loff_t pos_in,
        if (ret)
                return ret;
 
-       ret = file_in->f_op->clone_file_range(file_in, pos_in,
-                       file_out, pos_out, len);
+       ret = file_in->f_op->remap_file_range(file_in, pos_in,
+                       file_out, pos_out, len, 0);
        if (!ret) {
                fsnotify_access(file_in);
                fsnotify_modify(file_out);
@@ -2006,7 +2006,7 @@ int vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
                goto out_drop_write;
 
        ret = -EINVAL;
-       if (!dst_file->f_op->dedupe_file_range)
+       if (!dst_file->f_op->remap_file_range)
                goto out_drop_write;
 
        if (len == 0) {
@@ -2014,8 +2014,8 @@ int vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
                goto out_drop_write;
        }
 
-       ret = dst_file->f_op->dedupe_file_range(src_file, src_pos,
-                                               dst_file, dst_pos, len);
+       ret = dst_file->f_op->remap_file_range(src_file, src_pos, dst_file,
+                       dst_pos, len, REMAP_FILE_DEDUP);
 out_drop_write:
        mnt_drop_write_file(dst_file);
 
index 61a5ad2..2ad94d5 100644 (file)
@@ -920,27 +920,19 @@ out_unlock:
 }
 
 STATIC int
-xfs_file_clone_range(
+xfs_file_remap_range(
        struct file     *file_in,
        loff_t          pos_in,
        struct file     *file_out,
        loff_t          pos_out,
-       u64             len)
+       u64             len,
+       unsigned int    remap_flags)
 {
-       return xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out,
-                                    len, false);
-}
+       if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
+               return -EINVAL;
 
-STATIC int
-xfs_file_dedupe_range(
-       struct file     *file_in,
-       loff_t          pos_in,
-       struct file     *file_out,
-       loff_t          pos_out,
-       u64             len)
-{
        return xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out,
-                                    len, true);
+                       len, remap_flags & REMAP_FILE_DEDUP);
 }
 
 STATIC int
@@ -1175,8 +1167,7 @@ const struct file_operations xfs_file_operations = {
        .fsync          = xfs_file_fsync,
        .get_unmapped_area = thp_get_unmapped_area,
        .fallocate      = xfs_file_fallocate,
-       .clone_file_range = xfs_file_clone_range,
-       .dedupe_file_range = xfs_file_dedupe_range,
+       .remap_file_range = xfs_file_remap_range,
 };
 
 const struct file_operations xfs_dir_file_operations = {
index 55729e1..888cef3 100644 (file)
@@ -1721,6 +1721,24 @@ struct block_device_operations;
 #define NOMMU_VMFLAGS \
        (NOMMU_MAP_READ | NOMMU_MAP_WRITE | NOMMU_MAP_EXEC)
 
+/*
+ * These flags control the behavior of the remap_file_range function pointer.
+ * If it is called with len == 0 that means "remap to end of source file".
+ * See Documentation/filesystems/vfs.txt for more details about this call.
+ *
+ * REMAP_FILE_DEDUP: only remap if contents identical (i.e. deduplicate)
+ */
+#define REMAP_FILE_DEDUP               (1 << 0)
+
+/*
+ * These flags signal that the caller is ok with altering various aspects of
+ * the behavior of the remap operation.  The changes must be made by the
+ * implementation; the vfs remap helper functions can take advantage of them.
+ * Flags in this category exist to preserve the quirky behavior of the hoisted
+ * btrfs clone/dedupe ioctls.
+ * There are no flags yet, but subsequent commits will add some.
+ */
+#define REMAP_FILE_ADVISORY            (0)
 
 struct iov_iter;
 
@@ -1759,10 +1777,9 @@ struct file_operations {
 #endif
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
                        loff_t, size_t, unsigned int);
-       int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
-                       u64);
-       int (*dedupe_file_range)(struct file *, loff_t, struct file *, loff_t,
-                       u64);
+       int (*remap_file_range)(struct file *file_in, loff_t pos_in,
+                               struct file *file_out, loff_t pos_out,
+                               u64 len, unsigned int remap_flags);
        int (*fadvise)(struct file *, loff_t, loff_t, int);
 } __randomize_layout;