OSDN Git Service

vfs: make remap_file_range functions take and return bytes completed
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 29 Oct 2018 23:41:49 +0000 (10:41 +1100)
committerDave Chinner <david@fromorbit.com>
Mon, 29 Oct 2018 23:41:49 +0000 (10:41 +1100)
Change the remap_file_range functions to take a number of bytes to
operate upon and return the number of bytes they operated on.  This is a
requirement for allowing fs implementations to return short clone/dedupe
results to the user, which will enable us to obey resource limits in a
graceful manner.

A subsequent patch will enable copy_file_range to signal to the
->clone_file_range implementation that it can handle a short length,
which will be returned in the function's return value.  For now the
short return is not implemented anywhere so the behavior won't change --
either copy_file_range manages to clone the entire range or it tries an
alternative.

Neither clone ioctl can take advantage of this, alas.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
18 files changed:
Documentation/filesystems/vfs.txt
fs/btrfs/ctree.h
fs/btrfs/ioctl.c
fs/cifs/cifsfs.c
fs/ioctl.c
fs/nfs/nfs4file.c
fs/nfsd/vfs.c
fs/ocfs2/file.c
fs/ocfs2/refcounttree.c
fs/ocfs2/refcounttree.h
fs/overlayfs/copy_up.c
fs/overlayfs/file.c
fs/read_write.c
fs/xfs/xfs_file.c
fs/xfs/xfs_reflink.c
fs/xfs/xfs_reflink.h
include/linux/fs.h
mm/filemap.c

index 6f5babf..1bd2919 100644 (file)
@@ -883,9 +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 (*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);
+       loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
+                                  struct file *file_out, loff_t pos_out,
+                                  loff_t len, unsigned int remap_flags);
        int (*fadvise)(struct file *, loff_t, loff_t, int);
 };
 
@@ -966,8 +966,8 @@ otherwise noted.
        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.
+       return value should the number of bytes remapped, or the usual
+       negative error code if errors occurred before any bytes were remapped.
        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.
index 124a056..771a961 100644 (file)
@@ -3247,9 +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_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);
+loff_t btrfs_remap_file_range(struct file *file_in, loff_t pos_in,
+                             struct file *file_out, loff_t pos_out,
+                             loff_t len, unsigned int remap_flags);
 
 /* tree-defrag.c */
 int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
index bfd99c6..b0c513e 100644 (file)
@@ -4328,10 +4328,12 @@ out_unlock:
        return ret;
 }
 
-int btrfs_remap_file_range(struct file *src_file, loff_t off,
-               struct file *dst_file, loff_t destoff, u64 len,
+loff_t btrfs_remap_file_range(struct file *src_file, loff_t off,
+               struct file *dst_file, loff_t destoff, loff_t len,
                unsigned int remap_flags)
 {
+       int ret;
+
        if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
                return -EINVAL;
 
@@ -4349,10 +4351,11 @@ int btrfs_remap_file_range(struct file *src_file, loff_t off,
                        return -EINVAL;
                }
 
-               return btrfs_extent_same(src, off, len, dst, destoff);
+               ret = btrfs_extent_same(src, off, len, dst, destoff);
+       } else {
+               ret = btrfs_clone_files(dst_file, src_file, off, len, destoff);
        }
-
-       return btrfs_clone_files(dst_file, src_file, off, len, destoff);
+       return ret < 0 ? ret : len;
 }
 
 static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)
index e8144d0..5ca71c6 100644 (file)
@@ -975,8 +975,8 @@ const struct inode_operations cifs_symlink_inode_ops = {
        .listxattr = cifs_listxattr,
 };
 
