diff options
author | Shyam Prasad N <sprasad@microsoft.com> | 2021-07-19 19:37:52 +0200 |
---|---|---|
committer | Steve French <stfrench@microsoft.com> | 2022-01-08 03:09:22 +0100 |
commit | 73f9bfbe3d818bb52266d5c9f3ba57d97842ffe7 (patch) | |
tree | d2cd41b96bddfe592b5a6e9bfd1eb54223947657 /fs/cifs/sess.c | |
parent | cifs: fix hang on cifs_get_next_mid() (diff) | |
download | linux-73f9bfbe3d818bb52266d5c9f3ba57d97842ffe7.tar.xz linux-73f9bfbe3d818bb52266d5c9f3ba57d97842ffe7.zip |
cifs: maintain a state machine for tcp/smb/tcon sessions
If functions like cifs_negotiate_protocol, cifs_setup_session,
cifs_tree_connect are called in parallel on different channels,
each of these will be execute the requests. This maybe unnecessary
in some cases, and only the first caller may need to do the work.
This is achieved by having more states for the tcp/smb/tcon session
status fields. And tracking the state of reconnection based on the
state machine.
For example:
for tcp connections:
CifsNew/CifsNeedReconnect ->
CifsNeedNegotiate ->
CifsInNegotiate ->
CifsNeedSessSetup ->
CifsInSessSetup ->
CifsGood
for smb sessions:
CifsNew/CifsNeedReconnect ->
CifsGood
for tcon:
CifsNew/CifsNeedReconnect ->
CifsInFilesInvalidate ->
CifsNeedTcon ->
CifsInTcon ->
CifsGood
If any channel reconnect sees that it's in the middle of
transition to CifsGood, then they can skip the function.
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/cifs/sess.c')
-rw-r--r-- | fs/cifs/sess.c | 8 |
1 files changed, 5 insertions, 3 deletions
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); |