From: Paulo Alcantara <pc@cjr.nz>
Date: Thu, 15 Jul 2021 21:53:53 -0300
Subject: [PATCH] cifs: do not share tcp sessions of dfs connections
Git-commit: cdc3363065aba2711e51019b3d5787f044f8a133
References: bsc#1185902
Patch-mainline: v5.14-rc2
Make sure that we do not share tcp sessions of dfs mounts when
mounting regular shares that connect to same server. DFS connections
rely on a single instance of tcp in order to do failover properly in
cifs_reconnect().
Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>
Acked-by: Paulo Alcantara <palcantara@suse.de>
---
fs/cifs/cifsglob.h | 3 +++
fs/cifs/connect.c | 39 +++++++++++++++++++++++++++++++++++----
2 files changed, 38 insertions(+), 4 deletions(-)
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -773,6 +773,9 @@ struct TCP_Server_Info {
* reconnect.
*/
int nr_targets;
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ bool is_dfs_conn; /* if a dfs connection */
+#endif
};
struct cifs_credits {
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -2639,6 +2639,16 @@ cifs_find_tcp_session(struct smb_vol *vo
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+#ifdef CONFIG_CIFS_DFS_UPCALL
+ /*
+ * DFS failover implementation in cifs_reconnect() requires unique tcp sessions for
+ * DFS connections to do failover properly, so avoid sharing them with regular
+ * shares or even links that may connect to same server but having completely
+ * different failover targets.
+ */
+ if (server->is_dfs_conn)
+ continue;
+#endif
if (!match_server(server, vol))
continue;
@@ -4292,6 +4302,23 @@ static int mount_setup_tlink(struct cifs
}
#ifdef CONFIG_CIFS_DFS_UPCALL
+static int mount_get_dfs_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
+ unsigned int *xid, struct TCP_Server_Info **nserver,
+ struct cifs_ses **nses, struct cifs_tcon **ntcon)
+{
+ int rc;
+
+ vol->nosharesock = true;
+ rc = mount_get_conns(vol, cifs_sb, xid, nserver, nses, ntcon);
+ if (*nserver) {
+ cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__);
+ spin_lock(&cifs_tcp_ses_lock);
+ (*nserver)->is_dfs_conn = true;
+ spin_unlock(&cifs_tcp_ses_lock);
+ }
+ return rc;
+}
+
/*
* cifs_build_path_to_root returns full path to root when we do not have an
* exiting connection (tcon)
@@ -4453,8 +4480,7 @@ static int setup_dfs_tgt_conn(const char
* targets.
*/
mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
- rc = mount_get_conns(&fake_vol, cifs_sb, xid, server, ses,
- tcon);
+ rc = mount_get_dfs_conns(&fake_vol, cifs_sb, xid, server, ses, tcon);
if (!rc || (*server && *ses)) {
/*
* We were able to connect to new target server.
@@ -4797,7 +4823,12 @@ int cifs_mount(struct cifs_sb_info *cifs
goto error;
}
- vol->nosharesock = true;
+ mount_put_conns(cifs_sb, xid, server, ses, tcon);
+ /*
+ * Ignore error check here because we may failover to other targets from cached a
+ * referral.
+ */
+ (void)mount_get_dfs_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
/* Get path of DFS root */
ref_path = build_unc_path_to_root(vol, cifs_sb, false);
@@ -4826,7 +4857,7 @@ int cifs_mount(struct cifs_sb_info *cifs
/* Connect to new DFS target only if we were redirected */
if (oldmnt != cifs_sb->mountdata) {
mount_put_conns(cifs_sb, xid, server, ses, tcon);
- rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
+ rc = mount_get_dfs_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
}
if (rc && !server && !ses) {
/* Failed to connect. Try to connect to other targets in the referral. */