diff mbox

[CIFS] SMB2/SMB3 Copy offload support (refcopy) finishup

Message ID CAH2r5muLwHY_e8QJP5N4veyqem4mQ9rOJC87nOeHYVyvFmWLsw@mail.gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Steve French Nov. 16, 2013, 7:14 a.m. UTC
From 6474d967fc8228fff7579748f7219f6a8e443f30 Mon Sep 17 00:00:00 2001
From: Steve French <smfrench@gmail.com>
Date: Sat, 16 Nov 2013 01:06:00 -0600
Subject: [PATCH] CIFS: SMB2/SMB3 Copy offload support (refcopy) finish up

This patch extends the ability of copychunk (refcopy) to
handle servers with smaller than usual maximum chunk sizes
and also to handle files bigger than the maximum chunk sizes

Signed-off-by: Steve French <smfrench@gmail.com>
---
 fs/cifs/smb2ops.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 fs/cifs/smb2pdu.c |  2 +-
 2 files changed, 79 insertions(+), 12 deletions(-)

         goto ioctl_exit;

Comments

David Disseldorp Nov. 16, 2013, 6:50 p.m. UTC | #1
On Sat, 16 Nov 2013 01:14:19 -0600
Steve French <smfrench@gmail.com> wrote:

>  fs/cifs/smb2ops.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-------
>  fs/cifs/smb2pdu.c |  2 +-
>  2 files changed, 79 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index 11dde4b..7a21447 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -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);
>  
> @@ -552,22 +555,86 @@ smb2_clone_range(const unsigned int xid,

The SMB2_request_res_key() error path above here should be fixed to free
pcchunk.

>  	/* 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;
>  
> +	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 that server copy to target from src file identified by key */

Comment should be indented here too.

> -	rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
> -			trgtfile->fid.persistent_fid,
> +		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);
> -
> -	/* BB need to special case rc = EINVAL to alter chunk size */
> -
> -	cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
> +			sizeof(struct copychunk_ioctl),	(char **)&retbuf,
> +			&ret_data_len);
> +		if (rc == 0) {
> +			if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
> +				goto cchunk_out;
> +			if (retbuf->TotalBytesWritten == 0) {
> +				cifs_dbg(FYI, "no bytes copied\n");
> +				goto cchunk_out;
> +			}

These two error cases should set rc non-zero, it may not be the last
copy-chunk for this clone.
...

> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -1214,7 +1214,7 @@ 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)) {

This should probably be FSCTL_SRV_COPYCHUNK[_WRITE] specific. See MS-SMB2
  3.3.4.4   Sending an Error Response
  When the server is responding with a failure to any command sent by the
  client, the response message MUST be constructed as described here. An
  error code other than one of the following indicates a failure:
  ... 

  STATUS_INVALID_PARAMETER in an FSCTL_SRV_COPYCHUNK or
  FSCTL_SRV_COPYCHUNK_WRITE response, when returning an
  SRV_COPYCHUNK_RESPONSE as described in section 3.3.5.15.6.2.


>  		if (tcon)
>  			cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
>  		goto ioctl_exit;

Cheers, David
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Steve French Nov. 17, 2013, 12:09 a.m. UTC | #2
sending updated patch to include your feedback

On Sat, Nov 16, 2013 at 12:50 PM, David Disseldorp <ddiss@suse.de> wrote:
> On Sat, 16 Nov 2013 01:14:19 -0600
> Steve French <smfrench@gmail.com> wrote:
>
>>  fs/cifs/smb2ops.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-------
>>  fs/cifs/smb2pdu.c |  2 +-
>>  2 files changed, 79 insertions(+), 12 deletions(-)
>>
>> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
>> index 11dde4b..7a21447 100644
>> --- a/fs/cifs/smb2ops.c
>> +++ b/fs/cifs/smb2ops.c
>> @@ -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);
>>
>> @@ -552,22 +555,86 @@ smb2_clone_range(const unsigned int xid,
>
> The SMB2_request_res_key() error path above here should be fixed to free
> pcchunk.
>
>>       /* 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;
>>
>> +     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 that server copy to target from src file identified by key */
>
> Comment should be indented here too.
>
>> -     rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
>> -                     trgtfile->fid.persistent_fid,
>> +             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);
>> -
>> -     /* BB need to special case rc = EINVAL to alter chunk size */
>> -
>> -     cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
>> +                     sizeof(struct copychunk_ioctl), (char **)&retbuf,
>> +                     &ret_data_len);
>> +             if (rc == 0) {
>> +                     if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
>> +                             goto cchunk_out;
>> +                     if (retbuf->TotalBytesWritten == 0) {
>> +                             cifs_dbg(FYI, "no bytes copied\n");
>> +                             goto cchunk_out;
>> +                     }
>
> These two error cases should set rc non-zero, it may not be the last
> copy-chunk for this clone.
> ...
>
>> --- a/fs/cifs/smb2pdu.c
>> +++ b/fs/cifs/smb2pdu.c
>> @@ -1214,7 +1214,7 @@ 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)) {
>
> This should probably be FSCTL_SRV_COPYCHUNK[_WRITE] specific. See MS-SMB2
>   3.3.4.4   Sending an Error Response
>   When the server is responding with a failure to any command sent by the
>   client, the response message MUST be constructed as described here. An
>   error code other than one of the following indicates a failure:
>   ...
>
>   STATUS_INVALID_PARAMETER in an FSCTL_SRV_COPYCHUNK or
>   FSCTL_SRV_COPYCHUNK_WRITE response, when returning an
>   SRV_COPYCHUNK_RESPONSE as described in section 3.3.5.15.6.2.
>
>
>>               if (tcon)
>>                       cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
>>               goto ioctl_exit;
>
> Cheers, David
diff mbox

Patch

From 6474d967fc8228fff7579748f7219f6a8e443f30 Mon Sep 17 00:00:00 2001
From: Steve French <smfrench@gmail.com>
Date: Sat, 16 Nov 2013 01:06:00 -0600
Subject: [PATCH] CIFS: SMB2/SMB3 Copy offload support (refcopy) finish up

This patch extends the ability of copychunk (refcopy) to
handle servers with smaller than usual maximum chunk sizes
and also to handle files bigger than the maximum chunk sizes

Signed-off-by: Steve French <smfrench@gmail.com>
---
 fs/cifs/smb2ops.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 fs/cifs/smb2pdu.c |  2 +-
 2 files changed, 79 insertions(+), 12 deletions(-)

diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 11dde4b..7a21447 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -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);
 
@@ -552,22 +555,86 @@  smb2_clone_range(const unsigned int xid,
 	/* 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;
 
+	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 that server copy to target from src file identified by key */
-	rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
-			trgtfile->fid.persistent_fid,
+		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);
-
-	/* BB need to special case rc = EINVAL to alter chunk size */
-
-	cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
+			sizeof(struct copychunk_ioctl),	(char **)&retbuf,
+			&ret_data_len);
+		if (rc == 0) {
+			if (ret_data_len != sizeof(struct copychunk_ioctl_rsp))
+				goto cchunk_out;
+			if (retbuf->TotalBytesWritten == 0) {
+				cifs_dbg(FYI, "no bytes copied\n");
+				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++;
+
+			src_off += le32_to_cpu(retbuf->TotalBytesWritten);
+			dest_off += le32_to_cpu(retbuf->TotalBytesWritten);
+			len -= le32_to_cpu(retbuf->TotalBytesWritten);
+
+			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;
 }
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index d65270c..c5915dc 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1214,7 +1214,7 @@  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;
-- 
1.8.3.1