Merge tag 'staging-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[linux-2.6-microblaze.git] / fs / ocfs2 / file.c
index 9fa35cb..d640c5f 100644 (file)
@@ -2343,7 +2343,7 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb,
 
        written = __generic_file_write_iter(iocb, from);
        /* buffered aio wouldn't have proper lock coverage today */
-       BUG_ON(written == -EIOCBQUEUED && !(iocb->ki_flags & IOCB_DIRECT));
+       BUG_ON(written == -EIOCBQUEUED && !direct_io);
 
        /*
         * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io
@@ -2463,7 +2463,7 @@ static ssize_t ocfs2_file_read_iter(struct kiocb *iocb,
        trace_generic_file_read_iter_ret(ret);
 
        /* buffered aio wouldn't have proper lock coverage today */
-       BUG_ON(ret == -EIOCBQUEUED && !(iocb->ki_flags & IOCB_DIRECT));
+       BUG_ON(ret == -EIOCBQUEUED && !direct_io);
 
        /* see ocfs2_file_write_iter */
        if (ret == -EIOCBQUEUED || !ocfs2_iocb_is_rw_locked(iocb)) {
@@ -2527,24 +2527,79 @@ out:
        return offset;
 }
 
-static int ocfs2_file_clone_range(struct file *file_in,
-                                 loff_t pos_in,
-                                 struct file *file_out,
-                                 loff_t pos_out,
-                                 u64 len)
+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)
 {
-       return ocfs2_reflink_remap_range(file_in, pos_in, file_out, pos_out,
-                                        len, false);
-}
+       struct inode *inode_in = file_inode(file_in);
+       struct inode *inode_out = file_inode(file_out);
+       struct ocfs2_super *osb = OCFS2_SB(inode_in->i_sb);
+       struct buffer_head *in_bh = NULL, *out_bh = NULL;
+       bool same_inode = (inode_in == inode_out);
+       loff_t remapped = 0;
+       ssize_t ret;
 
-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);
+       if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
+               return -EINVAL;
+       if (!ocfs2_refcount_tree(osb))
+               return -EOPNOTSUPP;
+       if (ocfs2_is_hard_readonly(osb) || ocfs2_is_soft_readonly(osb))
+               return -EROFS;
+
+       /* Lock both files against IO */
+       ret = ocfs2_reflink_inodes_lock(inode_in, &in_bh, inode_out, &out_bh);
+       if (ret)
+               return ret;
+
+       /* Check file eligibility and prepare for block sharing. */
+       ret = -EINVAL;
+       if ((OCFS2_I(inode_in)->ip_flags & OCFS2_INODE_SYSTEM_FILE) ||
+           (OCFS2_I(inode_out)->ip_flags & OCFS2_INODE_SYSTEM_FILE))
+               goto out_unlock;
+
+       ret = generic_remap_file_range_prep(file_in, pos_in, file_out, pos_out,
+                       &len, remap_flags);
+       if (ret < 0 || len == 0)
+               goto out_unlock;
+
+       /* Lock out changes to the allocation maps and remap. */
+       down_write(&OCFS2_I(inode_in)->ip_alloc_sem);
+       if (!same_inode)
+               down_write_nested(&OCFS2_I(inode_out)->ip_alloc_sem,
+                                 SINGLE_DEPTH_NESTING);
+
+       /* Zap any page cache for the destination file's range. */
+       truncate_inode_pages_range(&inode_out->i_data,
+                                  round_down(pos_out, PAGE_SIZE),
+                                  round_up(pos_out + len, PAGE_SIZE) - 1);
+
+       remapped = ocfs2_reflink_remap_blocks(inode_in, in_bh, pos_in,
+                       inode_out, out_bh, pos_out, len);
+       up_write(&OCFS2_I(inode_in)->ip_alloc_sem);
+       if (!same_inode)
+               up_write(&OCFS2_I(inode_out)->ip_alloc_sem);
+       if (remapped < 0) {
+               ret = remapped;
+               mlog_errno(ret);
+               goto out_unlock;
+       }
+
+       /*
+        * Empty the extent map so that we may get the right extent
+        * record from the disk.
+        */
+       ocfs2_extent_map_trunc(inode_in, 0);
+       ocfs2_extent_map_trunc(inode_out, 0);
+
+       ret = ocfs2_reflink_update_dest(inode_out, out_bh, pos_out + len);
+       if (ret) {
+               mlog_errno(ret);
+               goto out_unlock;
+       }
+
+out_unlock:
+       ocfs2_reflink_inodes_unlock(inode_in, in_bh, inode_out, out_bh);
+       return remapped > 0 ? remapped : ret;
 }
 
 const struct inode_operations ocfs2_file_iops = {
@@ -2586,8 +2641,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 +2687,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 = {