cifs: make sure that channel scaling is done only once

Following a successful cifs_tree_connect, we have the code
to scale up/down the number of channels in the session.
However, it is not protected by a lock today.

As a result, this code can be executed by several processes
that select the same channel. The core functions handle this
well, as they pick chan_lock. However, we've seen cases where
smb2_reconnect throws some warnings.

To fix that, this change introduces a flags bitmap inside the
cifs_ses structure. A new flag type is used to ensure that
only one process enters this section at any time.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by Shyam Prasad N and committed by Steve French ee36a3b3 41bccc98

+20 -3
+3
fs/smb/client/cifsglob.h
··· 1032 __u8 signkey[SMB3_SIGN_KEY_SIZE]; 1033 }; 1034 1035 /* 1036 * Session structure. One of these for each uid session with a particular host 1037 */ ··· 1066 enum securityEnum sectype; /* what security flavor was specified? */ 1067 bool sign; /* is signing required? */ 1068 bool domainAuto:1; 1069 __u16 session_flags; 1070 __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; 1071 __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
··· 1032 __u8 signkey[SMB3_SIGN_KEY_SIZE]; 1033 }; 1034 1035 + #define CIFS_SES_FLAG_SCALE_CHANNELS (0x1) 1036 + 1037 /* 1038 * Session structure. One of these for each uid session with a particular host 1039 */ ··· 1064 enum securityEnum sectype; /* what security flavor was specified? */ 1065 bool sign; /* is signing required? */ 1066 bool domainAuto:1; 1067 + unsigned int flags; 1068 __u16 session_flags; 1069 __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; 1070 __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
+17 -3
fs/smb/client/smb2pdu.c
··· 399 goto out; 400 } 401 402 if (!rc && 403 (server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { 404 mutex_unlock(&ses->session_mutex); ··· 437 if (ses->chan_max > ses->chan_count && 438 ses->iface_count && 439 !SERVER_IS_CHAN(server)) { 440 - if (ses->chan_count == 1) 441 cifs_server_dbg(VFS, "supports multichannel now\n"); 442 443 cifs_try_adding_channels(ses); 444 - queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, 445 - (SMB_INTERFACE_POLL_INTERVAL * HZ)); 446 } 447 } else { 448 mutex_unlock(&ses->session_mutex); 449 } 450 skip_add_channels: 451 452 if (smb2_command != SMB2_INTERNAL_CMD) 453 mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
··· 399 goto out; 400 } 401 402 + spin_lock(&ses->ses_lock); 403 + if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) { 404 + spin_unlock(&ses->ses_lock); 405 + mutex_unlock(&ses->session_mutex); 406 + goto skip_add_channels; 407 + } 408 + ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS; 409 + spin_unlock(&ses->ses_lock); 410 + 411 if (!rc && 412 (server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { 413 mutex_unlock(&ses->session_mutex); ··· 428 if (ses->chan_max > ses->chan_count && 429 ses->iface_count && 430 !SERVER_IS_CHAN(server)) { 431 + if (ses->chan_count == 1) { 432 cifs_server_dbg(VFS, "supports multichannel now\n"); 433 + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, 434 + (SMB_INTERFACE_POLL_INTERVAL * HZ)); 435 + } 436 437 cifs_try_adding_channels(ses); 438 } 439 } else { 440 mutex_unlock(&ses->session_mutex); 441 } 442 + 443 skip_add_channels: 444 + spin_lock(&ses->ses_lock); 445 + ses->flags &= ~CIFS_SES_FLAG_SCALE_CHANNELS; 446 + spin_unlock(&ses->ses_lock); 447 448 if (smb2_command != SMB2_INTERNAL_CMD) 449 mod_delayed_work(cifsiod_wq, &server->reconnect, 0);