]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
cifs: prevent data race in cifs_reconnect_tcon()
authorPaulo Alcantara <pc@manguebit.com>
Tue, 28 Feb 2023 22:01:55 +0000 (19:01 -0300)
committerRoxana Nicolescu <roxana.nicolescu@canonical.com>
Wed, 17 May 2023 11:33:41 +0000 (13:33 +0200)
BugLink: https://bugs.launchpad.net/bugs/2016878
[ Upstream commit 1bcd548d935a33c6fc58331405eb1b82fd6150de ]

Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior
to waiting the server to be reconnected in cifs_reconnect_tcon().  It
is set in cifs_tcp_ses_needs_reconnect() and protected by
TCP_Server_Info::srv_lock.

Create a new cifs_wait_for_server_reconnect() helper that can be used
by both SMB2+ and CIFS reconnect code.

Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Stable-dep-of: bc962159e8e3 ("cifs: avoid race conditions with parallel reconnects")
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/misc.c
fs/cifs/smb2pdu.c

index e75184544ecb443cec86293a00bff40d5ef6dfc1..639df85dafd6c40de9330f8c5871e5478d011364 100644 (file)
@@ -697,5 +697,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
 
 struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
 void cifs_put_tcon_super(struct super_block *sb);
+int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
 
 #endif                 /* _CIFSPROTO_H */
index 566e6a26b897c074b1001227c5d58921d1e7ca5c..5ca4a5383aaaefff074bfb7aa5becfa807de4c9e 100644 (file)
@@ -70,7 +70,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
        struct cifs_ses *ses;
        struct TCP_Server_Info *server;
        struct nls_table *nls_codepage;
-       int retries;
 
        /*
         * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
@@ -98,45 +97,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
        }
        spin_unlock(&tcon->tc_lock);
 
-       retries = server->nr_targets;
-
-       /*
-        * Give demultiplex thread up to 10 seconds to each target available for
-        * reconnect -- should be greater than cifs socket timeout which is 7
-        * seconds.
-        */
-       while (server->tcpStatus == CifsNeedReconnect) {
-               rc = wait_event_interruptible_timeout(server->response_q,
-                                                     (server->tcpStatus != CifsNeedReconnect),
-                                                     10 * HZ);
-               if (rc < 0) {
-                       cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
-                                __func__);
-                       return -ERESTARTSYS;
-               }
-
-               /* are we still trying to reconnect? */
-               spin_lock(&server->srv_lock);
-               if (server->tcpStatus != CifsNeedReconnect) {
-                       spin_unlock(&server->srv_lock);
-                       break;
-               }
-               spin_unlock(&server->srv_lock);
-
-               if (retries && --retries)
-                       continue;
-
-               /*
-                * on "soft" mounts we wait once. Hard mounts keep
-                * retrying until process is killed or server comes
-                * back on-line
-                */
-               if (!tcon->retry) {
-                       cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
-                       return -EHOSTDOWN;
-               }
-               retries = server->nr_targets;
-       }
+       rc = cifs_wait_for_server_reconnect(server, tcon->retry);
+       if (rc)
+               return rc;
 
        spin_lock(&ses->chan_lock);
        if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
index 9f4486b705d5c6cea4959a8c55cc1a1d955e182f..5542893ef03f718ce9f44ef97ae606aa1de1b4d8 100644 (file)
@@ -1376,3 +1376,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid,
        return 0;
 }
 #endif
