cifs: avoid race conditions with parallel reconnects
[linux-2.6-microblaze.git] / fs / cifs / smb2transport.c
index d827b75..790acf6 100644 (file)
@@ -81,6 +81,7 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
        struct cifs_ses *ses = NULL;
        int i;
        int rc = 0;
+       bool is_binding = false;
 
        spin_lock(&cifs_tcp_ses_lock);
 
@@ -97,9 +98,12 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
        goto out;
 
 found:
+       spin_lock(&ses->ses_lock);
        spin_lock(&ses->chan_lock);
-       if (cifs_chan_needs_reconnect(ses, server) &&
-           !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
+
+       is_binding = (cifs_chan_needs_reconnect(ses, server) &&
+                     ses->ses_status == SES_GOOD);
+       if (is_binding) {
                /*
                 * If we are in the process of binding a new channel
                 * to an existing session, use the master connection
@@ -107,6 +111,7 @@ found:
                 */
                memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
                spin_unlock(&ses->chan_lock);
+               spin_unlock(&ses->ses_lock);
                goto out;
        }
 
@@ -119,10 +124,12 @@ found:
                if (chan->server == server) {
                        memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE);
                        spin_unlock(&ses->chan_lock);
+                       spin_unlock(&ses->ses_lock);
                        goto out;
                }
        }
        spin_unlock(&ses->chan_lock);
+       spin_unlock(&ses->ses_lock);
 
        cifs_dbg(VFS,
                 "%s: Could not find channel signing key for session 0x%llx\n",
@@ -392,11 +399,15 @@ generate_smb3signingkey(struct cifs_ses *ses,
        bool is_binding = false;
        int chan_index = 0;
 
+       spin_lock(&ses->ses_lock);
        spin_lock(&ses->chan_lock);
-       is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
+       is_binding = (cifs_chan_needs_reconnect(ses, server) &&
+                     ses->ses_status == SES_GOOD);
+
        chan_index = cifs_ses_get_chan_index(ses, server);
        /* TODO: introduce ref counting for channels when the can be freed */
        spin_unlock(&ses->chan_lock);
+       spin_unlock(&ses->ses_lock);
 
        /*
         * All channels use the same encryption/decryption keys but