On Wed, 2017-02-08 at 15:44 -0800, Pavel Shilovsky wrote: > 2017-02-07 3:33 GMT-08:00 Sachin Prabhu <sprabhu@xxxxxxxxxx>: > > The patch introduces the file_operations helper > > cifs_copy_file_range(). > > > > The vfs helper vfs_copy_file_range() first calls clone_file_range > > which for cifs uses > > SMB2_ioctl(.., FSCTL_DUPLICATE_EXTENTS_TO_FILE, ..) > > to do a server side copy of the file. This ioctl is only supported > > on > > SMB3 and later versions. > > > > Once this call fails, vfs_copy_file_range() calls copy_file_range() > > corresponding to the cifs filesystem which is introduced by this > > patch. > > This calls the more widely available > > SMB2_ioctl(.., FSCTL_SRV_COPYCHUNK_WRITE, ..) > > > > The upstream changes in this area was first made by the commit > > 04b38d601239 ("vfs: pull btrfs clone API to vfs layer") > > This commit also introduces the ioctls FICLONE and FICLONERANGE > > which > > removes the need for separate cifs ioctls to perform server side > > copies > > and hence can be removed. > > > > ioctls FICLONE and FICLONERANGE perform a file clone operation which > for cifs is implemented through FSCTL_DUPLICATE_EXTENTS_TO_FILE for > SMB3 only. ioctl CIFS_IOC_COPYCHUNK_FILE performs a file copy > operation which is implemented through FSCTL_SRV_COPYCHUNK_WRITE. So, > this generic ioctls can't substitute the existing cifs one. > > Also there is a copy_file_range() system call that calls > vfs_copy_file_range(). With this patch applied it tries to clone > first > and then copy. It means that for SMB3 it will still call > DUPLICATE_EXTENTS_TO_FILE and we end up with two calls issuing > DUPLICATE_EXTENTS_TO_FILE and zero -SRV_COPYCHUNK_WRITE. > > Thus I suggest not to remove a separate cifs ioctl that always calls > COPYCHUNK_WRITE for possible scenarios where > DUPLICATE_EXTENTS_TO_FILE > is not suitable. I'll post another patch which keeps the ioctl intact. Sachin Prabhu > > > Signed-off-by: Sachin Prabhu <sprabhu@xxxxxxxxxx> > > --- > > fs/cifs/cifsfs.c | 72 +++++++++++++++++++++++++++++++++++ > > fs/cifs/cifsglob.h | 2 +- > > fs/cifs/ioctl.c | 107 --------------------------------------- > > -------------- > > fs/cifs/smb2ops.c | 16 +++++--- > > 4 files changed, 84 insertions(+), 113 deletions(-) > > > > diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c > > index 70f4e65..33bc53d 100644 > > --- a/fs/cifs/cifsfs.c > > +++ b/fs/cifs/cifsfs.c > > @@ -972,6 +972,72 @@ static int cifs_clone_file_range(struct file > > *src_file, loff_t off, > > return rc; > > } > > > > +ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, > > + struct file *dst_file, loff_t destoff, > > + size_t len, unsigned int flags) > > +{ > > + struct inode *src_inode = file_inode(src_file); > > + struct inode *target_inode = file_inode(dst_file); > > + struct cifsFileInfo *smb_file_src = src_file->private_data; > > + struct cifsFileInfo *smb_file_target = dst_file- > > >private_data; > > + struct cifs_tcon *src_tcon; > > + struct cifs_tcon *target_tcon; > > + unsigned int xid; > > + int rc; > > + > > + xid = get_xid(); > > + > > + cifs_dbg(FYI, "copy file range\n"); > > + > > + if (src_inode == target_inode) { > > + rc = -EINVAL; > > + goto out; > > + } > > + > > + if (!src_file->private_data || !dst_file->private_data) { > > + rc = -EBADF; > > + cifs_dbg(VFS, "missing cifsFileInfo on copy range > > src file\n"); > > + goto out; > > + } > > + > > + rc = -EXDEV; > > + src_tcon = tlink_tcon(smb_file_src->tlink); > > + target_tcon = tlink_tcon(smb_file_target->tlink); > > + > > + if (src_tcon->ses != target_tcon->ses) { > > + cifs_dbg(VFS, "source and target of copy not on > > same server\n"); > > + goto out; > > + } > > + > > + /* > > + * Note: cifs case is easier than btrfs since server > > responsible for > > + * checks for proper open modes and file type and if it > > wants > > + * server could even support copy of range where source = > > target > > + */ > > + lock_two_nondirectories(target_inode, src_inode); > > + > > + cifs_dbg(FYI, "about to flush pages\n"); > > + /* should we flush first and last page first */ > > + truncate_inode_pages(&target_inode->i_data, 0); > > + > > + if (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); > > I think there is a naming confusion here. I suggest to rename > clone_range() callback to copy_range() to reflect its actual logic. > > > + else > > + rc = -EOPNOTSUPP; > > + > > + /* force revalidate of size and timestamps of target file > > now > > + that target is updated on the server */ > > + CIFS_I(target_inode)->time = 0; > > + /* although unlocking in the reverse order from locking is > > not > > + strictly necessary here it is a little cleaner to be > > consistent */ > > + unlock_two_nondirectories(src_inode, target_inode); > > + > > +out: > > + free_xid(xid); > > + return rc; > > +} > > + > > const struct file_operations cifs_file_ops = { > > .read_iter = cifs_loose_read_iter, > > .write_iter = cifs_file_write_iter, > > @@ -984,6 +1050,7 @@ const struct file_operations cifs_file_ops = { > > .splice_read = generic_file_splice_read, > > .llseek = cifs_llseek, > > .unlocked_ioctl = cifs_ioctl, > > + .copy_file_range = cifs_copy_file_range, > > .clone_file_range = cifs_clone_file_range, > > .setlease = cifs_setlease, > > .fallocate = cifs_fallocate, > > @@ -1001,6 +1068,7 @@ const struct file_operations > > cifs_file_strict_ops = { > > .splice_read = generic_file_splice_read, > > .llseek = cifs_llseek, > > .unlocked_ioctl = cifs_ioctl, > > + .copy_file_range = cifs_copy_file_range, > > .clone_file_range = cifs_clone_file_range, > > .setlease = cifs_setlease, > > .fallocate = cifs_fallocate, > > @@ -1018,6 +1086,7 @@ const struct file_operations > > cifs_file_direct_ops = { > > .mmap = cifs_file_mmap, > > .splice_read = generic_file_splice_read, > > .unlocked_ioctl = cifs_ioctl, > > + .copy_file_range = cifs_copy_file_range, > > .clone_file_range = cifs_clone_file_range, > > .llseek = cifs_llseek, > > .setlease = cifs_setlease, > > @@ -1035,6 +1104,7 @@ const struct file_operations > > cifs_file_nobrl_ops = { > > .splice_read = generic_file_splice_read, > > .llseek = cifs_llseek, > > .unlocked_ioctl = cifs_ioctl, > > + .copy_file_range = cifs_copy_file_range, > > .clone_file_range = cifs_clone_file_range, > > .setlease = cifs_setlease, > > .fallocate = cifs_fallocate, > > @@ -1051,6 +1121,7 @@ const struct file_operations > > cifs_file_strict_nobrl_ops = { > > .splice_read = generic_file_splice_read, > > .llseek = cifs_llseek, > > .unlocked_ioctl = cifs_ioctl, > > + .copy_file_range = cifs_copy_file_range, > > .clone_file_range = cifs_clone_file_range, > > .setlease = cifs_setlease, > > .fallocate = cifs_fallocate, > > @@ -1067,6 +1138,7 @@ const struct file_operations > > cifs_file_direct_nobrl_ops = { > > .mmap = cifs_file_mmap, > > .splice_read = generic_file_splice_read, > > .unlocked_ioctl = cifs_ioctl, > > + .copy_file_range = cifs_copy_file_range, > > .clone_file_range = cifs_clone_file_range, > > .llseek = cifs_llseek, > > .setlease = cifs_setlease, > > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h > > index 7ea8a33..ae0c095 100644 > > --- a/fs/cifs/cifsglob.h > > +++ b/fs/cifs/cifsglob.h > > @@ -405,7 +405,7 @@ struct smb_version_operations { > > char * (*create_lease_buf)(u8 *, u8); > > /* parse lease context buffer and return oplock/epoch info > > */ > > __u8 (*parse_lease_buf)(void *, unsigned int *); > > - int (*clone_range)(const unsigned int, struct cifsFileInfo > > *src_file, > > + ssize_t (*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, > > diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c > > index 0015287..14975b7 100644 > > --- a/fs/cifs/ioctl.c > > +++ b/fs/cifs/ioctl.c > > @@ -34,110 +34,6 @@ > > #include "cifs_ioctl.h" > > #include <linux/btrfs.h> > > > > -static int cifs_file_clone_range(unsigned int xid, struct file > > *src_file, > > - struct file *dst_file) > > -{ > > - struct inode *src_inode = file_inode(src_file); > > - struct inode *target_inode = file_inode(dst_file); > > - struct cifsFileInfo *smb_file_src; > > - struct cifsFileInfo *smb_file_target; > > - struct cifs_tcon *src_tcon; > > - struct cifs_tcon *target_tcon; > > - int rc; > > - > > - cifs_dbg(FYI, "ioctl clone range\n"); > > - > > - if (!src_file->private_data || !dst_file->private_data) { > > - rc = -EBADF; > > - cifs_dbg(VFS, "missing cifsFileInfo on copy range > > src file\n"); > > - goto out; > > - } > > - > > - rc = -EXDEV; > > - smb_file_target = dst_file->private_data; > > - smb_file_src = src_file->private_data; > > - src_tcon = tlink_tcon(smb_file_src->tlink); > > - target_tcon = tlink_tcon(smb_file_target->tlink); > > - > > - if (src_tcon->ses != target_tcon->ses) { > > - cifs_dbg(VFS, "source and target of copy not on > > same server\n"); > > - goto out; > > - } > > - > > - /* > > - * Note: cifs case is easier than btrfs since server > > responsible for > > - * checks for proper open modes and file type and if it > > wants > > - * server could even support copy of range where source = > > target > > - */ > > - lock_two_nondirectories(target_inode, src_inode); > > - > > - cifs_dbg(FYI, "about to flush pages\n"); > > - /* should we flush first and last page first */ > > - truncate_inode_pages(&target_inode->i_data, 0); > > - > > - if (target_tcon->ses->server->ops->clone_range) > > - rc = target_tcon->ses->server->ops- > > >clone_range(xid, > > - smb_file_src, smb_file_target, 0, > > src_inode->i_size, 0); > > - else > > - rc = -EOPNOTSUPP; > > - > > - /* force revalidate of size and timestamps of target file > > now > > - that target is updated on the server */ > > - CIFS_I(target_inode)->time = 0; > > - /* although unlocking in the reverse order from locking is > > not > > - strictly necessary here it is a little cleaner to be > > consistent */ > > - unlock_two_nondirectories(src_inode, target_inode); > > -out: > > - return rc; > > -} > > - > > -static long cifs_ioctl_clone(unsigned int xid, struct file > > *dst_file, > > - unsigned long srcfd) > > -{ > > - int rc; > > - struct fd src_file; > > - struct inode *src_inode; > > - > > - cifs_dbg(FYI, "ioctl clone range\n"); > > - /* the destination must be opened for writing */ > > - if (!(dst_file->f_mode & FMODE_WRITE)) { > > - cifs_dbg(FYI, "file target not open for write\n"); > > - return -EINVAL; > > - } > > - > > - /* check if target volume is readonly and take reference */ > > - rc = mnt_want_write_file(dst_file); > > - if (rc) { > > - cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", > > rc); > > - return rc; > > - } > > - > > - src_file = fdget(srcfd); > > - if (!src_file.file) { > > - rc = -EBADF; > > - goto out_drop_write; > > - } > > - > > - if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) { > > - rc = -EBADF; > > - cifs_dbg(VFS, "src file seems to be from a > > different filesystem type\n"); > > - goto out_fput; > > - } > > - > > - src_inode = file_inode(src_file.file); > > - rc = -EINVAL; > > - if (S_ISDIR(src_inode->i_mode)) > > - goto out_fput; > > - > > - rc = cifs_file_clone_range(xid, src_file.file, dst_file); > > - > > -out_fput: > > - fdput(src_file); > > -out_drop_write: > > - mnt_drop_write_file(dst_file); > > - return rc; > > -} > > - > > static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon > > *tcon, > > void __user *arg) > > { > > @@ -250,9 +146,6 @@ long cifs_ioctl(struct file *filep, unsigned > > int command, unsigned long arg) > > cifs_dbg(FYI, "set compress flag rc > > %d\n", rc); > > } > > break; > > - case CIFS_IOC_COPYCHUNK_FILE: > > - rc = cifs_ioctl_clone(xid, filep, arg); > > - break; > > case CIFS_IOC_SET_INTEGRITY: > > if (pSMBFile == NULL) > > break; > > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c > > index 5d456eb..b7aa0926 100644 > > --- a/fs/cifs/smb2ops.c > > +++ b/fs/cifs/smb2ops.c > > @@ -586,7 +586,7 @@ SMB2_request_res_key(const unsigned int xid, > > struct cifs_tcon *tcon, > > return rc; > > } > > > > -static int > > +static ssize_t > > smb2_clone_range(const unsigned int xid, > > struct cifsFileInfo *srcfile, > > struct cifsFileInfo *trgtfile, u64 src_off, > > @@ -599,6 +599,7 @@ smb2_clone_range(const unsigned int xid, > > struct cifs_tcon *tcon; > > int chunks_copied = 0; > > bool chunk_sizes_updated = false; > > + ssize_t bytes_written, total_bytes_written = 0; > > > > pcchunk = kmalloc(sizeof(struct copychunk_ioctl), > > GFP_KERNEL); > > > > @@ -662,14 +663,16 @@ smb2_clone_range(const unsigned int xid, > > } > > chunks_copied++; > > > > + bytes_written = le32_to_cpu(retbuf- > > >TotalBytesWritten); > > src_off += le32_to_cpu(retbuf- > > >TotalBytesWritten); > > dest_off += le32_to_cpu(retbuf- > > >TotalBytesWritten); > > - len -= le32_to_cpu(retbuf- > > >TotalBytesWritten); > > + len -= bytes_written; > > + total_bytes_written += bytes_written; > > > > - cifs_dbg(FYI, "Chunks %d PartialChunk %d > > Total %d\n", > > + cifs_dbg(FYI, "Chunks %d PartialChunk %d > > Total %zu\n", > > le32_to_cpu(retbuf->ChunksWritten), > > le32_to_cpu(retbuf- > > >ChunkBytesWritten), > > - le32_to_cpu(retbuf- > > >TotalBytesWritten)); > > + bytes_written); > > } else if (rc == -EINVAL) { > > if (ret_data_len != sizeof(struct > > copychunk_ioctl_rsp)) > > goto cchunk_out; > > @@ -706,7 +709,10 @@ smb2_clone_range(const unsigned int xid, > > cchunk_out: > > kfree(pcchunk); > > kfree(retbuf); > > - return rc; > > + if (rc) > > + return rc; > > + else > > + return total_bytes_written; > > } > > > > static int > > -- > > 2.9.3 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux- > > cifs" in > > the body of a message to majordomo@xxxxxxxxxxxxxxx > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > Other than the comments above the patch looks good. > -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html