Commit 5752bf64 authored by Shyam Prasad N's avatar Shyam Prasad N Committed by Steve French
Browse files

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: default avatarShyam Prasad N <sprasad@microsoft.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent dd3cd870
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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);
+5 −0
Original line number Diff line number Diff line
@@ -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;
+9 −0
Original line number Diff line number Diff line
@@ -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
+20 −7
Original line number Diff line number Diff line
@@ -3995,16 +3995,26 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
	int rc = -ENOSYS;
	bool is_binding = false;


	spin_lock(&cifs_tcp_ses_lock);
	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);
	is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
	if (CIFS_ALL_CHANS_GOOD(ses) ||
	    cifs_chan_in_reconnect(ses, server)) {
		spin_unlock(&ses->chan_lock);

	spin_lock(&cifs_tcp_ses_lock);
	if (ses->ses_status == SES_EXITING) {
		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;
@@ -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;
+27 −0
Original line number Diff line number Diff line
@@ -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)