mirror of https://gitee.com/openkylin/linux.git
CIFS: Fix SMB2/SMB3 Copy offload support (refcopy) for large files
This third version of the patch, incorparating feedback from David Disseldorp extends the ability of copychunk (refcopy) over smb2/smb3 mounts to handle servers with smaller than usual maximum chunk sizes and also fixes it to handle files bigger than the maximum chunk sizes In the future this can be extended further to handle sending multiple chunk requests in on SMB2 ioctl request which will further improve performance, but even with one 1MB chunk per request the speedup on cp is quite large. Reviewed-by: David Disseldorp <ddiss@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
parent
2d3c627502
commit
9bf0c9cd43
|
@ -532,7 +532,10 @@ smb2_clone_range(const unsigned int xid,
|
|||
int rc;
|
||||
unsigned int ret_data_len;
|
||||
struct copychunk_ioctl *pcchunk;
|
||||
char *retbuf = NULL;
|
||||
struct copychunk_ioctl_rsp *retbuf = NULL;
|
||||
struct cifs_tcon *tcon;
|
||||
int chunks_copied = 0;
|
||||
bool chunk_sizes_updated = false;
|
||||
|
||||
pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL);
|
||||
|
||||
|
@ -547,27 +550,96 @@ smb2_clone_range(const unsigned int xid,
|
|||
|
||||
/* Note: request_res_key sets res_key null only if rc !=0 */
|
||||
if (rc)
|
||||
return rc;
|
||||
goto cchunk_out;
|
||||
|
||||
/* For now array only one chunk long, will make more flexible later */
|
||||
pcchunk->ChunkCount = __constant_cpu_to_le32(1);
|
||||
pcchunk->Reserved = 0;
|
||||
pcchunk->SourceOffset = cpu_to_le64(src_off);
|
||||
pcchunk->TargetOffset = cpu_to_le64(dest_off);
|
||||
pcchunk->Length = cpu_to_le32(len);
|
||||
pcchunk->Reserved2 = 0;
|
||||
|
||||
/* Request that server copy to target from src file identified by key */
|
||||
rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
|
||||
trgtfile->fid.persistent_fid,
|
||||
tcon = tlink_tcon(trgtfile->tlink);
|
||||
|
||||
while (len > 0) {
|
||||
pcchunk->SourceOffset = cpu_to_le64(src_off);
|
||||
pcchunk->TargetOffset = cpu_to_le64(dest_off);
|
||||
pcchunk->Length =
|
||||
cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));
|
||||
|
||||
/* Request server copy to target from src identified by key */
|
||||
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
|
||||
trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
|
||||
true /* is_fsctl */, (char *)pcchunk,
|
||||
sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len);
|
||||
sizeof(struct copychunk_ioctl), (char **)&retbuf,
|
||||
&ret_data_len);
|
||||
if (rc == 0) {
|
||||
if (ret_data_len !=
|
||||
sizeof(struct copychunk_ioctl_rsp)) {
|
||||
cifs_dbg(VFS, "invalid cchunk response size\n");
|
||||
rc = -EIO;
|
||||
goto cchunk_out;
|
||||
}
|
||||
if (retbuf->TotalBytesWritten == 0) {
|
||||
cifs_dbg(FYI, "no bytes copied\n");
|
||||
rc = -EIO;
|
||||
goto cchunk_out;
|
||||
}
|
||||
/*
|
||||
* Check if server claimed to write more than we asked
|
||||
*/
|
||||
if (le32_to_cpu(retbuf->TotalBytesWritten) >
|
||||
le32_to_cpu(pcchunk->Length)) {
|
||||
cifs_dbg(VFS, "invalid copy chunk response\n");
|
||||
rc = -EIO;
|
||||
goto cchunk_out;
|
||||
}
|
||||
if (le32_to_cpu(retbuf->ChunksWritten) != 1) {
|
||||
cifs_dbg(VFS, "invalid num chunks written\n");
|
||||
rc = -EIO;
|
||||
goto cchunk_out;
|
||||
}
|
||||
chunks_copied++;
|
||||
|
||||
/* BB need to special case rc = EINVAL to alter chunk size */
|
||||
src_off += le32_to_cpu(retbuf->TotalBytesWritten);
|
||||
dest_off += le32_to_cpu(retbuf->TotalBytesWritten);
|
||||
len -= le32_to_cpu(retbuf->TotalBytesWritten);
|
||||
|
||||
cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
|
||||
cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n",
|
||||
le32_to_cpu(retbuf->ChunksWritten),
|
||||
le32_to_cpu(retbuf->ChunkBytesWritten),
|
||||
le32_to_cpu(retbuf->TotalBytesWritten));
|
||||
} else if (rc == -EINVAL) {
|
||||
if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
|
||||
goto cchunk_out;
|
||||
|
||||
cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n",
|
||||
le32_to_cpu(retbuf->ChunksWritten),
|
||||
le32_to_cpu(retbuf->ChunkBytesWritten),
|
||||
le32_to_cpu(retbuf->TotalBytesWritten));
|
||||
|
||||
/*
|
||||
* Check if this is the first request using these sizes,
|
||||
* (ie check if copy succeed once with original sizes
|
||||
* and check if the server gave us different sizes after
|
||||
* we already updated max sizes on previous request).
|
||||
* if not then why is the server returning an error now
|
||||
*/
|
||||
if ((chunks_copied != 0) || chunk_sizes_updated)
|
||||
goto cchunk_out;
|
||||
|
||||
/* Check that server is not asking us to grow size */
|
||||
if (le32_to_cpu(retbuf->ChunkBytesWritten) <
|
||||
tcon->max_bytes_chunk)
|
||||
tcon->max_bytes_chunk =
|
||||
le32_to_cpu(retbuf->ChunkBytesWritten);
|
||||
else
|
||||
goto cchunk_out; /* server gave us bogus size */
|
||||
|
||||
/* No need to change MaxChunks since already set to 1 */
|
||||
chunk_sizes_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
cchunk_out:
|
||||
kfree(pcchunk);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1214,10 +1214,17 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
|||
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
|
||||
rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
|
||||
|
||||
if (rc != 0) {
|
||||
if ((rc != 0) && (rc != -EINVAL)) {
|
||||
if (tcon)
|
||||
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||
goto ioctl_exit;
|
||||
} else if (rc == -EINVAL) {
|
||||
if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) &&
|
||||
(opcode != FSCTL_SRV_COPYCHUNK)) {
|
||||
if (tcon)
|
||||
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||
goto ioctl_exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if caller wants to look at return data or just return rc */
|
||||
|
|
Loading…
Reference in New Issue