Enzo Matsumiya cba50e
From: Shyam Prasad N <sprasad@microsoft.com>
Enzo Matsumiya cba50e
Date: Mon, 19 Jul 2021 10:54:46 +0000
Enzo Matsumiya cba50e
Subject: [PATCH] cifs: protect session channel fields with chan_lock
Enzo Matsumiya cba50e
Git-commit: 724244cdb3828522109c88e56a0242537aefabe9
Enzo Matsumiya cba50e
Patch-mainline: v5.16-rc1
Enzo Matsumiya cba50e
References: bsc#1192606
Enzo Matsumiya cba50e
Enzo Matsumiya cba50e
Introducing a new spin lock to protect all the channel related
Enzo Matsumiya cba50e
fields in a cifs_ses struct. This lock should be taken
Enzo Matsumiya cba50e
whenever dealing with the channel fields, and should be held
Enzo Matsumiya cba50e
only for very short intervals which will not sleep.
Enzo Matsumiya cba50e
Enzo Matsumiya cba50e
Currently, all channel related fields in cifs_ses structure
Enzo Matsumiya cba50e
are protected by session_mutex. However, this mutex is held for
Enzo Matsumiya cba50e
long periods (sometimes while waiting for a reply from server).
Enzo Matsumiya cba50e
This makes the codepath quite tricky to change.
Enzo Matsumiya cba50e
Enzo Matsumiya cba50e
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Enzo Matsumiya cba50e
Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Enzo Matsumiya cba50e
Signed-off-by: Steve French <stfrench@microsoft.com>
Enzo Matsumiya cba50e
Acked-by: Enzo Matsumiya <ematsumiya@suse.de>
Enzo Matsumiya cba50e
---
Enzo Matsumiya cba50e
 fs/cifs/cifs_debug.c |  2 ++
Enzo Matsumiya cba50e
 fs/cifs/cifsglob.h   |  5 +++++
Enzo Matsumiya cba50e
 fs/cifs/connect.c    | 25 +++++++++++++++++++---
Enzo Matsumiya cba50e
 fs/cifs/misc.c       |  1 +
Enzo Matsumiya cba50e
 fs/cifs/sess.c       | 50 +++++++++++++++++++++++++++++++++-----------
Enzo Matsumiya cba50e
 fs/cifs/transport.c  |  3 +++
Enzo Matsumiya cba50e
 6 files changed, 71 insertions(+), 15 deletions(-)
Enzo Matsumiya cba50e
Enzo Matsumiya cba50e
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
Enzo Matsumiya cba50e
index 905a901f7f80..248a8f973cf9 100644
Enzo Matsumiya cba50e
--- a/fs/cifs/cifs_debug.c
Enzo Matsumiya cba50e
+++ b/fs/cifs/cifs_debug.c
Enzo Matsumiya cba50e
@@ -414,12 +414,14 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
Enzo Matsumiya cba50e
 				   from_kuid(&init_user_ns, ses->linux_uid),
Enzo Matsumiya cba50e
 				   from_kuid(&init_user_ns, ses->cred_uid));
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+			spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
 			if (ses->chan_count > 1) {
Enzo Matsumiya cba50e
 				seq_printf(m, "\n\n\tExtra Channels: %zu ",
Enzo Matsumiya cba50e
 					   ses->chan_count-1);
Enzo Matsumiya cba50e
 				for (j = 1; j < ses->chan_count; j++)
Enzo Matsumiya cba50e
 					cifs_dump_channel(m, j, &ses->chans[j]);
Enzo Matsumiya cba50e
 			}
Enzo Matsumiya cba50e
+			spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 			seq_puts(m, "\n\n\tShares: ");
Enzo Matsumiya cba50e
 			j = 0;
