mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	vfs: allow copy_file_range to copy across devices
We want to enable cross-filesystem copy_file_range functionality where possible, so push the "same superblock only" checks down to the individual filesystem callouts so they can make their own decisions about cross-superblock copy offload and fallack to generic_copy_file_range() for cross-superblock copy. [Amir] We do not call ->remap_file_range() in case the files are not on the same sb and do not call ->copy_file_range() in case the files do not belong to the same filesystem driver. This changes behavior of the copy_file_range(2) syscall, which will now allow cross filesystem in-kernel copy. CIFS already supports cross-superblock copy, between two shares to the same server. This functionality will now be available via the copy_file_range(2) syscall. Cc: Steve French <stfrench@microsoft.com> Signed-off-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
		
							parent
							
								
									8c3f406c09
								
							
						
					
					
						commit
						5dae222a5f
					
				
					 5 changed files with 24 additions and 10 deletions
				
			
		| 
						 | 
					@ -1909,6 +1909,8 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (src_inode == dst_inode)
 | 
						if (src_inode == dst_inode)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						if (src_inode->i_sb != dst_inode->i_sb)
 | 
				
			||||||
 | 
							return -EXDEV;
 | 
				
			||||||
	if (ceph_snap(dst_inode) != CEPH_NOSNAP)
 | 
						if (ceph_snap(dst_inode) != CEPH_NOSNAP)
 | 
				
			||||||
		return -EROFS;
 | 
							return -EROFS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2109,7 +2111,7 @@ static ssize_t ceph_copy_file_range(struct file *src_file, loff_t src_off,
 | 
				
			||||||
	ret = __ceph_copy_file_range(src_file, src_off, dst_file, dst_off,
 | 
						ret = __ceph_copy_file_range(src_file, src_off, dst_file, dst_off,
 | 
				
			||||||
				     len, flags);
 | 
									     len, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ret == -EOPNOTSUPP)
 | 
						if (ret == -EOPNOTSUPP || ret == -EXDEV)
 | 
				
			||||||
		ret = generic_copy_file_range(src_file, src_off, dst_file,
 | 
							ret = generic_copy_file_range(src_file, src_off, dst_file,
 | 
				
			||||||
					      dst_off, len, flags);
 | 
										      dst_off, len, flags);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1149,7 +1149,7 @@ static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off,
 | 
				
			||||||
					len, flags);
 | 
										len, flags);
 | 
				
			||||||
	free_xid(xid);
 | 
						free_xid(xid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rc == -EOPNOTSUPP)
 | 
						if (rc == -EOPNOTSUPP || rc == -EXDEV)
 | 
				
			||||||
		rc = generic_copy_file_range(src_file, off, dst_file,
 | 
							rc = generic_copy_file_range(src_file, off, dst_file,
 | 
				
			||||||
					     destoff, len, flags);
 | 
										     destoff, len, flags);
 | 
				
			||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3142,6 +3142,9 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
 | 
				
			||||||
	if (fc->no_copy_file_range)
 | 
						if (fc->no_copy_file_range)
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb)
 | 
				
			||||||
 | 
							return -EXDEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fc->writeback_cache) {
 | 
						if (fc->writeback_cache) {
 | 
				
			||||||
		inode_lock(inode_in);
 | 
							inode_lock(inode_in);
 | 
				
			||||||
		err = fuse_writeback_range(inode_in, pos_in, pos_in + len);
 | 
							err = fuse_writeback_range(inode_in, pos_in, pos_in + len);
 | 
				
			||||||
| 
						 | 
					@ -3203,7 +3206,7 @@ static ssize_t fuse_copy_file_range(struct file *src_file, loff_t src_off,
 | 
				
			||||||
	ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off,
 | 
						ret = __fuse_copy_file_range(src_file, src_off, dst_file, dst_off,
 | 
				
			||||||
				     len, flags);
 | 
									     len, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ret == -EOPNOTSUPP)
 | 
						if (ret == -EOPNOTSUPP || ret == -EXDEV)
 | 
				
			||||||
		ret = generic_copy_file_range(src_file, src_off, dst_file,
 | 
							ret = generic_copy_file_range(src_file, src_off, dst_file,
 | 
				
			||||||
					      dst_off, len, flags);
 | 
										      dst_off, len, flags);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -133,6 +133,9 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
 | 
				
			||||||
				      struct file *file_out, loff_t pos_out,
 | 
									      struct file *file_out, loff_t pos_out,
 | 
				
			||||||
				      size_t count, unsigned int flags)
 | 
									      size_t count, unsigned int flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						/* Only offload copy if superblock is the same */
 | 
				
			||||||
 | 
						if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb)
 | 
				
			||||||
 | 
							return -EXDEV;
 | 
				
			||||||
	if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY))
 | 
						if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY))
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
	if (file_inode(file_in) == file_inode(file_out))
 | 
						if (file_inode(file_in) == file_inode(file_out))
 | 
				
			||||||