-static int cifs_remap_file_range(struct file *src_file, loff_t off,
-               struct file *dst_file, loff_t destoff, u64 len,
+static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
+               struct file *dst_file, loff_t destoff, loff_t len,
                unsigned int remap_flags)
 {
        struct inode *src_inode = file_inode(src_file);
@@ -1029,7 +1029,7 @@ static int cifs_remap_file_range(struct file *src_file, loff_t off,
        unlock_two_nondirectories(src_inode, target_inode);
 out:
        free_xid(xid);
-       return rc;
+       return rc < 0 ? rc : len;
 }
 
 ssize_t cifs_file_copychunk_range(unsigned int xid,
index 2005529..72537b6 100644 (file)
@@ -223,6 +223,7 @@ static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
                             u64 off, u64 olen, u64 destoff)
 {
        struct fd src_file = fdget(srcfd);
+       loff_t cloned;
        int ret;
 
        if (!src_file.file)
@@ -230,7 +231,14 @@ static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
        ret = -EXDEV;
        if (src_file.file->f_path.mnt != dst_file->f_path.mnt)
                goto fdput;
-       ret = vfs_clone_file_range(src_file.file, off, dst_file, destoff, olen);
+       cloned = vfs_clone_file_range(src_file.file, off, dst_file, destoff,
+                                     olen);
+       if (cloned < 0)
+               ret = cloned;
+       else if (olen && cloned != olen)
+               ret = -EINVAL;
+       else
+               ret = 0;
 fdput:
        fdput(src_file);
        return ret;
index ae5780c..46d691b 100644 (file)
@@ -180,8 +180,8 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t
        return nfs42_proc_allocate(filep, offset, len);
 }
 
-static int nfs42_remap_file_range(struct file *src_file, loff_t src_off,
-               struct file *dst_file, loff_t dst_off, u64 count,
+static loff_t nfs42_remap_file_range(struct file *src_file, loff_t src_off,
+               struct file *dst_file, loff_t dst_off, loff_t count,
                unsigned int remap_flags)
 {
        struct inode *dst_inode = file_inode(dst_file);
@@ -244,7 +244,7 @@ out_unlock:
                inode_unlock(src_inode);
        }
 out:
-       return ret;
+       return ret < 0 ? ret : count;
 }
 #endif /* CONFIG_NFS_V4_2 */
 
index b53e763..ac6cb61 100644 (file)
@@ -541,8 +541,12 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
 __be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
                u64 dst_pos, u64 count)
 {
-       return nfserrno(vfs_clone_file_range(src, src_pos, dst, dst_pos,
-                                            count));
+       loff_t cloned;
+
+       cloned = vfs_clone_file_range(src, src_pos, dst, dst_pos, count);
+       if (count && cloned != count)
+               cloned = -EINVAL;
+       return nfserrno(cloned < 0 ? cloned : 0);
 }
 
 ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
index 9809b0e..fbaeafe 100644 (file)
@@ -2527,18 +2527,18 @@ out:
        return offset;
 }
 
-static int ocfs2_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)
+static loff_t ocfs2_remap_file_range(struct file *file_in, loff_t pos_in,
+                                    struct file *file_out, loff_t pos_out,
+                                    loff_t len, unsigned int remap_flags)
 {
+       int ret;
+
        if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
                return -EINVAL;
 
-       return ocfs2_reflink_remap_range(file_in, pos_in, file_out, pos_out,
-                                        len, remap_flags);
+       ret = ocfs2_reflink_remap_range(file_in, pos_in, file_out, pos_out,
+                                       len, remap_flags);
+       return ret < 0 ? ret : len;
 }
 
 const struct inode_operations ocfs2_file_iops = {
index df97815..6a42c04 100644 (file)
@@ -4824,7 +4824,7 @@ int ocfs2_reflink_remap_range(struct file *file_in,
                              loff_t pos_in,
                              struct file *file_out,
                              loff_t pos_out,
-                             u64 len,
+                             loff_t len,
                              unsigned int remap_flags)
 {
        struct inode *inode_in = file_inode(file_in);
index d2c5f52..eb65c1d 100644 (file)
@@ -119,7 +119,7 @@ int ocfs2_reflink_remap_range(struct file *file_in,
                              loff_t pos_in,
                              struct file *file_out,
                              loff_t pos_out,
-                             u64 len,
+                             loff_t len,
                              unsigned int remap_flags);
 
 #endif /* OCFS2_REFCOUNTTREE_H */
index 1cc797a..8750b72 100644 (file)
@@ -125,6 +125,7 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
        struct file *new_file;
        loff_t old_pos = 0;
        loff_t new_pos = 0;
+       loff_t cloned;
        int error = 0;
 
        if (len == 0)
@@ -141,11 +142,10 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
        }
 
        /* Try to use clone_file_range to clone up within the same fs */
-       error = do_clone_file_range(old_file, 0, new_file, 0, len);
-       if (!error)
+       cloned = do_clone_file_range(old_file, 0, new_file, 0, len);
+       if (cloned == len)
                goto out;
        /* Couldn't clone, so now we try to copy the data */
-       error = 0;
 
        /* FIXME: copy up sparse files efficiently */
        while (len) {
index fffb36f..6c3fec6 100644 (file)
@@ -434,14 +434,14 @@ enum ovl_copyop {
        OVL_DEDUPE,
 };
 
-static ssize_t ovl_copyfile(struct file *file_in, loff_t pos_in,
+static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
                            struct file *file_out, loff_t pos_out,
-                           u64 len, unsigned int flags, enum ovl_copyop op)
+                           loff_t len, unsigned int flags, enum ovl_copyop op)
 {
        struct inode *inode_out = file_inode(file_out);
        struct fd real_in, real_out;
        const struct cred *old_cred;
-       ssize_t ret;
+       loff_t ret;
 
        ret = ovl_real_fdget(file_out, &real_out);
        if (ret)
@@ -489,9 +489,9 @@ static ssize_t ovl_copy_file_range(struct file *file_in, loff_t pos_in,
                            OVL_COPY);
 }
 
-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)
+static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in,
+                                  struct file *file_out, loff_t pos_out,
+                                  loff_t len, unsigned int remap_flags)
 {
        enum ovl_copyop op;
 
index b61bd3f..356641a 100644 (file)
@@ -1589,10 +1589,13 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
         * more efficient if both clone and copy are supported (e.g. NFS).
         */
        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;
+               loff_t cloned;
+
+               cloned = file_in->f_op->remap_file_range(file_in, pos_in,
+                               file_out, pos_out,
+                               min_t(loff_t, MAX_RW_COUNT, len), 0);
+               if (cloned > 0) {
+                       ret = cloned;
                        goto done;
                }
        }
