From: Greg Kroah-Hartman Date: Mon, 28 Jan 2019 10:15:45 +0000 (+0100) Subject: 4.14-stable patches X-Git-Tag: v4.9.154~45 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8ad849efd6aa1ebbb263783b5ca0930af6c62015;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches added patches: cifs-do-not-reconnect-tcp-session-in-add_credits.patch cifs-fix-credit-calculation-for-encrypted-reads-with-errors.patch cifs-fix-credits-calculations-for-reads-with-errors.patch cifs-fix-possible-hang-during-async-mtu-reads-and-writes.patch --- diff --git a/queue-4.14/cifs-do-not-reconnect-tcp-session-in-add_credits.patch b/queue-4.14/cifs-do-not-reconnect-tcp-session-in-add_credits.patch new file mode 100644 index 00000000000..b5759b14d61 --- /dev/null +++ b/queue-4.14/cifs-do-not-reconnect-tcp-session-in-add_credits.patch @@ -0,0 +1,145 @@ +From ef68e831840c40c7d01b328b3c0f5d8c4796c232 Mon Sep 17 00:00:00 2001 +From: Pavel Shilovsky +Date: Fri, 18 Jan 2019 17:25:36 -0800 +Subject: CIFS: Do not reconnect TCP session in add_credits() + +From: Pavel Shilovsky + +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: +Signed-off-by: Pavel Shilovsky +Reviewed-by: Ronnie Sahlberg +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -524,6 +524,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) + { +@@ -536,6 +551,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 +@@ -33,6 +33,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) + { +@@ -40,17 +41,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; +@@ -63,14 +62,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; +@@ -94,8 +94,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 diff --git a/queue-4.14/cifs-fix-credit-calculation-for-encrypted-reads-with-errors.patch b/queue-4.14/cifs-fix-credit-calculation-for-encrypted-reads-with-errors.patch new file mode 100644 index 00000000000..23e308d8862 --- /dev/null +++ b/queue-4.14/cifs-fix-credit-calculation-for-encrypted-reads-with-errors.patch @@ -0,0 +1,65 @@ +From ec678eae746dd25766a61c4095e2b649d3b20b09 Mon Sep 17 00:00:00 2001 +From: Pavel Shilovsky +Date: Fri, 18 Jan 2019 15:38:11 -0800 +Subject: CIFS: Fix credit calculation for encrypted reads with errors + +From: Pavel Shilovsky + +commit ec678eae746dd25766a61c4095e2b649d3b20b09 upstream. + +We do need to account for credits received in error responses +to read requests on encrypted sessions. + +Cc: +Signed-off-by: Pavel Shilovsky +Reviewed-by: Ronnie Sahlberg +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman + +--- + fs/cifs/smb2ops.c | 24 ++++++++++++++---------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -2531,11 +2531,23 @@ handle_read_data(struct TCP_Server_Info + server->ops->is_status_pending(buf, server, 0)) + return -1; + +- rdata->result = server->ops->map_error(buf, false); ++ /* set up first two iov to get credits */ ++ rdata->iov[0].iov_base = buf; ++ rdata->iov[0].iov_len = 4; ++ rdata->iov[1].iov_base = buf + 4; ++ rdata->iov[1].iov_len = ++ min_t(unsigned int, buf_len, server->vals->read_rsp_size) - 4; ++ cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", ++ rdata->iov[0].iov_base, rdata->iov[0].iov_len); ++ cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", ++ rdata->iov[1].iov_base, rdata->iov[1].iov_len); ++ ++ rdata->result = server->ops->map_error(buf, true); + if (rdata->result != 0) { + cifs_dbg(FYI, "%s: server returned error %d\n", + __func__, rdata->result); +- dequeue_mid(mid, rdata->result); ++ /* normal error on read response */ ++ dequeue_mid(mid, false); + return 0; + } + +@@ -2605,14 +2617,6 @@ handle_read_data(struct TCP_Server_Info + return 0; + } + +- /* set up first iov for signature check */ +- rdata->iov[0].iov_base = buf; +- rdata->iov[0].iov_len = 4; +- rdata->iov[1].iov_base = buf + 4; +- rdata->iov[1].iov_len = server->vals->read_rsp_size - 4; +- cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", +- rdata->iov[0].iov_base, server->vals->read_rsp_size); +- + length = rdata->copy_into_pages(server, rdata, &iter); + + kfree(bvec); diff --git a/queue-4.14/cifs-fix-credits-calculations-for-reads-with-errors.patch b/queue-4.14/cifs-fix-credits-calculations-for-reads-with-errors.patch new file mode 100644 index 00000000000..5c04987abdb --- /dev/null +++ b/queue-4.14/cifs-fix-credits-calculations-for-reads-with-errors.patch @@ -0,0 +1,96 @@ +From 8004c78c68e894e4fd5ac3c22cc22eb7dc24cabc Mon Sep 17 00:00:00 2001 +From: Pavel Shilovsky +Date: Thu, 17 Jan 2019 15:29:26 -0800 +Subject: CIFS: Fix credits calculations for reads with errors + +From: Pavel Shilovsky + +commit 8004c78c68e894e4fd5ac3c22cc22eb7dc24cabc upstream. + +Currently we mark MID as malformed if we get an error from server +in a read response. This leads to not properly processing credits +in the readv callback. Fix this by marking such a response as +normal received response and process it appropriately. + +Cc: +Signed-off-by: Pavel Shilovsky +Reviewed-by: Ronnie Sahlberg +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman + +--- + fs/cifs/cifssmb.c | 35 +++++++++++++++++++++++------------ + 1 file changed, 23 insertions(+), 12 deletions(-) + +--- a/fs/cifs/cifssmb.c ++++ b/fs/cifs/cifssmb.c +@@ -1445,18 +1445,26 @@ cifs_discard_remaining_data(struct TCP_S + } + + static int +-cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) ++__cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid, ++ bool malformed) + { + int length; +- struct cifs_readdata *rdata = mid->callback_data; + + length = cifs_discard_remaining_data(server); +- dequeue_mid(mid, rdata->result); ++ dequeue_mid(mid, malformed); + mid->resp_buf = server->smallbuf; + server->smallbuf = NULL; + return length; + } + ++static int ++cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) ++{ ++ struct cifs_readdata *rdata = mid->callback_data; ++ ++ return __cifs_readv_discard(server, mid, rdata->result); ++} ++ + int + cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) + { +@@ -1496,12 +1504,23 @@ cifs_readv_receive(struct TCP_Server_Inf + return -1; + } + ++ /* set up first two iov for signature check and to get credits */ ++ rdata->iov[0].iov_base = buf; ++ rdata->iov[0].iov_len = 4; ++ rdata->iov[1].iov_base = buf + 4; ++ rdata->iov[1].iov_len = server->total_read - 4; ++ cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n", ++ rdata->iov[0].iov_base, rdata->iov[0].iov_len); ++ cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n", ++ rdata->iov[1].iov_base, rdata->iov[1].iov_len); ++ + /* Was the SMB read successful? */ + rdata->result = server->ops->map_error(buf, false); + if (rdata->result != 0) { + cifs_dbg(FYI, "%s: server returned error %d\n", + __func__, rdata->result); +- return cifs_readv_discard(server, mid); ++ /* normal error on read response */ ++ return __cifs_readv_discard(server, mid, false); + } + + /* Is there enough to get to the rest of the READ_RSP header? */ +@@ -1544,14 +1563,6 @@ cifs_readv_receive(struct TCP_Server_Inf + server->total_read += length; + } + +- /* set up first iov for signature check */ +- rdata->iov[0].iov_base = buf; +- rdata->iov[0].iov_len = 4; +- rdata->iov[1].iov_base = buf + 4; +- rdata->iov[1].iov_len = server->total_read - 4; +- cifs_dbg(FYI, "0: iov_base=%p iov_len=%u\n", +- rdata->iov[0].iov_base, server->total_read); +- + /* how much data is in the response? */ + data_len = server->ops->read_data_length(buf); + if (data_offset + data_len > buflen) { diff --git a/queue-4.14/cifs-fix-possible-hang-during-async-mtu-reads-and-writes.patch b/queue-4.14/cifs-fix-possible-hang-during-async-mtu-reads-and-writes.patch new file mode 100644 index 00000000000..4da936d3f6a --- /dev/null +++ b/queue-4.14/cifs-fix-possible-hang-during-async-mtu-reads-and-writes.patch @@ -0,0 +1,55 @@ +From acc58d0bab55a50e02c25f00bd6a210ee121595f Mon Sep 17 00:00:00 2001 +From: Pavel Shilovsky +Date: Thu, 17 Jan 2019 08:21:24 -0800 +Subject: CIFS: Fix possible hang during async MTU reads and writes + +From: Pavel Shilovsky + +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: +Signed-off-by: Pavel Shilovsky +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman + +--- + fs/cifs/smb2ops.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -153,14 +153,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); + diff --git a/queue-4.14/series b/queue-4.14/series index 10714ad207c..e91022135ca 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -27,3 +27,7 @@ uart-fix-crash-in-uart_write-and-uart_put_char.patch tty-n_hdlc-fix-__might_sleep-warning.patch hv_balloon-avoid-touching-uninitialized-struct-page-during-tail-onlining.patch drivers-hv-vmbus-check-for-ring-when-getting-debug-info.patch +cifs-fix-possible-hang-during-async-mtu-reads-and-writes.patch +cifs-fix-credits-calculations-for-reads-with-errors.patch +cifs-fix-credit-calculation-for-encrypted-reads-with-errors.patch +cifs-do-not-reconnect-tcp-session-in-add_credits.patch