cifs: Fix a race condition with cifs_echo_request [Linux 4.14.137]

This Linux kernel change "cifs: Fix a race condition with cifs_echo_request" is included in the Linux 4.14.137 release. This change is authored by Ronnie Sahlberg <lsahlber [at] redhat.com> on Sat Jul 6 06:52:46 2019 +1000. The commit for this change in Linux stable tree is 443aeb7 (patch) which is from upstream commit f2caf90. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream f2caf90.

cifs: Fix a race condition with cifs_echo_request

[ Upstream commit f2caf901c1b7ce65f9e6aef4217e3241039db768 ]

There is a race condition with how we send (or supress and don't send)
smb echos that will cause the client to incorrectly think the
server is unresponsive and thus needs to be reconnected.

Summary of the race condition:
 1) Daisy chaining scheduling creates a gap.
 2) If traffic comes unfortunate shortly after
    the last echo, the planned echo is suppressed.
 3) Due to the gap, the next echo transmission is delayed
    until after the timeout, which is set hard to twice
    the echo interval.

This is fixed by changing the timeouts from 2 to three times the echo interval.

Detailed description of the bug: https://lutz.donnerhacke.de/eng/Blog/Groundhog-Day-with-SMB-remount

Signed-off-by: Ronnie Sahlberg <[email protected]>
Reviewed-by: Pavel Shilovsky <[email protected]>
Signed-off-by: Steve French <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>

There are 8 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 fs/cifs/connect.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 33cd844..57c62ff 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -554,10 +554,10 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
 server_unresponsive(struct TCP_Server_Info *server)
 {
    /*
-    * We need to wait 2 echo intervals to make sure we handle such
+    * We need to wait 3 echo intervals to make sure we handle such
     * situations right:
     * 1s  client sends a normal SMB request
-    * 2s  client gets a response
+    * 3s  client gets a response
     * 30s echo workqueue job pops, and decides we got a response recently
     *     and don't need to send another
     * ...
@@ -566,9 +566,9 @@ static inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
     */
    if ((server->tcpStatus == CifsGood ||
        server->tcpStatus == CifsNeedNegotiate) &&
-       time_after(jiffies, server->lstrp + 2 * server->echo_interval)) {
+       time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
        cifs_dbg(VFS, "Server %s has not responded in %lu seconds. Reconnecting...\n",
-            server->hostname, (2 * server->echo_interval) / HZ);
+            server->hostname, (3 * server->echo_interval) / HZ);
        cifs_reconnect(server);
        wake_up(&server->response_q);
        return true;

Leave a Reply

Your email address will not be published. Required fields are marked *