| 
						 | 
					@ -148,7 +151,7 @@ static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = __nfs4_copy_file_range(file_in, pos_in, file_out, pos_out, count,
 | 
						ret = __nfs4_copy_file_range(file_in, pos_in, file_out, pos_out, count,
 | 
				
			||||||
				     flags);
 | 
									     flags);
 | 
				
			||||||
	if (ret == -EOPNOTSUPP)
 | 
						if (ret == -EOPNOTSUPP || ret == -EXDEV)
 | 
				
			||||||
		ret = generic_copy_file_range(file_in, pos_in, file_out,
 | 
							ret = generic_copy_file_range(file_in, pos_in, file_out,
 | 
				
			||||||
					      pos_out, count, flags);
 | 
										      pos_out, count, flags);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1599,7 +1599,16 @@ static ssize_t do_copy_file_range(struct file *file_in, loff_t pos_in,
 | 
				
			||||||
				  struct file *file_out, loff_t pos_out,
 | 
									  struct file *file_out, loff_t pos_out,
 | 
				
			||||||
				  size_t len, unsigned int flags)
 | 
									  size_t len, unsigned int flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (file_out->f_op->copy_file_range)
 | 
						/*
 | 
				
			||||||
 | 
						 * Although we now allow filesystems to handle cross sb copy, passing
 | 
				
			||||||
 | 
						 * a file of the wrong filesystem type to filesystem driver can result
 | 
				
			||||||
 | 
						 * in an attempt to dereference the wrong type of ->private_data, so
 | 
				
			||||||
 | 
						 * avoid doing that until we really have a good reason.  NFS defines
 | 
				
			||||||
 | 
						 * several different file_system_type structures, but they all end up
 | 
				
			||||||
 | 
						 * using the same ->copy_file_range() function pointer.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (file_out->f_op->copy_file_range &&
 | 
				
			||||||
 | 
						    file_out->f_op->copy_file_range == file_in->f_op->copy_file_range)
 | 
				
			||||||
		return file_out->f_op->copy_file_range(file_in, pos_in,
 | 
							return file_out->f_op->copy_file_range(file_in, pos_in,
 | 
				
			||||||
						       file_out, pos_out,
 | 
											       file_out, pos_out,
 | 
				
			||||||
						       len, flags);
 | 
											       len, flags);
 | 
				
			||||||
| 
						 | 
					@ -1622,10 +1631,6 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
 | 
				
			||||||
	if (flags != 0)
 | 
						if (flags != 0)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* this could be relaxed once a method supports cross-fs copies */
 | 
					 | 
				
			||||||
	if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb)
 | 
					 | 
				
			||||||
		return -EXDEV;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len,
 | 
						ret = generic_copy_file_checks(file_in, pos_in, file_out, pos_out, &len,
 | 
				
			||||||
				       flags);
 | 
									       flags);
 | 
				
			||||||
	if (unlikely(ret))
 | 
						if (unlikely(ret))
 | 
				
			||||||
| 
						 | 
					@ -1648,7 +1653,8 @@ 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
 | 
						 * Try cloning first, this is supported by more file systems, and
 | 
				
			||||||
	 * more efficient if both clone and copy are supported (e.g. NFS).
 | 
						 * more efficient if both clone and copy are supported (e.g. NFS).
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (file_in->f_op->remap_file_range) {
 | 
						if (file_in->f_op->remap_file_range &&
 | 
				
			||||||
 | 
						    file_inode(file_in)->i_sb == file_inode(file_out)->i_sb) {
 | 
				
			||||||
		loff_t cloned;
 | 
							loff_t cloned;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cloned = file_in->f_op->remap_file_range(file_in, pos_in,
 | 
							cloned = file_in->f_op->remap_file_range(file_in, pos_in,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue