Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02b16665 authored by Steve French's avatar Steve French
Browse files

Add reflink copy over SMB3.11 with new FSCTL_DUPLICATE_EXTENTS



 Getting fantastic copy performance with cp --reflink over SMB3.11
 using the new FSCTL_DUPLICATE_EXTENTS.

 This FSCTL was added in the SMB3.11 dialect (testing was
 against REFS file system) so have put it as a 3.11 protocol
 specific operation ("vers=3.1.1" on the mount).  Tested at
 the SMB3 plugfest in Redmond.

 It depends on the new FS Attribute (BLOCK_REFCOUNTING) which
 is used to advertise support for the ability to do this ioctl
 (if you can support multiple files pointing to the same block
 than this refcounting ability or equivalent is needed to
 support the new reflink-like duplicate extent SMB3 ioctl.

Signed-off-by: default avatarSteve French <steve.french@primarydata.com>
parent aab1893d
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -390,6 +390,9 @@ struct smb_version_operations {
	int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
	int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
			struct cifsFileInfo *target_file, u64 src_off, u64 len,
			struct cifsFileInfo *target_file, u64 src_off, u64 len,
			u64 dest_off);
			u64 dest_off);
	int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src,
			struct cifsFileInfo *target_file, u64 src_off, u64 len,
			u64 dest_off);
	int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
	int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
	ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *,
	ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *,
			const unsigned char *, const unsigned char *, char *,
			const unsigned char *, const unsigned char *, char *,
+2 −0
Original line number Original line Diff line number Diff line
@@ -2255,6 +2255,8 @@ typedef struct {




/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
#define FILE_SUPPORTS_SPARSE_VDL	0x10000000 /* faster nonsparse extend */
#define FILE_SUPPORTS_BLOCK_REFCOUNTING	0x08000000 /* allow ioctl dup extents */
#define FILE_SUPPORT_INTEGRITY_STREAMS	0x04000000
#define FILE_SUPPORT_INTEGRITY_STREAMS	0x04000000
#define FILE_SUPPORTS_USN_JOURNAL	0x02000000
#define FILE_SUPPORTS_USN_JOURNAL	0x02000000
#define FILE_SUPPORTS_OPEN_BY_FILE_ID	0x01000000
#define FILE_SUPPORTS_OPEN_BY_FILE_ID	0x01000000
+13 −3
Original line number Original line Diff line number Diff line
@@ -31,12 +31,14 @@
#include "cifsproto.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "cifs_debug.h"
#include "cifsfs.h"
#include "cifsfs.h"
#include <linux/btrfs.h>


#define CIFS_IOCTL_MAGIC	0xCF
#define CIFS_IOCTL_MAGIC	0xCF
#define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int)
#define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int)


static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
			unsigned long srcfd, u64 off, u64 len, u64 destoff)
			unsigned long srcfd, u64 off, u64 len, u64 destoff,
			bool dup_extents)
{
{
	int rc;
	int rc;
	struct cifsFileInfo *smb_file_target = dst_file->private_data;
	struct cifsFileInfo *smb_file_target = dst_file->private_data;
@@ -109,9 +111,14 @@ static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
	truncate_inode_pages_range(&target_inode->i_data, destoff,
	truncate_inode_pages_range(&target_inode->i_data, destoff,
				   PAGE_CACHE_ALIGN(destoff + len)-1);
				   PAGE_CACHE_ALIGN(destoff + len)-1);


	if (target_tcon->ses->server->ops->clone_range)
	if (dup_extents && target_tcon->ses->server->ops->duplicate_extents)
		rc = target_tcon->ses->server->ops->duplicate_extents(xid,
			smb_file_src, smb_file_target, off, len, destoff);
	else if (!dup_extents && target_tcon->ses->server->ops->clone_range)
		rc = target_tcon->ses->server->ops->clone_range(xid,
		rc = target_tcon->ses->server->ops->clone_range(xid,
			smb_file_src, smb_file_target, off, len, destoff);
			smb_file_src, smb_file_target, off, len, destoff);
	else
		rc = -EOPNOTSUPP;


	/* force revalidate of size and timestamps of target file now
	/* force revalidate of size and timestamps of target file now
	   that target is updated on the server */
	   that target is updated on the server */
@@ -205,7 +212,10 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
			}
			}
			break;
			break;
		case CIFS_IOC_COPYCHUNK_FILE:
		case CIFS_IOC_COPYCHUNK_FILE:
			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, false);
			break;
		case BTRFS_IOC_CLONE:
			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, true);
			break;
			break;
		default:
		default:
			cifs_dbg(FYI, "unsupported ioctl\n");
			cifs_dbg(FYI, "unsupported ioctl\n");
