struct list_head *tmp;
        struct list_head *tmp1;
 
+       /* only send once per connect */
+       spin_lock(&cifs_tcp_ses_lock);
+       if (tcon->ses->status != CifsGood ||
+           tcon->tidStatus != CifsNeedReconnect) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               return;
+       }
+       tcon->tidStatus = CifsInFilesInvalidate;
+       spin_unlock(&cifs_tcp_ses_lock);
+
        /* list all files open on tree connection and mark them invalid */
        spin_lock(&tcon->open_file_lock);
        list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
        memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
        mutex_unlock(&tcon->crfid.fid_mutex);
 
+       spin_lock(&cifs_tcp_ses_lock);
+       if (tcon->tidStatus == CifsInFilesInvalidate)
+               tcon->tidStatus = CifsNeedTcon;
+       spin_unlock(&cifs_tcp_ses_lock);
+
        /*
         * BB Add call to invalidate_inodes(sb) for all superblocks mounted
         * to this tcon.
 
        nls_codepage = load_nls_default();
 
-       /*
-        * need to prevent multiple threads trying to simultaneously
-        * reconnect the same SMB session
-        */
-       mutex_lock(&ses->session_mutex);
-
        /*
         * Recheck after acquire mutex. If another thread is negotiating
         * and the server never sends an answer the socket will be closed
        if (server->tcpStatus == CifsNeedReconnect) {
                spin_unlock(&cifs_tcp_ses_lock);
                rc = -EHOSTDOWN;
-               mutex_unlock(&ses->session_mutex);
                goto out;
        }
        spin_unlock(&cifs_tcp_ses_lock);
                        goto skip_sess_setup;
 
                rc = -EHOSTDOWN;
-               mutex_unlock(&ses->session_mutex);
                goto out;
        }
        spin_unlock(&ses->chan_lock);
 
+       mutex_lock(&ses->session_mutex);
        rc = cifs_negotiate_protocol(0, ses, server);
        if (!rc)
                rc = cifs_setup_session(0, ses, server, nls_codepage);
 
                if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses))
                        goto next_session;
 
+               ses->status = CifsNeedReconnect;
                num_sessions++;
 
-               list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
+               list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
                        tcon->need_reconnect = true;
+                       tcon->tidStatus = CifsNeedReconnect;
+               }
                if (ses->tcon_ipc)
                        ses->tcon_ipc->need_reconnect = true;
 
                cifs_dbg(FYI, "Existing smb sess found (status=%d)\n",
                         ses->status);
 
-               mutex_lock(&ses->session_mutex);
                spin_lock(&ses->chan_lock);
                if (cifs_chan_needs_reconnect(ses, server)) {
                        spin_unlock(&ses->chan_lock);
                        cifs_dbg(FYI, "Session needs reconnect\n");
 
+                       mutex_lock(&ses->session_mutex);
                        rc = cifs_negotiate_protocol(xid, ses, server);
                        if (rc) {
                                mutex_unlock(&ses->session_mutex);
                                free_xid(xid);
                                return ERR_PTR(rc);
                        }
+                       mutex_unlock(&ses->session_mutex);
+
                        spin_lock(&ses->chan_lock);
                }
                spin_unlock(&ses->chan_lock);
-               mutex_unlock(&ses->session_mutex);
 
                /* existing SMB ses has a server reference already */
                cifs_put_tcp_session(server, 0);
 
        ses->sectype = ctx->sectype;
        ses->sign = ctx->sign;
-       mutex_lock(&ses->session_mutex);
 
        /* add server as first channel */
        spin_lock(&ses->chan_lock);
        ses->chans_need_reconnect = 1;
        spin_unlock(&ses->chan_lock);
 
+       mutex_lock(&ses->session_mutex);
        rc = cifs_negotiate_protocol(xid, ses, server);
        if (!rc)
                rc = cifs_setup_session(xid, ses, server, ctx->local_nls);
+       mutex_unlock(&ses->session_mutex);
 
        /* each channel uses a different signing key */
        memcpy(ses->chans[0].signkey, ses->smb3signingkey,
               sizeof(ses->smb3signingkey));
 
-       mutex_unlock(&ses->session_mutex);
        if (rc)
                goto get_ses_fail;
 
                }
        }
 
-       /*
-        * BB Do we need to wrap session_mutex around this TCon call and Unix
-        * SetFS as we do on SessSetup and reconnect?
-        */
        xid = get_xid();
        rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon,
                                            ctx->local_nls);
                return -ENOSYS;
 
        /* only send once per connect */