+
+int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry)
+{
+       int timeout = 10;
+       int rc;
+
+       spin_lock(&server->srv_lock);
+       if (server->tcpStatus != CifsNeedReconnect) {
+               spin_unlock(&server->srv_lock);
+               return 0;
+       }
+       timeout *= server->nr_targets;
+       spin_unlock(&server->srv_lock);
+
+       /*
+        * Give demultiplex thread up to 10 seconds to each target available for
+        * reconnect -- should be greater than cifs socket timeout which is 7
+        * seconds.
+        *
+        * On "soft" mounts we wait once. Hard mounts keep retrying until
+        * process is killed or server comes back on-line.
+        */
+       do {
+               rc = wait_event_interruptible_timeout(server->response_q,
+                                                     (server->tcpStatus != CifsNeedReconnect),
+                                                     timeout * HZ);
+               if (rc < 0) {
+                       cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
+                                __func__);
+                       return -ERESTARTSYS;
+               }
+
+               /* are we still trying to reconnect? */
+               spin_lock(&server->srv_lock);
+               if (server->tcpStatus != CifsNeedReconnect) {
+                       spin_unlock(&server->srv_lock);
+                       return 0;
+               }
+               spin_unlock(&server->srv_lock);
+       } while (retry);
+
+       cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
+       return -EHOSTDOWN;
+}
index 6e6e44d8b4c7943528df644ece466c142e788979..83d04cd2f9df8f0a7885cfe2fd59ae5bd575463c 100644 (file)
@@ -139,66 +139,6 @@ out:
        return;
 }
 
-static int wait_for_server_reconnect(struct TCP_Server_Info *server,
-                                    __le16 smb2_command, bool retry)
-{
-       int timeout = 10;
-       int rc;
-
-       spin_lock(&server->srv_lock);
-       if (server->tcpStatus != CifsNeedReconnect) {
-               spin_unlock(&server->srv_lock);
-               return 0;
-       }
-       timeout *= server->nr_targets;
-       spin_unlock(&server->srv_lock);
-
-       /*
-        * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
-        * here since they are implicitly done when session drops.
-        */
-       switch (smb2_command) {
-       /*
-        * BB Should we keep oplock break and add flush to exceptions?
-        */
-       case SMB2_TREE_DISCONNECT:
-       case SMB2_CANCEL:
-       case SMB2_CLOSE:
-       case SMB2_OPLOCK_BREAK:
-               return -EAGAIN;
-       }
-
-       /*
-        * Give demultiplex thread up to 10 seconds to each target available for
-        * reconnect -- should be greater than cifs socket timeout which is 7
-        * seconds.
-        *
-        * On "soft" mounts we wait once. Hard mounts keep retrying until
-        * process is killed or server comes back on-line.
-        */
-       do {
-               rc = wait_event_interruptible_timeout(server->response_q,
-                                                     (server->tcpStatus != CifsNeedReconnect),
-                                                     timeout * HZ);
-               if (rc < 0) {
-                       cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
-                                __func__);
-                       return -ERESTARTSYS;
-               }
-
-               /* are we still trying to reconnect? */
-               spin_lock(&server->srv_lock);
-               if (server->tcpStatus != CifsNeedReconnect) {
-                       spin_unlock(&server->srv_lock);
-                       return 0;
-               }
-               spin_unlock(&server->srv_lock);
-       } while (retry);
-
-       cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
-       return -EHOSTDOWN;
-}
-
 static int
 smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
               struct TCP_Server_Info *server)
@@ -239,7 +179,27 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
            (!tcon->ses->server) || !server)
                return -EIO;
 
-       rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
+       spin_lock(&server->srv_lock);
+       if (server->tcpStatus == CifsNeedReconnect) {
+               /*
+                * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
+                * here since they are implicitly done when session drops.
+                */
+               switch (smb2_command) {
+               /*
+                * BB Should we keep oplock break and add flush to exceptions?
+                */
+               case SMB2_TREE_DISCONNECT:
+               case SMB2_CANCEL:
+               case SMB2_CLOSE:
+               case SMB2_OPLOCK_BREAK:
+                       spin_unlock(&server->srv_lock);
+                       return -EAGAIN;
+               }
+       }
+       spin_unlock(&server->srv_lock);
+
+       rc = cifs_wait_for_server_reconnect(server, tcon->retry);
        if (rc)
                return rc;