From: Darrick J. Wong <darrick.w...@oracle.com>

Plumb in a remap flag that enables the filesystem remap handler to
shorten remapping requests for callers that can handle it.  Now
copy_file_range can report partial success (in case we run up against
alignment problems, resource limits, etc.).

Signed-off-by: Darrick J. Wong <darrick.w...@oracle.com>
---
 fs/read_write.c    |    3 ++-
 include/linux/fs.h |    2 ++
 mm/filemap.c       |   16 ++++++++++++----
 3 files changed, 16 insertions(+), 5 deletions(-)


diff --git a/fs/read_write.c b/fs/read_write.c
index a628fd9a47cf..8ed0aed81649 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1593,7 +1593,8 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t 
pos_in,
 
                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);
+                               min_t(loff_t, MAX_RW_COUNT, len),
+                               RFR_CAN_SHORTEN);
                if (cloned >= 0) {
                        ret = cloned;
                        goto done;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 9f90dcd4df3b..e0494d719ebc 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1726,9 +1726,11 @@ struct block_device_operations;
  *
  * RFR_IDENTICAL_DATA: only remap if contents identical (i.e. deduplicate)
  * RFR_TO_SRC_EOF: remap to the end of the source file
+ * RFR_CAN_SHORTEN: caller can handle a shortened request
  */
 #define RFR_IDENTICAL_DATA     (1 << 0)
 #define RFR_TO_SRC_EOF         (1 << 1)
+#define RFR_CAN_SHORTEN                (1 << 2)
 
 struct iov_iter;
 
diff --git a/mm/filemap.c b/mm/filemap.c
index 2522737483de..2179d0204ee6 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -3064,8 +3064,12 @@ int generic_remap_checks(struct file *file_in, loff_t 
pos_in,
        if (pos_in + count == size_in) {
                bcount = ALIGN(size_in, bs) - pos_in;
        } else {
-               if (!IS_ALIGNED(count, bs))
-                       return -EINVAL;
+               if (!IS_ALIGNED(count, bs)) {
+                       if (remap_flags & RFR_CAN_SHORTEN)
+                               count = ALIGN_DOWN(count, bs);
+                       else
+                               return -EINVAL;
+               }
 
                bcount = count;
        }
@@ -3076,10 +3080,14 @@ int generic_remap_checks(struct file *file_in, loff_t 
pos_in,
            pos_out < pos_in + bcount)
                return -EINVAL;
 
-       /* For now we don't support changing the length. */
-       if (*req_count != count)
+       /*
+        * We shortened the request but the caller can't deal with that, so
+        * bounce the request back to userspace.
+        */
+       if (*req_count != count && !(remap_flags & RFR_CAN_SHORTEN))
                return -EINVAL;
 
+       *req_count = count;
        return 0;
 }
 

Reply via email to