mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-23 08:35:19 -05:00
cifs: avoid parallel session setups on same channel
After allowing channels to reconnect in parallel, it now becomes important to take care that multiple processes do not call negotiate/session setup in parallel on the same channel. This change avoids that by marking a channel as "in_reconnect". During session setup if the channel in question has this flag set, we return immediately. Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
dd3cd8709e
commit
5752bf645f
5 changed files with 65 additions and 7 deletions
|
@ -419,6 +419,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
|||
spin_lock(&ses->chan_lock);
|
||||
if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0))
|
||||
seq_puts(m, "\tPrimary channel: DISCONNECTED ");
|
||||
if (CIFS_CHAN_IN_RECONNECT(ses, 0))
|
||||
seq_puts(m, "\t[RECONNECTING] ");
|
||||
|
||||
if (ses->chan_count > 1) {
|
||||
seq_printf(m, "\n\n\tExtra Channels: %zu ",
|
||||
|
@ -427,6 +429,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
|||
cifs_dump_channel(m, j, &ses->chans[j]);
|
||||
if (CIFS_CHAN_NEEDS_RECONNECT(ses, j))
|
||||
seq_puts(m, "\tDISCONNECTED ");
|
||||
if (CIFS_CHAN_IN_RECONNECT(ses, j))
|
||||
seq_puts(m, "\t[RECONNECTING] ");
|
||||
}
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
|
|
@ -922,6 +922,7 @@ struct cifs_server_iface {
|
|||
};
|
||||
|
||||
struct cifs_chan {
|
||||
unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
|
||||
struct TCP_Server_Info *server;
|
||||
__u8 signkey[SMB3_SIGN_KEY_SIZE];
|
||||
};
|
||||
|
@ -984,12 +985,16 @@ struct cifs_ses {
|
|||
#define CIFS_MAX_CHANNELS 16
|
||||
#define CIFS_ALL_CHANNELS_SET(ses) \
|
||||
((1UL << (ses)->chan_count) - 1)
|
||||
#define CIFS_ALL_CHANS_GOOD(ses) \
|
||||
(!(ses)->chans_need_reconnect)
|
||||
#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \
|
||||
((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses))
|
||||
#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \
|
||||
((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses))
|
||||
#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \
|
||||
test_bit((index), &(ses)->chans_need_reconnect)
|
||||
#define CIFS_CHAN_IN_RECONNECT(ses, index) \
|
||||
((ses)->chans[(index)].in_reconnect)
|
||||
|
||||
struct cifs_chan chans[CIFS_MAX_CHANNELS];
|
||||
size_t chan_count;
|
||||
|
|
|
@ -619,6 +619,15 @@ unsigned int
|
|||
cifs_ses_get_chan_index(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
cifs_chan_set_in_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
bool
|
||||
cifs_chan_in_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
|
|
|
@ -3995,17 +3995,27 @@ 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(&ses->chan_lock);
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (ses->ses_status == SES_EXITING) {
|
||||
if (ses->ses_status != SES_GOOD &&
|
||||
ses->ses_status != SES_NEW &&
|
||||
ses->ses_status != SES_NEED_RECON) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* only send once per connect */
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (CIFS_ALL_CHANS_GOOD(ses) ||
|
||||
cifs_chan_in_reconnect(ses, server)) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
cifs_chan_set_in_reconnect(ses, server);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
if (!is_binding)
|
||||
ses->ses_status = SES_IN_SETUP;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
@ -4035,16 +4045,19 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
|||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (ses->ses_status == SES_IN_SETUP)
|
||||
ses->ses_status = SES_NEED_RECON;
|
||||
spin_lock(&ses->chan_lock);
|
||||
cifs_chan_clear_in_reconnect(ses, server);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
} else {
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (ses->ses_status == SES_IN_SETUP)
|
||||
ses->ses_status = SES_GOOD;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
cifs_chan_clear_in_reconnect(ses, server);
|
||||
cifs_chan_clear_need_reconnect(ses, server);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
|
|
@ -85,6 +85,33 @@ cifs_ses_get_chan_index(struct cifs_ses *ses,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_chan_set_in_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
ses->chans[chan_index].in_reconnect = true;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
ses->chans[chan_index].in_reconnect = false;
|
||||
}
|
||||
|
||||
bool
|
||||
cifs_chan_in_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
|
||||
}
|
||||
|
||||
void
|
||||
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
|
|
Loading…
Reference in a new issue