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