-       if (!server->ops->need_neg(server))
+       spin_lock(&cifs_tcp_ses_lock);
+       if (!server->ops->need_neg(server) ||
+           server->tcpStatus != CifsNeedNegotiate) {
+               spin_unlock(&cifs_tcp_ses_lock);
                return 0;
+       }
+       server->tcpStatus = CifsInNegotiate;
+       spin_unlock(&cifs_tcp_ses_lock);
 
        rc = server->ops->negotiate(xid, ses, server);
        if (rc == 0) {
                spin_lock(&cifs_tcp_ses_lock);
-               if (server->tcpStatus == CifsNeedNegotiate)
-                       server->tcpStatus = CifsGood;
+               if (server->tcpStatus == CifsInNegotiate)
+                       server->tcpStatus = CifsNeedSessSetup;
                else
                        rc = -EHOSTDOWN;
                spin_unlock(&cifs_tcp_ses_lock);
        int rc = -ENOSYS;
        bool is_binding = false;
 
+       /* only send once per connect */
+       spin_lock(&cifs_tcp_ses_lock);
+       if (server->tcpStatus != CifsNeedSessSetup) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               return 0;
+       }
+       ses->status = CifsInSessSetup;
+       spin_unlock(&cifs_tcp_ses_lock);
+
        spin_lock(&ses->chan_lock);
        is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
        spin_unlock(&ses->chan_lock);
        struct dfs_cache_tgt_iterator *tit;
        bool target_match;
 
+       /* only send once per connect */
+       spin_lock(&cifs_tcp_ses_lock);
+       if (tcon->ses->status != CifsGood ||
+           (tcon->tidStatus != CifsNew &&
+           tcon->tidStatus != CifsNeedTcon)) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               return 0;
+       }
+       tcon->tidStatus = CifsInTcon;
+       spin_unlock(&cifs_tcp_ses_lock);
+
        extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
 
        tit = dfs_cache_get_tgt_iterator(tl);
 {
        const struct smb_version_operations *ops = tcon->ses->server->ops;
 
+       /* only send once per connect */
+       spin_lock(&cifs_tcp_ses_lock);
+       if (tcon->ses->status != CifsGood ||
+           (tcon->tidStatus != CifsNew &&
+           tcon->tidStatus != CifsNeedTcon)) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               return 0;
+       }
+       tcon->tidStatus = CifsInTcon;
+       spin_unlock(&cifs_tcp_ses_lock);
+
        return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
 }
 #endif
 
 
        chan_server = cifs_get_tcp_session(&ctx, ses->server);
 
-       mutex_lock(&ses->session_mutex);
        spin_lock(&ses->chan_lock);
        chan = &ses->chans[ses->chan_count];
        chan->server = chan_server;
 
        spin_unlock(&ses->chan_lock);
 
+       mutex_lock(&ses->session_mutex);
        /*
         * We need to allocate the server crypto now as we will need
         * to sign packets before we generate the channel signing key
        rc = smb311_crypto_shash_allocate(chan->server);
        if (rc) {
                cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
+               mutex_unlock(&ses->session_mutex);
                goto out;
        }
 
        if (!rc)
                rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls);
 
+       mutex_unlock(&ses->session_mutex);
+
 out:
        if (rc && chan->server) {
                spin_lock(&ses->chan_lock);
                spin_unlock(&ses->chan_lock);
        }
 
-       mutex_unlock(&ses->session_mutex);
-
        if (rc && chan->server)
                cifs_put_tcp_session(chan->server, 0);
 
 
        /* Even if one channel is active, session is in good state */
        spin_lock(&cifs_tcp_ses_lock);
+       server->tcpStatus = CifsGood;
        ses->status = CifsGood;
        spin_unlock(&cifs_tcp_ses_lock);
 
 
 
        nls_codepage = load_nls_default();
 
-       /*
-        * need to prevent multiple threads trying to simultaneously reconnect
-        * the same SMB session
-        */
-       mutex_lock(&ses->session_mutex);
-
        /*
         * Recheck after acquire mutex. If another thread is negotiating
         * and the server never sends an answer the socket will be closed
        if (server->tcpStatus == CifsNeedReconnect) {
                spin_unlock(&cifs_tcp_ses_lock);
                rc = -EHOSTDOWN;
-               mutex_unlock(&ses->session_mutex);
                goto out;
        }
        spin_unlock(&cifs_tcp_ses_lock);
                        goto skip_sess_setup;
 
                rc = -EHOSTDOWN;
-               mutex_unlock(&ses->session_mutex);
                goto out;
        }
        spin_unlock(&ses->chan_lock);
 
+       mutex_lock(&ses->session_mutex);
        rc = cifs_negotiate_protocol(0, ses, server);
        if (!rc) {
                rc = cifs_setup_session(0, ses, server, nls_codepage);
                if ((rc == -EACCES) && !tcon->retry) {
-                       rc = -EHOSTDOWN;
                        mutex_unlock(&ses->session_mutex);
+                       rc = -EHOSTDOWN;
                        goto failed;
                }
        }
 
        if (rc || !tcon->need_reconnect) {
-               mutex_unlock(&tcon->ses->session_mutex);
+               mutex_unlock(&ses->session_mutex);
                goto out;
        }
 
                tcon->need_reopen_files = true;
 
        rc = cifs_tree_connect(0, tcon, nls_codepage);
-       mutex_unlock(&tcon->ses->session_mutex);
+       mutex_unlock(&ses->session_mutex);
 
        cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
        if (rc) {
 
        /* Even if one channel is active, session is in good state */
        spin_lock(&cifs_tcp_ses_lock);
+       server->tcpStatus = CifsGood;
        ses->status = CifsGood;
        spin_unlock(&cifs_tcp_ses_lock);