[4/8] cifs: update receive_encrypted_standard to handle compounded responses

Message ID 20180731232618.23586-5-lsahlber@redhat.com
State New
Headers show
Series
  • cifs compounding
Related show

Commit Message

Ronnie Sahlberg July 31, 2018, 11:26 p.m.
Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/smb2ops.c | 39 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)

Comments

Pavel Shilovsky Aug. 3, 2018, 9:53 p.m. | #1
2018-07-31 16:26 GMT-07:00 Ronnie Sahlberg <lsahlber@redhat.com>:
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/smb2ops.c | 39 ++++++++++++++++++++++++++++++++++++---
>  1 file changed, 36 insertions(+), 3 deletions(-)
>
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index 81ff2bd6ceaa..10b888b954e4 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -2930,11 +2930,14 @@ static int
>  receive_encrypted_standard(struct TCP_Server_Info *server,
>                            struct mid_q_entry **mid)
>  {
> -       int length;
> +       int ret, length;
>         char *buf = server->smallbuf;
> +       struct smb2_sync_hdr *shdr;
>         unsigned int pdu_length = server->pdu_size;
>         unsigned int buf_size;
>         struct mid_q_entry *mid_entry;
> +       int next_is_large;
> +       char *next_buffer = NULL;
>
>         /* switch to large buffer if too big for a small one */
>         if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) {
> @@ -2955,6 +2958,21 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
>         if (length)
>                 return length;
>
> +       next_is_large = server->large_buf;
> + one_more:
> +       shdr = (struct smb2_sync_hdr *)buf;
> +       if (shdr->NextCommand) {
> +               if (next_is_large) {
> +                       next_buffer = (char *)cifs_buf_get();
> +                       memcpy(next_buffer, server->bigbuf + shdr->NextCommand,
> +                              pdu_length - shdr->NextCommand);
> +               } else {
> +                       next_buffer = (char *)cifs_small_buf_get();
> +                       memcpy(next_buffer, server->smallbuf + shdr->NextCommand,
> +                              pdu_length - shdr->NextCommand);
> +               }
> +       }
> +
>         mid_entry = smb2_find_mid(server, buf);
>         if (mid_entry == NULL)
>                 cifs_dbg(FYI, "mid not found\n");
> @@ -2962,13 +2980,28 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
>                 cifs_dbg(FYI, "mid found\n");
>                 mid_entry->decrypted = true;
>         }
> +       mid_entry->resp_buf_size = server->pdu_size;
>
>         *mid = mid_entry;

demultiplex thread calls receive_encrypted_standard() with double
pointer to mid to populate, so it can issue mid->callback to wake up a
thread waiting for a specific mid. Now in receive_encrypted_standard()
we process several mids but save only the last one in the output thus
demultiplex thread will only call mid->callback for the last message
in a chain.

In the same time in compound_send_recv() we call wait_for_response()
for every mid in a compounded chain. Doesn't it mean that we will wait
forever or am I missing something?

>
>         if (mid_entry && mid_entry->handle)
> -               return mid_entry->handle(server, mid_entry);
> +               ret = mid_entry->handle(server, mid_entry);
> +       else
> +               ret = cifs_handle_standard(server, mid_entry);
> +
> +       if (ret == 0 && shdr->NextCommand) {
> +               pdu_length -= shdr->NextCommand;
> +               server->large_buf = next_is_large;
> +               if (next_is_large) {
> +                       server->bigbuf = next_buffer;
> +               } else {
> +                       server->smallbuf = next_buffer;
> +               }
> +               buf += shdr->NextCommand;
> +               goto one_more;
> +       }
>
> -       return cifs_handle_standard(server, mid_entry);
> +        return ret;
>  }
>
>  static int
> --
> 2.13.3
>
> --
> 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
--
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

Patch

diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 81ff2bd6ceaa..10b888b954e4 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -2930,11 +2930,14 @@  static int
 receive_encrypted_standard(struct TCP_Server_Info *server,
 			   struct mid_q_entry **mid)
 {
-	int length;
+	int ret, length;
 	char *buf = server->smallbuf;
+	struct smb2_sync_hdr *shdr;
 	unsigned int pdu_length = server->pdu_size;
 	unsigned int buf_size;
 	struct mid_q_entry *mid_entry;
+	int next_is_large;
+	char *next_buffer = NULL;
 
 	/* switch to large buffer if too big for a small one */
 	if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) {
@@ -2955,6 +2958,21 @@  receive_encrypted_standard(struct TCP_Server_Info *server,
 	if (length)
 		return length;
 
+	next_is_large = server->large_buf;
+ one_more:
+	shdr = (struct smb2_sync_hdr *)buf;
+	if (shdr->NextCommand) {
+		if (next_is_large) {
+			next_buffer = (char *)cifs_buf_get();
+			memcpy(next_buffer, server->bigbuf + shdr->NextCommand,
+			       pdu_length - shdr->NextCommand);
+		} else {
+			next_buffer = (char *)cifs_small_buf_get();
+			memcpy(next_buffer, server->smallbuf + shdr->NextCommand,
+			       pdu_length - shdr->NextCommand);
+		}
+	}
+
 	mid_entry = smb2_find_mid(server, buf);
 	if (mid_entry == NULL)
 		cifs_dbg(FYI, "mid not found\n");
@@ -2962,13 +2980,28 @@  receive_encrypted_standard(struct TCP_Server_Info *server,
 		cifs_dbg(FYI, "mid found\n");
 		mid_entry->decrypted = true;
 	}
+	mid_entry->resp_buf_size = server->pdu_size;
 
 	*mid = mid_entry;
 
 	if (mid_entry && mid_entry->handle)
-		return mid_entry->handle(server, mid_entry);
+		ret = mid_entry->handle(server, mid_entry);
+	else
+		ret = cifs_handle_standard(server, mid_entry);
+
+	if (ret == 0 && shdr->NextCommand) {
+		pdu_length -= shdr->NextCommand;
+		server->large_buf = next_is_large;
+		if (next_is_large) {
+			server->bigbuf = next_buffer;
+		} else {
+			server->smallbuf = next_buffer;
+		}
+		buf += shdr->NextCommand;
+		goto one_more;
+	}
 
-	return cifs_handle_standard(server, mid_entry);
+        return ret;
 }
 
 static int