@@ -1686,11 +1689,12 @@ out2:
        return ret;
 }
 
-static int remap_verify_area(struct file *file, loff_t pos, u64 len, bool write)
+static int remap_verify_area(struct file *file, loff_t pos, loff_t len,
+                            bool write)
 {
        struct inode *inode = file_inode(file);
 
-       if (unlikely(pos < 0))
+       if (unlikely(pos < 0 || len < 0))
                return -EINVAL;
 
         if (unlikely((loff_t) (pos + len) < 0))
@@ -1721,7 +1725,7 @@ static int remap_verify_area(struct file *file, loff_t pos, u64 len, bool write)
 static int generic_remap_check_len(struct inode *inode_in,
                                   struct inode *inode_out,
                                   loff_t pos_out,
-                                  u64 *len,
+                                  loff_t *len,
                                   unsigned int remap_flags)
 {
        u64 blkmask = i_blocksize(inode_in) - 1;
@@ -1747,7 +1751,7 @@ static int generic_remap_check_len(struct inode *inode_in,
  */
 int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
                                  struct file *file_out, loff_t pos_out,
-                                 u64 *len, unsigned int remap_flags)
+                                 loff_t *len, unsigned int remap_flags)
 {
        struct inode *inode_in = file_inode(file_in);
        struct inode *inode_out = file_inode(file_out);
@@ -1843,12 +1847,12 @@ int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
 }
 EXPORT_SYMBOL(generic_remap_file_range_prep);
 
-int do_clone_file_range(struct file *file_in, loff_t pos_in,
-                       struct file *file_out, loff_t pos_out, u64 len)
+loff_t do_clone_file_range(struct file *file_in, loff_t pos_in,
+                          struct file *file_out, loff_t pos_out, loff_t len)
 {
        struct inode *inode_in = file_inode(file_in);
        struct inode *inode_out = file_inode(file_out);
-       int ret;
+       loff_t ret;
 
        if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
                return -EISDIR;
@@ -1881,19 +1885,19 @@ int do_clone_file_range(struct file *file_in, loff_t pos_in,
 
        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);
-       }
+       if (ret < 0)
+               return ret;
 
+       fsnotify_access(file_in);
+       fsnotify_modify(file_out);
        return ret;
 }
 EXPORT_SYMBOL(do_clone_file_range);
 
-int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
-                        struct file *file_out, loff_t pos_out, u64 len)
+loff_t vfs_clone_file_range(struct file *file_in, loff_t pos_in,
+                           struct file *file_out, loff_t pos_out, loff_t len)
 {
-       int ret;
+       loff_t ret;
 
        file_start_write(file_out);
        ret = do_clone_file_range(file_in, pos_in, file_out, pos_out, len);
@@ -1999,10 +2003,11 @@ out_error:
 }
 EXPORT_SYMBOL(vfs_dedupe_file_range_compare);
 
-int vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
-                             struct file *dst_file, loff_t dst_pos, u64 len)
+loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
+                                struct file *dst_file, loff_t dst_pos,
+                                loff_t len)
 {
-       s64 ret;
+       loff_t ret;
 
        ret = mnt_want_write_file(dst_file);
        if (ret)
@@ -2051,7 +2056,7 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same)
        int i;
        int ret;
        u16 count = same->dest_count;
-       int deduped;
+       loff_t deduped;
 
        if (!(file->f_mode & FMODE_READ))
                return -EINVAL;
index 20314eb..38fde4e 100644 (file)
@@ -919,20 +919,23 @@ out_unlock:
        return error;
 }
 
