]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
NFSv42: Copy offload should update the file size when appropriate
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Wed, 14 Apr 2021 14:10:09 +0000 (10:10 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Wed, 14 Apr 2021 14:41:57 +0000 (10:41 -0400)
If the result of a copy offload or clone operation is to grow the
destination file size, then we should update it. The reason is that when
a client holds a delegation, it is authoritative for the file size.

Fixes: 16abd2a0c124 ("NFSv4.2: fix client's attribute cache management for copy_file_range")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/nfs42proc.c

index 8d64eb953347c9f41478d74c9c72faa7f78ed700..3875120ef3ef0adba8b33355b51abef346efef5c 100644 (file)
@@ -269,6 +269,33 @@ out:
        return status;
 }
 
+/**
+ * nfs42_copy_dest_done - perform inode cache updates after clone/copy offload
+ * @inode: pointer to destination inode
+ * @pos: destination offset
+ * @len: copy length
+ *
+ * Punch a hole in the inode page cache, so that the NFS client will
+ * know to retrieve new data.
+ * Update the file size if necessary, and then mark the inode as having
+ * invalid cached values for change attribute, ctime, mtime and space used.
+ */
+static void nfs42_copy_dest_done(struct inode *inode, loff_t pos, loff_t len)
+{
+       loff_t newsize = pos + len;
+       loff_t end = newsize - 1;
+
+       truncate_pagecache_range(inode, pos, end);
+       spin_lock(&inode->i_lock);
+       if (newsize > i_size_read(inode))
+               i_size_write(inode, newsize);
+       nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
+                                            NFS_INO_INVALID_CTIME |
+                                            NFS_INO_INVALID_MTIME |
+                                            NFS_INO_INVALID_BLOCKS);
+       spin_unlock(&inode->i_lock);
+}
+
 static ssize_t _nfs42_proc_copy(struct file *src,
                                struct nfs_lock_context *src_lock,
                                struct file *dst,
@@ -362,14 +389,8 @@ static ssize_t _nfs42_proc_copy(struct file *src,
                        goto out;
        }
 
-       truncate_pagecache_range(dst_inode, pos_dst,
-                                pos_dst + res->write_res.count);
-       spin_lock(&dst_inode->i_lock);
-       nfs_set_cache_invalid(
-               dst_inode, NFS_INO_REVAL_PAGECACHE | NFS_INO_REVAL_FORCED |
-                                  NFS_INO_INVALID_SIZE | NFS_INO_INVALID_ATTR |
-                                  NFS_INO_INVALID_DATA);
-       spin_unlock(&dst_inode->i_lock);
+       nfs42_copy_dest_done(dst_inode, pos_dst, res->write_res.count);
+
        spin_lock(&src_inode->i_lock);
        nfs_set_cache_invalid(src_inode, NFS_INO_REVAL_PAGECACHE |
                                                 NFS_INO_REVAL_FORCED |
@@ -1055,8 +1076,10 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
 
        status = nfs4_call_sync(server->client, server, msg,
                                &args.seq_args, &res.seq_res, 0);
-       if (status == 0)
+       if (status == 0) {
+               nfs42_copy_dest_done(dst_inode, dst_offset, count);
                status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
+       }
 
        kfree(res.dst_fattr);
        return status;