Enzo Matsumiya cba50e
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
Enzo Matsumiya cba50e
index 673ecbfe6d9a..3c18c56a119b 100644
Enzo Matsumiya cba50e
--- a/fs/cifs/cifsglob.h
Enzo Matsumiya cba50e
+++ b/fs/cifs/cifsglob.h
Enzo Matsumiya cba50e
@@ -952,16 +952,21 @@ struct cifs_ses {
Enzo Matsumiya cba50e
 	 * iface_lock should be taken when accessing any of these fields
Enzo Matsumiya cba50e
 	 */
Enzo Matsumiya cba50e
 	spinlock_t iface_lock;
Enzo Matsumiya cba50e
+	/* ========= begin: protected by iface_lock ======== */
Enzo Matsumiya cba50e
 	struct cifs_server_iface *iface_list;
Enzo Matsumiya cba50e
 	size_t iface_count;
Enzo Matsumiya cba50e
 	unsigned long iface_last_update; /* jiffies */
Enzo Matsumiya cba50e
+	/* ========= end: protected by iface_lock ======== */
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	spinlock_t chan_lock;
Enzo Matsumiya cba50e
+	/* ========= begin: protected by chan_lock ======== */
Enzo Matsumiya cba50e
 #define CIFS_MAX_CHANNELS 16
Enzo Matsumiya cba50e
 	struct cifs_chan chans[CIFS_MAX_CHANNELS];
Enzo Matsumiya cba50e
 	struct cifs_chan *binding_chan;
Enzo Matsumiya cba50e
 	size_t chan_count;
Enzo Matsumiya cba50e
 	size_t chan_max;
Enzo Matsumiya cba50e
 	atomic_t chan_seq; /* round robin state */
Enzo Matsumiya cba50e
+	/* ========= end: protected by chan_lock ======== */
Enzo Matsumiya cba50e
 };
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 /*
Enzo Matsumiya cba50e
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
Enzo Matsumiya cba50e
index 498ec05ca10d..09a532ed8fe4 100644
Enzo Matsumiya cba50e
--- a/fs/cifs/connect.c
Enzo Matsumiya cba50e
+++ b/fs/cifs/connect.c
Enzo Matsumiya cba50e
@@ -1577,8 +1577,12 @@ static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx)
Enzo Matsumiya cba50e
 	 * If an existing session is limited to less channels than
Enzo Matsumiya cba50e
 	 * requested, it should not be reused
Enzo Matsumiya cba50e
 	 */
