diff options
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsglob.h | 8 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 24 | ||||
-rw-r--r-- | fs/cifs/connect.c | 61 | ||||
-rw-r--r-- | fs/cifs/sess.c | 8 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 16 |
5 files changed, 82 insertions, 35 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4ba35faff79c..f88d2b10045a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -113,7 +113,13 @@ enum statusEnum { CifsGood, CifsExiting, CifsNeedReconnect, - CifsNeedNegotiate + CifsNeedNegotiate, + CifsInNegotiate, + CifsNeedSessSetup, + CifsInSessSetup, + CifsNeedTcon, + CifsInTcon, + CifsInFilesInvalidate }; enum securityEnum { diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3ef2796e2f24..071e2f21a7db 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -73,6 +73,16 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) 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) { @@ -89,6 +99,11 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) 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. @@ -183,12 +198,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) 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 * and tcpStatus set to reconnect. @@ -197,7 +206,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) 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); @@ -215,11 +223,11 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) 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); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 815f629933de..a408187c7002 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -208,10 +208,13 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, 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; @@ -2035,12 +2038,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) 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); @@ -2059,10 +2062,11 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) 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); @@ -2112,7 +2116,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ses->sectype = ctx->sectype; ses->sign = ctx->sign; - mutex_lock(&ses->session_mutex); /* add server as first channel */ spin_lock(&ses->chan_lock); @@ -2122,15 +2125,16 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) 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; @@ -2347,10 +2351,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) } } - /* - * 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); @@ -3870,14 +3870,20 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses, 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); @@ -3894,6 +3900,15 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, 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); @@ -4264,6 +4279,17 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t 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); @@ -4422,6 +4448,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru { 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 diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 03ba30843950..d12490e12be5 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -308,7 +308,6 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, 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; @@ -326,6 +325,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, 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 @@ -334,6 +334,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, 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; } @@ -341,6 +342,8 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, 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); @@ -355,8 +358,6 @@ out: spin_unlock(&ses->chan_lock); } - mutex_unlock(&ses->session_mutex); - if (rc && chan->server) cifs_put_tcp_session(chan->server, 0); @@ -1053,6 +1054,7 @@ sess_establish_session(struct sess_data *sess_data) /* 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); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 0c18b6f4f9eb..2725e62470e4 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -252,12 +252,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *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 * and tcpStatus set to reconnect. @@ -266,7 +260,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, 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); @@ -284,23 +277,23 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, 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; } @@ -310,7 +303,7 @@ skip_sess_setup: 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) { @@ -1397,6 +1390,7 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) /* 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); |