-STATIC int
+STATIC loff_t
 xfs_file_remap_range(
        struct file     *file_in,
        loff_t          pos_in,
        struct file     *file_out,
        loff_t          pos_out,
-       u64             len,
+       loff_t          len,
        unsigned int    remap_flags)
 {
+       int             ret;
+
        if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
                return -EINVAL;
 
-       return xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out,
+       ret = xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out,
                        len, remap_flags);
+       return ret < 0 ? ret : len;
 }
 
 STATIC int
index 2d7dd8b..3dbe5fb 100644 (file)
@@ -1296,7 +1296,7 @@ xfs_reflink_remap_prep(
        loff_t                  pos_in,
        struct file             *file_out,
        loff_t                  pos_out,
-       u64                     *len,
+       loff_t                  *len,
        unsigned int            remap_flags)
 {
        struct inode            *inode_in = file_inode(file_in);
@@ -1387,7 +1387,7 @@ xfs_reflink_remap_range(
        loff_t                  pos_in,
        struct file             *file_out,
        loff_t                  pos_out,
-       u64                     len,
+       loff_t                  len,
        unsigned int            remap_flags)
 {
        struct inode            *inode_in = file_inode(file_in);
index 6f82d62..c3c46c2 100644 (file)
@@ -28,7 +28,7 @@ extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset,
                xfs_off_t count);
 extern int xfs_reflink_recover_cow(struct xfs_mount *mp);
 extern int xfs_reflink_remap_range(struct file *file_in, loff_t pos_in,
-               struct file *file_out, loff_t pos_out, u64 len,
+               struct file *file_out, loff_t pos_out, loff_t len,
                unsigned int remap_flags);
 extern int xfs_reflink_inode_has_shared_extents(struct xfs_trans *tp,
                struct xfs_inode *ip, bool *has_shared);
index c5435ca..c72d8c3 100644 (file)
@@ -1777,9 +1777,9 @@ struct file_operations {
 #endif
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
                        loff_t, size_t, unsigned int);
-       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);
+       loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
+                                  struct file *file_out, loff_t pos_out,
+                                  loff_t len, unsigned int remap_flags);
        int (*fadvise)(struct file *, loff_t, loff_t, int);
 } __randomize_layout;
 
@@ -1844,19 +1844,22 @@ extern ssize_t vfs_copy_file_range(struct file *, loff_t , struct file *,
                                   loff_t, size_t, unsigned int);
 extern int generic_remap_file_range_prep(struct file *file_in, loff_t pos_in,
                                         struct file *file_out, loff_t pos_out,
-                                        u64 *count, unsigned int remap_flags);
-extern int do_clone_file_range(struct file *file_in, loff_t pos_in,
-                              struct file *file_out, loff_t pos_out, u64 len);
-extern int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
-                               struct file *file_out, loff_t pos_out, u64 len);
+                                        loff_t *count,
+                                        unsigned int remap_flags);
+extern loff_t do_clone_file_range(struct file *file_in, loff_t pos_in,
+                                 struct file *file_out, loff_t pos_out,
+                                 loff_t len);
+extern loff_t vfs_clone_file_range(struct file *file_in, loff_t pos_in,
+                                  struct file *file_out, loff_t pos_out,
+                                  loff_t len);
 extern int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
                                         struct inode *dest, loff_t destoff,
                                         loff_t len, bool *is_same);
 extern int vfs_dedupe_file_range(struct file *file,
                                 struct file_dedupe_range *same);
-extern int vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
-                                    struct file *dst_file, loff_t dst_pos,
-                                    u64 len);
+extern loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
+                                       struct file *dst_file, loff_t dst_pos,
+                                       loff_t len);
 
 
 struct super_operations {
@@ -2986,7 +2989,7 @@ extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
 extern ssize_t generic_write_checks(struct kiocb *, struct iov_iter *);
 extern int generic_remap_checks(struct file *file_in, loff_t pos_in,
                                struct file *file_out, loff_t pos_out,
-                               uint64_t *count, unsigned int remap_flags);
+                               loff_t *count, unsigned int remap_flags);
 extern ssize_t generic_file_read_iter(struct kiocb *, struct iov_iter *);
 extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *);
 extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *);
index 410dc58..e9091d7 100644 (file)
@@ -2994,7 +2994,7 @@ EXPORT_SYMBOL(generic_write_checks);
  */
 int generic_remap_checks(struct file *file_in, loff_t pos_in,
                         struct file *file_out, loff_t pos_out,
-                        uint64_t *req_count, unsigned int remap_flags)
+                        loff_t *req_count, unsigned int remap_flags)
 {
        struct inode *inode_in = file_in->f_mapping->host;
        struct inode *inode_out = file_out->f_mapping->host;