Enzo Matsumiya cba50e
-	if (ses->chan_max < ctx->max_channels)
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
+	if (ses->chan_max < ctx->max_channels) {
Enzo Matsumiya cba50e
+		spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 		return 0;
Enzo Matsumiya cba50e
+	}
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 	switch (ses->sectype) {
Enzo Matsumiya cba50e
 	case Kerberos:
Enzo Matsumiya cba50e
@@ -1713,6 +1717,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
Enzo Matsumiya cba50e
 void cifs_put_smb_ses(struct cifs_ses *ses)
Enzo Matsumiya cba50e
 {
Enzo Matsumiya cba50e
 	unsigned int rc, xid;
Enzo Matsumiya cba50e
+	unsigned int chan_count;
Enzo Matsumiya cba50e
 	struct TCP_Server_Info *server = ses->server;
Enzo Matsumiya cba50e
 	cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
@@ -1754,12 +1759,24 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
Enzo Matsumiya cba50e
 	list_del_init(&ses->smb_ses_list);
Enzo Matsumiya cba50e
 	spin_unlock(&cifs_tcp_ses_lock);
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
+	chan_count = ses->chan_count;
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
+
Enzo Matsumiya cba50e
 	/* close any extra channels */
Enzo Matsumiya cba50e
-	if (ses->chan_count > 1) {
Enzo Matsumiya cba50e
+	if (chan_count > 1) {
Enzo Matsumiya cba50e
 		int i;
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
-		for (i = 1; i < ses->chan_count; i++)
Enzo Matsumiya cba50e
+		for (i = 1; i < chan_count; i++) {
Enzo Matsumiya cba50e
+			/*
Enzo Matsumiya cba50e
+			 * note: for now, we're okay accessing ses->chans
Enzo Matsumiya cba50e
+			 * without chan_lock. But when chans can go away, we'll
Enzo Matsumiya cba50e
+			 * need to introduce ref counting to make sure that chan
Enzo Matsumiya cba50e
+			 * is not freed from under us.
Enzo Matsumiya cba50e
+			 */
Enzo Matsumiya cba50e
 			cifs_put_tcp_session(ses->chans[i].server, 0);
Enzo Matsumiya cba50e
+			ses->chans[i].server = NULL;
Enzo Matsumiya cba50e
+		}
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 	sesInfoFree(ses);
Enzo Matsumiya cba50e
@@ -2018,9 +2035,11 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
Enzo Matsumiya cba50e
 	mutex_lock(&ses->session_mutex);
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 	/* add server as first channel */
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	ses->chans[0].server = server;
Enzo Matsumiya cba50e
 	ses->chan_count = 1;
Enzo Matsumiya cba50e
 	ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 	rc = cifs_negotiate_protocol(xid, ses);
Enzo Matsumiya cba50e
 	if (!rc)
Enzo Matsumiya cba50e
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
Enzo Matsumiya cba50e
index a6089ea53ad7..5148d48d6a35 100644
Enzo Matsumiya cba50e
--- a/fs/cifs/misc.c
Enzo Matsumiya cba50e
+++ b/fs/cifs/misc.c
Enzo Matsumiya cba50e
@@ -75,6 +75,7 @@ sesInfoAlloc(void)
Enzo Matsumiya cba50e
 		INIT_LIST_HEAD(&ret_buf->tcon_list);
Enzo Matsumiya cba50e
 		mutex_init(&ret_buf->session_mutex);
Enzo Matsumiya cba50e
 		spin_lock_init(&ret_buf->iface_lock);
Enzo Matsumiya cba50e
+		spin_lock_init(&ret_buf->chan_lock);
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
 	return ret_buf;
Enzo Matsumiya cba50e
 }
Enzo Matsumiya cba50e
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
Enzo Matsumiya cba50e
index 93a1619d60e6..27660bffb918 100644
Enzo Matsumiya cba50e
--- a/fs/cifs/sess.c
Enzo Matsumiya cba50e
+++ b/fs/cifs/sess.c
Enzo Matsumiya cba50e
@@ -54,41 +54,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
Enzo Matsumiya cba50e
 {
Enzo Matsumiya cba50e
 	int i;
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	for (i = 0; i < ses->chan_count; i++) {
Enzo Matsumiya cba50e
-		if (is_server_using_iface(ses->chans[i].server, iface))
Enzo Matsumiya cba50e
+		if (is_server_using_iface(ses->chans[i].server, iface)) {
Enzo Matsumiya cba50e
+			spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 			return true;
Enzo Matsumiya cba50e
+		}
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	return false;
Enzo Matsumiya cba50e
 }
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 /* returns number of channels added */
Enzo Matsumiya cba50e
 int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
Enzo Matsumiya cba50e
 {
Enzo Matsumiya cba50e
-	int old_chan_count = ses->chan_count;
Enzo Matsumiya cba50e
-	int left = ses->chan_max - ses->chan_count;
Enzo Matsumiya cba50e
+	int old_chan_count, new_chan_count;
Enzo Matsumiya cba50e
+	int left;
Enzo Matsumiya cba50e
 	int i = 0;
Enzo Matsumiya cba50e
 	int rc = 0;
Enzo Matsumiya cba50e
 	int tries = 0;
Enzo Matsumiya cba50e
 	struct cifs_server_iface *ifaces = NULL;
Enzo Matsumiya cba50e
 	size_t iface_count;
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	if (ses->server->dialect < SMB30_PROT_ID) {
Enzo Matsumiya cba50e
+		cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
Enzo Matsumiya cba50e
+		return 0;
Enzo Matsumiya cba50e
+	}
Enzo Matsumiya cba50e
+
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
+
Enzo Matsumiya cba50e
+	new_chan_count = old_chan_count = ses->chan_count;
Enzo Matsumiya cba50e
+	left = ses->chan_max - ses->chan_count;
Enzo Matsumiya cba50e
+
Enzo Matsumiya cba50e
 	if (left <= 0) {
Enzo Matsumiya cba50e
 		cifs_dbg(FYI,
Enzo Matsumiya cba50e
 			 "ses already at max_channels (%zu), nothing to open\n",
Enzo Matsumiya cba50e
 			 ses->chan_max);
Enzo Matsumiya cba50e
-		return 0;
Enzo Matsumiya cba50e
-	}
Enzo Matsumiya cba50e
-
Enzo Matsumiya cba50e
-	if (ses->server->dialect < SMB30_PROT_ID) {
Enzo Matsumiya cba50e
-		cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
Enzo Matsumiya cba50e
+		spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 		return 0;
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 	if (!(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
Enzo Matsumiya cba50e
 		cifs_dbg(VFS, "server %s does not support multichannel\n", ses->server->hostname);
Enzo Matsumiya cba50e
 		ses->chan_max = 1;
Enzo Matsumiya cba50e
+		spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 		return 0;
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 	/*
Enzo Matsumiya cba50e
 	 * Make a copy of the iface list at the time and use that
Enzo Matsumiya cba50e
@@ -142,10 +154,11 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
Enzo Matsumiya cba50e
 		cifs_dbg(FYI, "successfully opened new channel on iface#%d\n",
Enzo Matsumiya cba50e
 			 i);
Enzo Matsumiya cba50e
 		left--;
Enzo Matsumiya cba50e
+		new_chan_count++;
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 	kfree(ifaces);
Enzo Matsumiya cba50e
-	return ses->chan_count - old_chan_count;
Enzo Matsumiya cba50e
+	return new_chan_count - old_chan_count;
Enzo Matsumiya cba50e
 }
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
 /*
Enzo Matsumiya cba50e
@@ -157,10 +170,14 @@ cifs_ses_find_chan(struct cifs_ses *ses, struct TCP_Server_Info *server)
Enzo Matsumiya cba50e
 {
Enzo Matsumiya cba50e
 	int i;
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	for (i = 0; i < ses->chan_count; i++) {
Enzo Matsumiya cba50e
-		if (ses->chans[i].server == server)
Enzo Matsumiya cba50e
+		if (ses->chans[i].server == server) {
Enzo Matsumiya cba50e
+			spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 			return &ses->chans[i];
Enzo Matsumiya cba50e
+		}
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	return NULL;
Enzo Matsumiya cba50e
 }
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
@@ -168,6 +185,7 @@ static int
Enzo Matsumiya cba50e
 cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
Enzo Matsumiya cba50e
 		     struct cifs_server_iface *iface)
Enzo Matsumiya cba50e
 {
Enzo Matsumiya cba50e
+	struct TCP_Server_Info *chan_server;
Enzo Matsumiya cba50e
 	struct cifs_chan *chan;
Enzo Matsumiya cba50e
 	struct smb3_fs_context ctx = {NULL};
Enzo Matsumiya cba50e
 	static const char unc_fmt[] = "\\%s\\foo";
Enzo Matsumiya cba50e
@@ -240,15 +258,20 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
Enzo Matsumiya cba50e
 	       SMB2_CLIENT_GUID_SIZE);
Enzo Matsumiya cba50e
 	ctx.use_client_guid = true;
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
-	mutex_lock(&ses->session_mutex);
Enzo Matsumiya cba50e
+	chan_server = cifs_get_tcp_session(&ctx;;
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	mutex_lock(&ses->session_mutex);
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	chan = ses->binding_chan = &ses->chans[ses->chan_count];
Enzo Matsumiya cba50e
-	chan->server = cifs_get_tcp_session(&ctx;;
Enzo Matsumiya cba50e
+	chan->server = chan_server;
Enzo Matsumiya cba50e
 	if (IS_ERR(chan->server)) {
Enzo Matsumiya cba50e
 		rc = PTR_ERR(chan->server);
Enzo Matsumiya cba50e
 		chan->server = NULL;
Enzo Matsumiya cba50e
+		spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 		goto out;
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
+
Enzo Matsumiya cba50e
 	spin_lock(&cifs_tcp_ses_lock);
Enzo Matsumiya cba50e
 	chan->server->is_channel = true;
Enzo Matsumiya cba50e
 	spin_unlock(&cifs_tcp_ses_lock);
Enzo Matsumiya cba50e
@@ -283,8 +306,11 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
Enzo Matsumiya cba50e
 	 * ses to the new server.
Enzo Matsumiya cba50e
 	 */
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	ses->chan_count++;
Enzo Matsumiya cba50e
 	atomic_set(&ses->chan_seq, 0);
Enzo Matsumiya cba50e
+	spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
+
Enzo Matsumiya cba50e
 out:
Enzo Matsumiya cba50e
 	ses->binding = false;
Enzo Matsumiya cba50e
 	ses->binding_chan = NULL;
Enzo Matsumiya cba50e
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
Enzo Matsumiya cba50e
index b7379329b741..61ea3d3f95b4 100644
Enzo Matsumiya cba50e
--- a/fs/cifs/transport.c
Enzo Matsumiya cba50e
+++ b/fs/cifs/transport.c
Enzo Matsumiya cba50e
@@ -1044,14 +1044,17 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
Enzo Matsumiya cba50e
 	if (!ses)
Enzo Matsumiya cba50e
 		return NULL;
Enzo Matsumiya cba50e
 
Enzo Matsumiya cba50e
+	spin_lock(&ses->chan_lock);
Enzo Matsumiya cba50e
 	if (!ses->binding) {
Enzo Matsumiya cba50e
 		/* round robin */
Enzo Matsumiya cba50e
 		if (ses->chan_count > 1) {
Enzo Matsumiya cba50e
 			index = (uint)atomic_inc_return(&ses->chan_seq);
Enzo Matsumiya cba50e
 			index %= ses->chan_count;
Enzo Matsumiya cba50e
 		}
Enzo Matsumiya cba50e
+		spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 		return ses->chans[index].server;
Enzo Matsumiya cba50e
 	} else {
Enzo Matsumiya cba50e
+		spin_unlock(&ses->chan_lock);
Enzo Matsumiya cba50e
 		return cifs_ses_server(ses);
Enzo Matsumiya cba50e
 	}
Enzo Matsumiya cba50e
 }
Enzo Matsumiya cba50e
-- 
Enzo Matsumiya cba50e
2.33.1
Enzo Matsumiya cba50e