[PATCHv2] Add reflink copy over SMB3.11 with new FSCTL_DUPLICATE_EXTENTS
Darrick J. Wong
darrick.wong at oracle.com
Mon Jun 29 11:27:04 MDT 2015
On Sun, Jun 28, 2015 at 09:21:05PM -0500, Steve French wrote:
> Update the patch to correct target file size.
>
> 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: Steve French <steve.french at primarydata.com>
> ---
> fs/cifs/cifsglob.h | 3 +++
> fs/cifs/cifspdu.h | 2 ++
> fs/cifs/ioctl.c | 16 +++++++++++++---
> fs/cifs/smb2ops.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
> fs/cifs/smb2pdu.h | 8 ++++++++
> fs/cifs/smbfsctl.h | 1 +
> 6 files changed, 75 insertions(+), 3 deletions(-)
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index a0212ec..81194e6 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -390,6 +390,9 @@ struct smb_version_operations {
> int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
> struct cifsFileInfo *target_file, u64 src_off, u64 len,
> 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 *);
> ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *,
> const unsigned char *, const unsigned char *, char *,
> diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
> index 998a66f..47b030d 100644
> --- a/fs/cifs/cifspdu.h
> +++ b/fs/cifs/cifspdu.h
> @@ -2255,6 +2255,8 @@ typedef struct {
>
>
> /* 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_SUPPORTS_USN_JOURNAL 0x02000000
> #define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
> diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
> index 8b7898b..7843b19 100644
> --- a/fs/cifs/ioctl.c
> +++ b/fs/cifs/ioctl.c
> @@ -31,12 +31,14 @@
> #include "cifsproto.h"
> #include "cifs_debug.h"
> #include "cifsfs.h"
> +#include <linux/btrfs.h>
>
> #define CIFS_IOCTL_MAGIC 0xCF
> #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
>
> 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;
> 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,
> 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,
> smb_file_src, smb_file_target, off, len, destoff);
> + else
> + rc = -EOPNOTSUPP;
>
> /* force revalidate of size and timestamps of target file now
> that target is updated on the server */
> @@ -205,7 +212,10 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
> }
> break;
> 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);
Any interest in supporting BTRFS_IOC_CLONE_RANGE or BTRFS_IOC_EXTENT_SAME?
It looks like you could easily support the former, and the latter would
enable things like duperemove. I've been working on a pile of xfstests to
exercise these three ioctls, will post them later today, I hope.
--D
> break;
> default:
> cifs_dbg(FYI, "unsupported ioctl\n");
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index c7d228c..b1e9c0f 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -806,6 +806,53 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
> 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
> smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
> struct cifsFileInfo *cfile)
> @@ -1714,6 +1761,7 @@ struct smb_version_operations smb311_operations = {
> .create_lease_buf = smb3_create_lease_buf,
> .parse_lease_buf = smb3_parse_lease_buf,
> .clone_range = smb2_clone_range,
> + .duplicate_extents = smb2_duplicate_extents,
> /* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */
> .wp_retry_size = smb2_wp_retry_size,
> .dir_needs_close = smb2_dir_needs_close,
> diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
> index 725283a..8e7bbe5 100644
> --- a/fs/cifs/smb2pdu.h
> +++ b/fs/cifs/smb2pdu.h
> @@ -654,6 +654,14 @@ struct compress_ioctl {
> __le16 CompressionState; /* See cifspdu.h for possible flag values */
> } __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_hdr hdr;
> __le16 StructureSize; /* Must be 57 */
> diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h
> index 83efa59..d098da6 100644
> --- a/fs/cifs/smbfsctl.h
> +++ b/fs/cifs/smbfsctl.h
> @@ -78,6 +78,7 @@
> #define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */
> #define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
> #define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */
> +#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344
> #define FSCTL_SIS_LINK_FILES 0x0009C104
> #define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */
> #define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */
> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
More information about the samba-technical
mailing list