+48 −0
Original line number Original line Diff line number Diff line
@@ -806,6 +806,53 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
			    cfile->fid.volatile_fid, cfile->pid, &eof, false);
			    cfile->fid.volatile_fid, cfile->pid, &eof, false);
}
}


#ifdef CONFIG_CIFS_SMB311
static int
smb2_duplicate_extents(const unsigned int xid,
			struct cifsFileInfo *srcfile,
			struct cifsFileInfo *trgtfile, u64 src_off,
			u64 len, u64 dest_off)
{
	int rc;
	unsigned int ret_data_len;
	char *retbuf = NULL;
	struct duplicate_extents_to_file dup_ext_buf;
	struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);

	/* server fileays advertise duplicate extent support with this flag */
	if ((le32_to_cpu(tcon->fsAttrInfo.Attributes) &
	     FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0)
		return -EOPNOTSUPP;

	dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid;
	dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid;
	dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off);
	dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off);
	dup_ext_buf.ByteCount = cpu_to_le64(len);
	cifs_dbg(FYI, "duplicate extents: src off %lld dst off %lld len %lld",
		src_off, dest_off, len);

	rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
	if (rc)
		goto duplicate_extents_out;

	rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
			trgtfile->fid.volatile_fid,
			FSCTL_DUPLICATE_EXTENTS_TO_FILE,
			true /* is_fsctl */, (char *)&dup_ext_buf,
			sizeof(struct duplicate_extents_to_file),
			(char **)&retbuf,
			&ret_data_len);

	if (ret_data_len > 0)
		cifs_dbg(FYI, "non-zero response length in duplicate extents");

duplicate_extents_out:
	return rc;
}
#endif /* CONFIG_CIFS_SMB311 */


static int
static int
smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
		   struct cifsFileInfo *cfile)
		   struct cifsFileInfo *cfile)
@@ -1714,6 +1761,7 @@ struct smb_version_operations smb311_operations = {
	.create_lease_buf = smb3_create_lease_buf,
	.create_lease_buf = smb3_create_lease_buf,
	.parse_lease_buf = smb3_parse_lease_buf,
	.parse_lease_buf = smb3_parse_lease_buf,
	.clone_range = smb2_clone_range,
	.clone_range = smb2_clone_range,
	.duplicate_extents = smb2_duplicate_extents,
/*	.validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */
/*	.validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */
	.wp_retry_size = smb2_wp_retry_size,
	.wp_retry_size = smb2_wp_retry_size,
	.dir_needs_close = smb2_dir_needs_close,
	.dir_needs_close = smb2_dir_needs_close,
+8 −0
Original line number Original line Diff line number Diff line
@@ -654,6 +654,14 @@ struct compress_ioctl {
	__le16 CompressionState; /* See cifspdu.h for possible flag values */
	__le16 CompressionState; /* See cifspdu.h for possible flag values */
} __packed;
} __packed;


struct duplicate_extents_to_file {
	__u64 PersistentFileHandle; /* source file handle, opaque endianness */
	__u64 VolatileFileHandle;
	__le64 SourceFileOffset;
	__le64 TargetFileOffset;
	__le64 ByteCount;  /* Bytes to be copied */
} __packed;

struct smb2_ioctl_req {
struct smb2_ioctl_req {
	struct smb2_hdr hdr;
	struct smb2_hdr hdr;
	__le16 StructureSize;	/* Must be 57 */
	__le16 StructureSize;	/* Must be 57 */
Loading