--- /dev/null
+From ef68e831840c40c7d01b328b3c0f5d8c4796c232 Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Fri, 18 Jan 2019 17:25:36 -0800
+Subject: CIFS: Do not reconnect TCP session in add_credits()
+
+From: Pavel Shilovsky <pshilov@microsoft.com>
+
+commit ef68e831840c40c7d01b328b3c0f5d8c4796c232 upstream.
+
+When executing add_credits() we currently call cifs_reconnect()
+if the number of credits is zero and there are no requests in
+flight. In this case we may call cifs_reconnect() recursively
+twice and cause memory corruption given the following sequence
+of functions:
+
+mid1.callback() -> add_credits() -> cifs_reconnect() ->
+-> mid2.callback() -> add_credits() -> cifs_reconnect().
+
+Fix this by avoiding to call cifs_reconnect() in add_credits()
+and checking for zero credits in the demultiplex thread.
+
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/cifs/connect.c | 21 +++++++++++++++++++++
+ fs/cifs/smb2ops.c | 32 +++++++++++++++++++++++++-------
+ 2 files changed, 46 insertions(+), 7 deletions(-)
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -518,6 +518,21 @@ server_unresponsive(struct TCP_Server_In
+ return false;
+ }
+
++static inline bool
++zero_credits(struct TCP_Server_Info *server)
++{
++ int val;
++
++ spin_lock(&server->req_lock);
++ val = server->credits + server->echo_credits + server->oplock_credits;
++ if (server->in_flight == 0 && val == 0) {
++ spin_unlock(&server->req_lock);
++ return true;
++ }
++ spin_unlock(&server->req_lock);
++ return false;
++}
++
+ static int
+ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
+ {
+@@ -530,6 +545,12 @@ cifs_readv_from_socket(struct TCP_Server
+ for (total_read = 0; msg_data_left(smb_msg); total_read += length) {
+ try_to_freeze();
+
++ /* reconnect if no credits and no requests in flight */
++ if (zero_credits(server)) {
++ cifs_reconnect(server);
++ return -ECONNABORTED;
++ }
++
+ if (server_unresponsive(server))
+ return -ECONNABORTED;
+
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -30,6 +30,7 @@
+ #include "smb2glob.h"
+ #include "cifs_ioctl.h"
+
++/* Change credits for different ops and return the total number of credits */
+ static int
+ change_conf(struct TCP_Server_Info *server)
+ {
+@@ -37,17 +38,15 @@ change_conf(struct TCP_Server_Info *serv
+ server->oplock_credits = server->echo_credits = 0;
+ switch (server->credits) {
+ case 0:
+- return -1;
++ return 0;
+ case 1:
+ server->echoes = false;
+ server->oplocks = false;
+- cifs_dbg(VFS, "disabling echoes and oplocks\n");
+ break;
+ case 2:
+ server->echoes = true;
+ server->oplocks = false;
+ server->echo_credits = 1;
+- cifs_dbg(FYI, "disabling oplocks\n");
+ break;
+ default:
+ server->echoes = true;
+@@ -60,14 +59,15 @@ change_conf(struct TCP_Server_Info *serv
+ server->echo_credits = 1;
+ }
+ server->credits -= server->echo_credits + server->oplock_credits;
+- return 0;
++ return server->credits + server->echo_credits + server->oplock_credits;
+ }
+
+ static void
+ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
+ const int optype)
+ {
+- int *val, rc = 0;
++ int *val, rc = -1;
++
+ spin_lock(&server->req_lock);
+ val = server->ops->get_credits_field(server, optype);
+ *val += add;
+@@ -91,8 +91,26 @@ smb2_add_credits(struct TCP_Server_Info
+ }
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+- if (rc)
+- cifs_reconnect(server);
++
++ if (server->tcpStatus == CifsNeedReconnect)
++ return;
++
++ switch (rc) {
++ case -1:
++ /* change_conf hasn't been executed */
++ break;
++ case 0:
++ cifs_dbg(VFS, "Possible client or server bug - zero credits\n");
++ break;
++ case 1:
++ cifs_dbg(VFS, "disabling echoes and oplocks\n");
++ break;
++ case 2:
++ cifs_dbg(FYI, "disabling oplocks\n");
++ break;
++ default:
++ cifs_dbg(FYI, "add %u credits total=%d\n", add, rc);
++ }
+ }
+
+ static void
--- /dev/null
+From acc58d0bab55a50e02c25f00bd6a210ee121595f Mon Sep 17 00:00:00 2001
+From: Pavel Shilovsky <pshilov@microsoft.com>
+Date: Thu, 17 Jan 2019 08:21:24 -0800
+Subject: CIFS: Fix possible hang during async MTU reads and writes
+
+From: Pavel Shilovsky <pshilov@microsoft.com>
+
+commit acc58d0bab55a50e02c25f00bd6a210ee121595f upstream.
+
+When doing MTU i/o we need to leave some credits for
+possible reopen requests and other operations happening
+in parallel. Currently we leave 1 credit which is not
+enough even for reopen only: we need at least 2 credits
+if durable handle reconnect fails. Also there may be
+other operations at the same time including compounding
+ones which require 3 credits at a time each. Fix this
+by leaving 8 credits which is big enough to cover most
+scenarios.
+
+Was able to reproduce this when server was configured
+to give out fewer credits than usual.
+
+The proper fix would be to reconnect a file handle first
+and then obtain credits for an MTU request but this leads
+to bigger code changes and should happen in other patches.
+
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/cifs/smb2ops.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -148,14 +148,14 @@ smb2_wait_mtu_credits(struct TCP_Server_
+
+ scredits = server->credits;
+ /* can deadlock with reopen */
+- if (scredits == 1) {
++ if (scredits <= 8) {
+ *num = SMB2_MAX_BUFFER_SIZE;
+ *credits = 0;
+ break;
+ }
+
+- /* leave one credit for a possible reopen */
+- scredits--;
++ /* leave some credits for reopen and other ops */
++ scredits -= 8;
+ *num = min_t(unsigned int, size,
+ scredits * SMB2_MAX_BUFFER_SIZE);
+