All of lore.kernel.org
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: stable@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	patches@lists.linux.dev, "Paulo Alcantara (SUSE)" <pc@cjr.nz>,
	Steve French <stfrench@microsoft.com>,
	Sasha Levin <sashal@kernel.org>
Subject: [PATCH 5.15 010/206] cifs: support nested dfs links over reconnect
Date: Wed, 30 Nov 2022 19:21:02 +0100	[thread overview]
Message-ID: <20221130180533.258073946@linuxfoundation.org> (raw)
In-Reply-To: <20221130180532.974348590@linuxfoundation.org>

From: Paulo Alcantara <pc@cjr.nz>

[ Upstream commit c88f7dcd6d6429197fc2fd87b54a894ffcd48e8e ]

Mounting a dfs link that has nested links was already supported at
mount(2), so make it work over reconnect as well.

Make the following case work:

* mount //root/dfs/link /mnt -o ...
  - final share: /server/share

* in server settings
  - change target folder of /root/dfs/link3 to /server/share2
  - change target folder of /root/dfs/link2 to /root/dfs/link3
  - change target folder of /root/dfs/link to /root/dfs/link2

* mount -o remount,... /mnt
 - refresh all dfs referrals
 - mark current connection for failover
 - cifs_reconnect() reconnects to root server
 - tree_connect()
   * checks that /root/dfs/link2 is a link, then chase it
   * checks that root/dfs/link3 is a link, then chase it
   * finally tree connect to /server/share2

If the mounted share is no longer accessible and a reconnect had been
triggered, the client will retry it from both last referral
path (/root/dfs/link3) and original referral path (/root/dfs/link).

Any new referral paths found while chasing dfs links over reconnect,
it will be updated to TCP_Server_Info::leaf_fullpath, accordingly.

Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>
Stable-dep-of: 1dcdf5f5b213 ("cifs: Fix connections leak when tlink setup failed")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/cifs/cifs_dfs_ref.c |   59 +--
 fs/cifs/cifs_fs_sb.h   |    5 -
 fs/cifs/cifsglob.h     |   24 +-
 fs/cifs/cifsproto.h    |    5 +-
 fs/cifs/connect.c      | 1138 ++++++++++++++++++++--------------------
 fs/cifs/dfs_cache.c    |   44 +-
 fs/cifs/misc.c         |   62 +--
 fs/cifs/smb2ops.c      |   10 +-
 fs/cifs/smb2pdu.c      |    6 +-
 9 files changed, 660 insertions(+), 693 deletions(-)

diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
index 007427ba75e5..b0864da9ef43 100644
--- a/fs/cifs/cifs_dfs_ref.c
+++ b/fs/cifs/cifs_dfs_ref.c
@@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
 static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
 {
 	struct cifs_sb_info *cifs_sb;
-	struct cifs_ses *ses;
-	struct cifs_tcon *tcon;
 	void *page;
-	char *full_path, *root_path;
-	unsigned int xid;
-	int rc;
+	char *full_path;
 	struct vfsmount *mnt;
 
 	cifs_dbg(FYI, "in %s\n", __func__);
@@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
 	 * the double backslashes usually used in the UNC. This function
 	 * gives us the latter, so we must adjust the result.
 	 */
-	mnt = ERR_PTR(-ENOMEM);
-
 	cifs_sb = CIFS_SB(mntpt->d_sb);
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
 		mnt = ERR_PTR(-EREMOTE);
@@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
 	}
 
 	convert_delimiter(full_path, '\\');
-
 	cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
 
-	if (!cifs_sb_master_tlink(cifs_sb)) {
-		cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__);
-		goto free_full_path;
-	}
-
-	tcon = cifs_sb_master_tcon(cifs_sb);
-	if (!tcon) {
-		cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__);
-		goto free_full_path;
-	}
-
-	root_path = kstrdup(tcon->treeName, GFP_KERNEL);
-	if (!root_path) {
-		mnt = ERR_PTR(-ENOMEM);
-		goto free_full_path;
-	}
-	cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path);
-
-	ses = tcon->ses;
-	xid = get_xid();
-
-	/*
-	 * If DFS root has been expired, then unconditionally fetch it again to
-	 * refresh DFS referral cache.
-	 */
-	rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
-			    root_path + 1, NULL, NULL);
-	if (!rc) {
-		rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
-				    cifs_remap(cifs_sb), full_path + 1,
-				    NULL, NULL);
-	}
-
-	free_xid(xid);
-
-	if (rc) {
-		mnt = ERR_PTR(rc);
-		goto free_root_path;
-	}
-	/*
-	 * OK - we were able to get and cache a referral for @full_path.
-	 *
-	 * Now, pass it down to cifs_mount() and it will retry every available
-	 * node server in case of failures - no need to do it here.
-	 */
 	mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
-	cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
-		 full_path + 1, mnt);
+	cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt);
 
-free_root_path:
-	kfree(root_path);
 free_full_path:
 	free_dentry_path(page);
 cdda_exit:
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index f97407520ea1..013a4bd65280 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -61,11 +61,6 @@ struct cifs_sb_info {
 	/* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
 	char *prepath;
 
-	/*
-	 * Canonical DFS path initially provided by the mount call. We might connect to something
-	 * different via DFS but we want to keep it to do failover properly.
-	 */
-	char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */
 	/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
 	uuid_t dfs_mount_id;
 	/*
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index a97ed30843cf..1ab72c3d0bff 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -693,6 +693,19 @@ struct TCP_Server_Info {
 #endif
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	bool is_dfs_conn; /* if a dfs connection */
+	struct mutex refpath_lock; /* protects leaf_fullpath */
+	/*
+	 * Canonical DFS full paths that were used to chase referrals in mount and reconnect.
+	 *
+	 * origin_fullpath: first or original referral path
+	 * leaf_fullpath: last referral path (might be changed due to nested links in reconnect)
+	 *
+	 * current_fullpath: pointer to either origin_fullpath or leaf_fullpath
+	 * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect()
+	 *
+	 * format: \\HOST\SHARE\[OPTIONAL PATH]
+	 */
+	char *origin_fullpath, *leaf_fullpath, *current_fullpath;
 #endif
 };
 
@@ -1097,7 +1110,6 @@ struct cifs_tcon {
 	struct cached_fid crfid; /* Cached root fid */
 	/* BB add field for back pointer to sb struct(s)? */
 #ifdef CONFIG_CIFS_DFS_UPCALL
-	char *dfs_path; /* canonical DFS path */
 	struct list_head ulist; /* cache update list */
 #endif
 };
@@ -1950,4 +1962,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon)
 		tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT);
 }
 
+static inline bool cifs_is_referral_server(struct cifs_tcon *tcon,
+					   const struct dfs_info3_param *ref)
+{
+	/*
+	 * Check if all targets are capable of handling DFS referrals as per
+	 * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
+	 */
+	return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER));
+}
+
 #endif	/* _CIFS_GLOB_H */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index d0f85b666662..b2697356b5e7 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -607,7 +607,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
 
 struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
 void cifs_put_tcp_super(struct super_block *sb);
-int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
+int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
 char *extract_hostname(const char *unc);
 char *extract_sharename(const char *unc);
 
@@ -634,4 +634,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
 		return options;
 }
 
+struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
+void cifs_put_tcon_super(struct super_block *sb);
+
 #endif			/* _CIFSPROTO_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 5d87d5c01762..902eb8a5afd2 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -61,6 +61,20 @@ extern bool disable_legacy_dialects;
 /* Drop the connection to not overload the server */
 #define NUM_STATUS_IO_TIMEOUT   5
 
+struct mount_ctx {
+	struct cifs_sb_info *cifs_sb;
+	struct smb3_fs_context *fs_ctx;
+	unsigned int xid;
+	struct TCP_Server_Info *server;
+	struct cifs_ses *ses;
+	struct cifs_tcon *tcon;
+#ifdef CONFIG_CIFS_DFS_UPCALL
+	struct cifs_ses *root_ses;
+	uuid_t mount_id;
+	char *origin_fullpath, *leaf_fullpath;
+#endif
+};
+
 static int ip_connect(struct TCP_Server_Info *server);
 static int generic_ip_connect(struct TCP_Server_Info *server);
 static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
@@ -303,14 +317,68 @@ static int __cifs_reconnect(struct TCP_Server_Info *server)
 }
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
-static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb)
+static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target)
+{
+	int rc;
+	char *hostname;
+
+	if (!cifs_swn_set_server_dstaddr(server)) {
+		if (server->hostname != target) {
+			hostname = extract_hostname(target);
+			if (!IS_ERR(hostname)) {
+				kfree(server->hostname);
+				server->hostname = hostname;
+			} else {
+				cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
+					 __func__, PTR_ERR(hostname));
+				cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__,
+					 server->hostname);
+			}
+		}
+		/* resolve the hostname again to make sure that IP address is up-to-date. */
+		rc = reconn_set_ipaddr_from_hostname(server);
+		cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
+	}
+	/* Reconnect the socket */
+	if (cifs_rdma_enabled(server))
+		rc = smbd_reconnect(server);
+	else
+		rc = generic_ip_connect(server);
+
+	return rc;
+}
+
+static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl,
+				     struct dfs_cache_tgt_iterator **target_hint)
+{
+	int rc;
+	struct dfs_cache_tgt_iterator *tit;
+
+	*target_hint = NULL;
+
+	/* If dfs target list is empty, then reconnect to last server */
+	tit = dfs_cache_get_tgt_iterator(tl);
+	if (!tit)
+		return __reconnect_target_unlocked(server, server->hostname);
+
+	/* Otherwise, try every dfs target in @tl */
+	for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
+		rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit));
+		if (!rc) {
+			*target_hint = tit;
+			break;
+		}
+	}
+	return rc;
+}
+
+static int reconnect_dfs_server(struct TCP_Server_Info *server)
 {
 	int rc = 0;
-	const char *refpath = cifs_sb->origin_fullpath + 1;
+	const char *refpath = server->current_fullpath + 1;
 	struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
-	struct dfs_cache_tgt_iterator *tit = NULL;
-	int num_targets = 1;
-	char *hostname;
+	struct dfs_cache_tgt_iterator *target_hint = NULL;
+	int num_targets = 0;
 
 	/*
 	 * Determine the number of dfs targets the referral path in @cifs_sb resolves to.
@@ -320,11 +388,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
 	 * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
 	 * refreshing the referral, so, in this case, default it to 1.
 	 */
-	if (!dfs_cache_noreq_find(refpath, NULL, &tl)) {
+	if (!dfs_cache_noreq_find(refpath, NULL, &tl))
 		num_targets = dfs_cache_get_nr_tgts(&tl);
-		if (!num_targets)
-			num_targets = 1;
-	}
+	if (!num_targets)
+		num_targets = 1;
 
 	if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
 		return 0;
@@ -332,51 +399,17 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
 	cifs_mark_tcp_ses_conns_for_reconnect(server);
 
 	do {
-		/* Get next dfs target from target list (if any) */
-		if (!tit)
-			tit = dfs_cache_get_tgt_iterator(&tl);
-		else
-			tit = dfs_cache_get_next_tgt(&tl, tit);
-
 		try_to_freeze();
 		mutex_lock(&server->srv_mutex);
 
-		if (!cifs_swn_set_server_dstaddr(server)) {
-			/*
-			 * If any dfs target was selected, then update @server with either a
-			 * hostname or an address.
-			 */
-			if (tit) {
-				hostname = extract_hostname(dfs_cache_get_tgt_name(tit));
-				if (!IS_ERR(hostname)) {
-					kfree(server->hostname);
-					server->hostname = hostname;
-				} else {
-					cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
-						 __func__, PTR_ERR(hostname));
-					cifs_dbg(FYI, "%s: default to last target server: %s\n",
-						 __func__, server->hostname);
-				}
-			}
-			/* resolve the hostname again to make sure that IP address is up-to-date. */
-			rc = reconn_set_ipaddr_from_hostname(server);
-			cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
-		}
-
-		/* Reconnect the socket */
-		if (cifs_rdma_enabled(server))
-			rc = smbd_reconnect(server);
-		else
-			rc = generic_ip_connect(server);
-
+		rc = reconnect_target_unlocked(server, &tl, &target_hint);
 		if (rc) {
-			/* Failed to reconnect socket.  Retry next dfs target. */
+			/* Failed to reconnect socket */
 			mutex_unlock(&server->srv_mutex);
 			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
 			msleep(3000);
 			continue;
 		}
-
 		/*
 		 * Socket was created.  Update tcp session status to CifsNeedNegotiate so that a
 		 * process waiting for reconnect will know it needs to re-establish session and tcon
@@ -392,8 +425,8 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
 		mutex_unlock(&server->srv_mutex);
 	} while (server->tcpStatus == CifsNeedReconnect);
 
-	if (tit)
-		dfs_cache_noreq_update_tgthint(refpath, tit);
+	if (target_hint)
+		dfs_cache_noreq_update_tgthint(refpath, target_hint);
 
 	dfs_cache_free_tgts(&tl);
 
@@ -407,38 +440,15 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
 
 int cifs_reconnect(struct TCP_Server_Info *server)
 {
-	int rc;
-	struct super_block *sb;
-	struct cifs_sb_info *cifs_sb;
-
-	/*
-	 * If tcp session is not an dfs connection or it is a channel, then reconnect to last target
-	 * server.
-	 */
+	/* If tcp session is not an dfs connection, then reconnect to last target server */
 	spin_lock(&cifs_tcp_ses_lock);
-	if (!server->is_dfs_conn || server->is_channel) {
+	if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) {
 		spin_unlock(&cifs_tcp_ses_lock);
 		return __cifs_reconnect(server);
 	}
 	spin_unlock(&cifs_tcp_ses_lock);
 
-	/* If no superblock, then it might be an ipc connection */
-	sb = cifs_get_tcp_super(server);
-	if (IS_ERR(sb))
-		return __cifs_reconnect(server);
-
-	/*
-	 * Check for a referral path to look up in superblock.  If unset, then simply reconnect to
-	 * last target server.
-	 */
-	cifs_sb = CIFS_SB(sb);
-	if (!cifs_sb->origin_fullpath || !cifs_sb->origin_fullpath[0])
-		rc = __cifs_reconnect(server);
-	else
-		rc = reconnect_dfs_server(server, cifs_sb);
-
-	cifs_put_tcp_super(sb);
-	return rc;
+	return reconnect_dfs_server(server);
 }
 #else
 int cifs_reconnect(struct TCP_Server_Info *server)
@@ -829,6 +839,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
 		 */
 	}
 
+#ifdef CONFIG_CIFS_DFS_UPCALL
+	kfree(server->origin_fullpath);
+	kfree(server->leaf_fullpath);
+#endif
 	kfree(server);
 
 	length = atomic_dec_return(&tcpSesAllocCount);
@@ -1445,6 +1459,9 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
 	INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server);
 	INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
 	mutex_init(&tcp_ses->reconnect_mutex);
+#ifdef CONFIG_CIFS_DFS_UPCALL
+	mutex_init(&tcp_ses->refpath_lock);
+#endif
 	memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
 	       sizeof(tcp_ses->srcaddr));
 	memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
@@ -2909,73 +2926,64 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
 }
 
 /* Release all succeed connections */
-static inline void mount_put_conns(struct cifs_sb_info *cifs_sb,
-				   unsigned int xid,
-				   struct TCP_Server_Info *server,
-				   struct cifs_ses *ses, struct cifs_tcon *tcon)
+static inline void mount_put_conns(struct mount_ctx *mnt_ctx)
 {
 	int rc = 0;
 
-	if (tcon)
-		cifs_put_tcon(tcon);
-	else if (ses)
-		cifs_put_smb_ses(ses);
-	else if (server)
-		cifs_put_tcp_session(server, 0);
-	cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
-	free_xid(xid);
+	if (mnt_ctx->tcon)
+		cifs_put_tcon(mnt_ctx->tcon);
+	else if (mnt_ctx->ses)
+		cifs_put_smb_ses(mnt_ctx->ses);
+	else if (mnt_ctx->server)
+		cifs_put_tcp_session(mnt_ctx->server, 0);
+	mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
+	free_xid(mnt_ctx->xid);
 }
 
 /* Get connections for tcp, ses and tcon */
-static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
-			   unsigned int *xid,
-			   struct TCP_Server_Info **nserver,
-			   struct cifs_ses **nses, struct cifs_tcon **ntcon)
+static int mount_get_conns(struct mount_ctx *mnt_ctx)
 {
 	int rc = 0;
-	struct TCP_Server_Info *server;
-	struct cifs_ses *ses;
-	struct cifs_tcon *tcon;
-
-	*nserver = NULL;
-	*nses = NULL;
-	*ntcon = NULL;
+	struct TCP_Server_Info *server = NULL;
+	struct cifs_ses *ses = NULL;
+	struct cifs_tcon *tcon = NULL;
+	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	unsigned int xid;
 
-	*xid = get_xid();
+	xid = get_xid();
 
 	/* get a reference to a tcp session */
 	server = cifs_get_tcp_session(ctx);
 	if (IS_ERR(server)) {
 		rc = PTR_ERR(server);
-		return rc;
+		server = NULL;
+		goto out;
 	}
 
-	*nserver = server;
-
 	/* get a reference to a SMB session */
 	ses = cifs_get_smb_ses(server, ctx);
 	if (IS_ERR(ses)) {
 		rc = PTR_ERR(ses);
-		return rc;
+		ses = NULL;
+		goto out;
 	}
 
-	*nses = ses;
-
 	if ((ctx->persistent == true) && (!(ses->server->capabilities &
 					    SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) {
 		cifs_server_dbg(VFS, "persistent handles not supported by server\n");
-		return -EOPNOTSUPP;
+		rc = -EOPNOTSUPP;
+		goto out;
 	}
 
 	/* search for existing tcon to this server share */
 	tcon = cifs_get_tcon(ses, ctx);
 	if (IS_ERR(tcon)) {
 		rc = PTR_ERR(tcon);
-		return rc;
+		tcon = NULL;
+		goto out;
 	}
 
-	*ntcon = tcon;
-
 	/* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
 	if (tcon->posix_extensions)
 		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
@@ -2986,17 +2994,19 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif
 		 * reset of caps checks mount to see if unix extensions disabled
 		 * for just this mount.
 		 */
-		reset_cifs_unix_caps(*xid, tcon, cifs_sb, ctx);
+		reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx);
 		if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
 		    (le64_to_cpu(tcon->fsUnixInfo.Capability) &
-		     CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP))
-			return -EACCES;
+		     CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) {
+			rc = -EACCES;
+			goto out;
+		}
 	} else
 		tcon->unix_ext = 0; /* server does not support them */
 
 	/* do not care if a following call succeed - informational */
 	if (!tcon->pipe && server->ops->qfs_tcon) {
-		server->ops->qfs_tcon(*xid, tcon, cifs_sb);
+		server->ops->qfs_tcon(xid, tcon, cifs_sb);
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) {
 			if (tcon->fsDevInfo.DeviceCharacteristics &
 			    cpu_to_le32(FILE_READ_ONLY_DEVICE))
@@ -3020,7 +3030,13 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif
 	    (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx)))
 		cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx);
 
-	return 0;
+out:
+	mnt_ctx->server = server;
+	mnt_ctx->ses = ses;
+	mnt_ctx->tcon = tcon;
+	mnt_ctx->xid = xid;
+
+	return rc;
 }
 
 static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
@@ -3050,18 +3066,17 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
 }
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
-static int mount_get_dfs_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
-			       unsigned int *xid, struct TCP_Server_Info **nserver,
-			       struct cifs_ses **nses, struct cifs_tcon **ntcon)
+/* Get unique dfs connections */
+static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx)
 {
 	int rc;
 
-	ctx->nosharesock = true;
-	rc = mount_get_conns(ctx, cifs_sb, xid, nserver, nses, ntcon);
-	if (*nserver) {
+	mnt_ctx->fs_ctx->nosharesock = true;
+	rc = mount_get_conns(mnt_ctx);
+	if (mnt_ctx->server) {
 		cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__);
 		spin_lock(&cifs_tcp_ses_lock);
-		(*nserver)->is_dfs_conn = true;
+		mnt_ctx->server->is_dfs_conn = true;
 		spin_unlock(&cifs_tcp_ses_lock);
 	}
 	return rc;
@@ -3103,190 +3118,38 @@ build_unc_path_to_root(const struct smb3_fs_context *ctx,
 }
 
 /*
- * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb
+ * expand_dfs_referral - Update cifs_sb from dfs referral path
  *
- * If a referral is found, cifs_sb->ctx->mount_options will be (re-)allocated
- * to a string containing updated options for the submount.  Otherwise it
- * will be left untouched.
- *
- * Returns the rc from get_dfs_path to the caller, which can be used to
- * determine whether there were referrals.
+ * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the
+ * submount.  Otherwise it will be left untouched.
  */
-static int
-expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
-		    struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
-		    char *ref_path)
+static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path,
+			       struct dfs_info3_param *referral)
 {
 	int rc;
-	struct dfs_info3_param referral = {0};
-	char *full_path = NULL, *mdata = NULL;
-
-	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
-		return -EREMOTE;
-
-	full_path = build_unc_path_to_root(ctx, cifs_sb, true);
-	if (IS_ERR(full_path))
-		return PTR_ERR(full_path);
-
-	rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
-			    ref_path, &referral, NULL);
-	if (!rc) {
-		char *fake_devname = NULL;
-
-		mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
-						   full_path + 1, &referral,
-						   &fake_devname);
-		free_dfs_info_param(&referral);
-
-		if (IS_ERR(mdata)) {
-			rc = PTR_ERR(mdata);
-			mdata = NULL;
-		} else {
-			/*
-			 * We can not clear out the whole structure since we
-			 * no longer have an explicit function to parse
-			 * a mount-string. Instead we need to clear out the
-			 * individual fields that are no longer valid.
-			 */
-			kfree(ctx->prepath);
-			ctx->prepath = NULL;
-			rc = cifs_setup_volume_info(ctx, mdata, fake_devname);
-		}
-		kfree(fake_devname);
-		kfree(cifs_sb->ctx->mount_options);
-		cifs_sb->ctx->mount_options = mdata;
-	}
-	kfree(full_path);
-	return rc;
-}
-
-static int get_next_dfs_tgt(struct dfs_cache_tgt_list *tgt_list,
-			    struct dfs_cache_tgt_iterator **tgt_it)
-{
-	if (!*tgt_it)
-		*tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
-	else
-		*tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it);
-	return !*tgt_it ? -EHOSTDOWN : 0;
-}
-
-static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it,
-			   struct smb3_fs_context *fake_ctx, struct smb3_fs_context *ctx)
-{
-	const char *tgt = dfs_cache_get_tgt_name(tgt_it);
-	int len = strlen(tgt) + 2;
-	char *new_unc;
-
-	new_unc = kmalloc(len, GFP_KERNEL);
-	if (!new_unc)
-		return -ENOMEM;
-	scnprintf(new_unc, len, "\\%s", tgt);
-
-	kfree(ctx->UNC);
-	ctx->UNC = new_unc;
-
-	if (fake_ctx->prepath) {
-		kfree(ctx->prepath);
-		ctx->prepath = fake_ctx->prepath;
-		fake_ctx->prepath = NULL;
-	}
-	memcpy(&ctx->dstaddr, &fake_ctx->dstaddr, sizeof(ctx->dstaddr));
-
-	return 0;
-}
-
-static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb,
-			   struct smb3_fs_context *ctx, struct cifs_ses *root_ses,
-			   unsigned int *xid, struct TCP_Server_Info **server,
-			   struct cifs_ses **ses, struct cifs_tcon **tcon)
-{
-	int rc;
-	char *npath = NULL;
-	struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
-	struct dfs_cache_tgt_iterator *tgt_it = NULL;
-	struct smb3_fs_context tmp_ctx = {NULL};
-
-	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
-		return -EOPNOTSUPP;
-
-	npath = dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb));
-	if (IS_ERR(npath))
-		return PTR_ERR(npath);
-
-	cifs_dbg(FYI, "%s: path=%s full_path=%s\n", __func__, npath, full_path);
-
-	rc = dfs_cache_noreq_find(npath, NULL, &tgt_list);
-	if (rc)
-		goto out;
-	/*
-	 * We use a 'tmp_ctx' here because we need pass it down to the mount_{get,put} functions to
-	 * test connection against new DFS targets.
-	 */
-	rc = smb3_fs_context_dup(&tmp_ctx, ctx);
-	if (rc)
-		goto out;
-
-	for (;;) {
-		struct dfs_info3_param ref = {0};
-		char *fake_devname = NULL, *mdata = NULL;
-
-		/* Get next DFS target server - if any */
-		rc = get_next_dfs_tgt(&tgt_list, &tgt_it);
-		if (rc)
-			break;
-
-		rc = dfs_cache_get_tgt_referral(npath, tgt_it, &ref);
-		if (rc)
-			break;
-
-		cifs_dbg(FYI, "%s: old ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
-			 tmp_ctx.prepath);
-
-		mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, &ref,
-						   &fake_devname);
-		free_dfs_info_param(&ref);
-
-		if (IS_ERR(mdata)) {
-			rc = PTR_ERR(mdata);
-			mdata = NULL;
-		} else
-			rc = cifs_setup_volume_info(&tmp_ctx, mdata, fake_devname);
-
-		kfree(mdata);
-		kfree(fake_devname);
-
-		if (rc)
-			break;
-
-		cifs_dbg(FYI, "%s: new ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
-			 tmp_ctx.prepath);
-
-		mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
-		rc = mount_get_dfs_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
-		if (!rc || (*server && *ses)) {
-			/*
-			 * We were able to connect to new target server. Update current context with
-			 * new target server.
-			 */
-			rc = update_vol_info(tgt_it, &tmp_ctx, ctx);
-			break;
-		}
-	}
-	if (!rc) {
-		cifs_dbg(FYI, "%s: final ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
-			 tmp_ctx.prepath);
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+	char *fake_devname = NULL, *mdata = NULL;
+
+	mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral,
+					   &fake_devname);
+	if (IS_ERR(mdata)) {
+		rc = PTR_ERR(mdata);
+		mdata = NULL;
+	} else {
 		/*
-		 * Update DFS target hint in DFS referral cache with the target server we
-		 * successfully reconnected to.
+		 * We can not clear out the whole structure since we no longer have an explicit
+		 * function to parse a mount-string. Instead we need to clear out the individual
+		 * fields that are no longer valid.
 		 */
-		rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, cifs_sb->local_nls,
-					      cifs_remap(cifs_sb), path, tgt_it);
+		kfree(ctx->prepath);
+		ctx->prepath = NULL;
+		rc = cifs_setup_volume_info(ctx, mdata, fake_devname);
 	}
+	kfree(fake_devname);
+	kfree(cifs_sb->ctx->mount_options);
+	cifs_sb->ctx->mount_options = mdata;
 
-out:
-	kfree(npath);
-	smb3_cleanup_fs_context_contents(&tmp_ctx);
-	dfs_cache_free_tgts(&tgt_list);
 	return rc;
 }
 #endif
@@ -3393,12 +3256,14 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
  * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is,
  * otherwise 0.
  */
-static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
-			  const unsigned int xid,
-			  struct TCP_Server_Info *server,
-			  struct cifs_tcon *tcon)
+static int is_path_remote(struct mount_ctx *mnt_ctx)
 {
 	int rc;
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	struct TCP_Server_Info *server = mnt_ctx->server;
+	unsigned int xid = mnt_ctx->xid;
+	struct cifs_tcon *tcon = mnt_ctx->tcon;
+	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
 	char *full_path;
 
 	if (!server->ops->is_path_accessible)
@@ -3436,280 +3301,289 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *
 }
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
-static void set_root_ses(struct cifs_sb_info *cifs_sb, const uuid_t *mount_id, struct cifs_ses *ses,
-			 struct cifs_ses **root_ses)
+static void set_root_ses(struct mount_ctx *mnt_ctx)
 {
-	if (ses) {
+	if (mnt_ctx->ses) {
 		spin_lock(&cifs_tcp_ses_lock);
-		ses->ses_count++;
+		mnt_ctx->ses->ses_count++;
 		spin_unlock(&cifs_tcp_ses_lock);
-		dfs_cache_add_refsrv_session(mount_id, ses);
+		dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses);
 	}
-	*root_ses = ses;
+	mnt_ctx->root_ses = mnt_ctx->ses;
 }
 
-/* Set up next dfs prefix path in @dfs_path */
-static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
-			    const unsigned int xid, struct TCP_Server_Info *server,
-			    struct cifs_tcon *tcon, char **dfs_path)
+static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl)
 {
-	char *path, *npath;
-	int added_treename = is_tcon_dfs(tcon);
 	int rc;
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
 
-	path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename);
-	if (!path)
-		return -ENOMEM;
+	*isdfs = true;
 
-	rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
-	if (rc == -EREMOTE) {
-		struct smb3_fs_context v = {NULL};
-		/* if @path contains a tree name, skip it in the prefix path */
-		if (added_treename) {
-			rc = smb3_parse_devname(path, &v);
-			if (rc)
-				goto out;
-			npath = build_unc_path_to_root(&v, cifs_sb, true);
-			smb3_cleanup_fs_context_contents(&v);
-		} else {
-			v.UNC = ctx->UNC;
-			v.prepath = path + 1;
-			npath = build_unc_path_to_root(&v, cifs_sb, true);
-		}
+	rc = mount_get_conns(mnt_ctx);
+	/*
+	 * If called with 'nodfs' mount option, then skip DFS resolving.  Otherwise unconditionally
+	 * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
+	 *
+	 * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
+	 * to respond with PATH_NOT_COVERED to requests that include the prefix.
+	 */
+	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
+	    dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
+			   ctx->UNC + 1, NULL, root_tl)) {
+		if (rc)
+			return rc;
+		/* Check if it is fully accessible and then mount it */
+		rc = is_path_remote(mnt_ctx);
+		if (!rc)
+			*isdfs = false;
+		else if (rc != -EREMOTE)
+			return rc;
+	}
+	return 0;
+}
 
-		if (IS_ERR(npath)) {
-			rc = PTR_ERR(npath);
-			goto out;
-		}
+static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path,
+			      const char *ref_path, struct dfs_cache_tgt_iterator *tit)
+{
+	int rc;
+	struct dfs_info3_param ref = {};
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	char *oldmnt = cifs_sb->ctx->mount_options;
 
-		kfree(*dfs_path);
-		*dfs_path = npath;
-		rc = -EREMOTE;
+	rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref);
+	if (rc)
+		goto out;
+
+	rc = expand_dfs_referral(mnt_ctx, full_path, &ref);
+	if (rc)
+		goto out;
+
+	/* Connect to new target only if we were redirected (e.g. mount options changed) */
+	if (oldmnt != cifs_sb->ctx->mount_options) {
+		mount_put_conns(mnt_ctx);
+		rc = mount_get_dfs_conns(mnt_ctx);
+	}
+	if (!rc) {
+		if (cifs_is_referral_server(mnt_ctx->tcon, &ref))
+			set_root_ses(mnt_ctx);
+		rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls,
+					      cifs_remap(cifs_sb), ref_path, tit);
 	}
 
 out:
-	kfree(path);
+	free_dfs_info_param(&ref);
 	return rc;
 }
 
-/* Check if resolved targets can handle any DFS referrals */
-static int is_referral_server(const char *ref_path, struct cifs_sb_info *cifs_sb,
-			      struct cifs_tcon *tcon, bool *ref_server)
+static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl)
 {
 	int rc;
-	struct dfs_info3_param ref = {0};
+	char *full_path;
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+	struct dfs_cache_tgt_iterator *tit;
 
-	cifs_dbg(FYI, "%s: ref_path=%s\n", __func__, ref_path);
+	/* Put initial connections as they might be shared with other mounts.  We need unique dfs
+	 * connections per mount to properly failover, so mount_get_dfs_conns() must be used from
+	 * now on.
+	 */
+	mount_put_conns(mnt_ctx);
+	mount_get_dfs_conns(mnt_ctx);
 
-	if (is_tcon_dfs(tcon)) {
-		*ref_server = true;
-	} else {
-		char *npath;
+	full_path = build_unc_path_to_root(ctx, cifs_sb, true);
+	if (IS_ERR(full_path))
+		return PTR_ERR(full_path);
 
-		npath = dfs_cache_canonical_path(ref_path, cifs_sb->local_nls, cifs_remap(cifs_sb));
-		if (IS_ERR(npath))
-			return PTR_ERR(npath);
+	mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls,
+							    cifs_remap(cifs_sb));
+	if (IS_ERR(mnt_ctx->origin_fullpath)) {
+		rc = PTR_ERR(mnt_ctx->origin_fullpath);
+		mnt_ctx->origin_fullpath = NULL;
+		goto out;
+	}
 
-		rc = dfs_cache_noreq_find(npath, &ref, NULL);
-		kfree(npath);
-		if (rc) {
-			cifs_dbg(VFS, "%s: dfs_cache_noreq_find: failed (rc=%d)\n", __func__, rc);
-			return rc;
+	/* Try all dfs root targets */
+	for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl);
+	     tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) {
+		rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit);
+		if (!rc) {
+			mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL);
+			if (!mnt_ctx->leaf_fullpath)
+				rc = -ENOMEM;
+			break;
 		}
-		cifs_dbg(FYI, "%s: ref.flags=0x%x\n", __func__, ref.flags);
-		/*
-		 * Check if all targets are capable of handling DFS referrals as per
-		 * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
-		 */
-		*ref_server = !!(ref.flags & DFSREF_REFERRAL_SERVER);
-		free_dfs_info_param(&ref);
 	}
-	return 0;
+
+out:
+	kfree(full_path);
+	return rc;
 }
 
-int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
+static int __follow_dfs_link(struct mount_ctx *mnt_ctx)
 {
-	int rc = 0;
-	unsigned int xid;
-	struct TCP_Server_Info *server = NULL;
-	struct cifs_ses *ses = NULL, *root_ses = NULL;
-	struct cifs_tcon *tcon = NULL;
-	int count = 0;
-	uuid_t mount_id = {0};
-	char *ref_path = NULL, *full_path = NULL;
-	char *oldmnt = NULL;
-	bool ref_server = false;
+	int rc;
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+	char *full_path;
+	struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
+	struct dfs_cache_tgt_iterator *tit;
 
-	rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
-	/*
-	 * If called with 'nodfs' mount option, then skip DFS resolving.  Otherwise unconditionally
-	 * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
-	 *
-	 * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
-	 * to respond with PATH_NOT_COVERED to requests that include the prefix.
-	 */
-	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
-	    dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL,
-			   NULL)) {
-		if (rc)
-			goto error;
-		/* Check if it is fully accessible and then mount it */
-		rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
-		if (!rc)
-			goto out;
-		if (rc != -EREMOTE)
-			goto error;
+	full_path = build_unc_path_to_root(ctx, cifs_sb, true);
+	if (IS_ERR(full_path))
+		return PTR_ERR(full_path);
+
+	kfree(mnt_ctx->leaf_fullpath);
+	mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls,
+							  cifs_remap(cifs_sb));
+	if (IS_ERR(mnt_ctx->leaf_fullpath)) {
+		rc = PTR_ERR(mnt_ctx->leaf_fullpath);
+		mnt_ctx->leaf_fullpath = NULL;
+		goto out;
 	}
 
-	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(ctx, cifs_sb, &xid, &server, &ses, &tcon);
+	/* Get referral from dfs link */
+	rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls,
+			    cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl);
+	if (rc)
+		goto out;
 
-	/* Get path of DFS root */
-	ref_path = build_unc_path_to_root(ctx, cifs_sb, false);
-	if (IS_ERR(ref_path)) {
-		rc = PTR_ERR(ref_path);
-		ref_path = NULL;
-		goto error;
+	/* Try all dfs link targets */
+	for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl);
+	     tit; tit = dfs_cache_get_next_tgt(&tl, tit)) {
+		rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit);
+		if (!rc) {
+			rc = is_path_remote(mnt_ctx);
+			break;
+		}
+	}
+
+out:
+	kfree(full_path);
+	dfs_cache_free_tgts(&tl);
+	return rc;
+}
+
+static int follow_dfs_link(struct mount_ctx *mnt_ctx)
+{
+	int rc;
+	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
+	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+	char *full_path;
+	int num_links = 0;
+
+	full_path = build_unc_path_to_root(ctx, cifs_sb, true);
+	if (IS_ERR(full_path))
+		return PTR_ERR(full_path);
+
+	kfree(mnt_ctx->origin_fullpath);
+	mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls,
+							    cifs_remap(cifs_sb));
+	kfree(full_path);
+
+	if (IS_ERR(mnt_ctx->origin_fullpath)) {
+		rc = PTR_ERR(mnt_ctx->origin_fullpath);
+		mnt_ctx->origin_fullpath = NULL;
+		return rc;
 	}
 
-	uuid_gen(&mount_id);
-	set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
 	do {
-		/* Save full path of last DFS path we used to resolve final target server */
-		kfree(full_path);
-		full_path = build_unc_path_to_root(ctx, cifs_sb, !!count);
-		if (IS_ERR(full_path)) {
-			rc = PTR_ERR(full_path);
-			full_path = NULL;
-			break;
-		}
-		/* Chase referral */
-		oldmnt = cifs_sb->ctx->mount_options;
-		rc = expand_dfs_referral(xid, root_ses, ctx, cifs_sb, ref_path + 1);
-		if (rc)
+		rc = __follow_dfs_link(mnt_ctx);
+		if (!rc || rc != -EREMOTE)
 			break;
-		/* Connect to new DFS target only if we were redirected */
-		if (oldmnt != cifs_sb->ctx->mount_options) {
-			mount_put_conns(cifs_sb, xid, server, ses, tcon);
-			rc = mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
-		}
-		if (rc && !server && !ses) {
-			/* Failed to connect. Try to connect to other targets in the referral. */
-			rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, ctx, root_ses, &xid,
-					     &server, &ses, &tcon);
-		}
-		if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses)
-			break;
-		if (!tcon)
-			continue;
+	} while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
 
-		/* Make sure that requests go through new root servers */
-		rc = is_referral_server(ref_path + 1, cifs_sb, tcon, &ref_server);
-		if (rc)
-			break;
-		if (ref_server)
-			set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
+	return rc;
+}
 
-		/* Get next dfs path and then continue chasing them if -EREMOTE */
-		rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
-		/* Prevent recursion on broken link referrals */
-		if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS)
-			rc = -ELOOP;
-	} while (rc == -EREMOTE);
+/* Set up DFS referral paths for failover */
+static void setup_server_referral_paths(struct mount_ctx *mnt_ctx)
+{
+	struct TCP_Server_Info *server = mnt_ctx->server;
+
+	server->origin_fullpath = mnt_ctx->origin_fullpath;
+	server->leaf_fullpath = mnt_ctx->leaf_fullpath;
+	server->current_fullpath = mnt_ctx->leaf_fullpath;
+	mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL;
+}
 
-	if (rc || !tcon || !ses)
+int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
+{
+	int rc;
+	struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
+	struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
+	bool isdfs;
+
+	rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl);
+	if (rc)
 		goto error;
+	if (!isdfs)
+		goto out;
 
-	kfree(ref_path);
-	/*
-	 * Store DFS full path in both superblock and tree connect structures.
-	 *
-	 * For DFS root mounts, the prefix path (cifs_sb->prepath) is preserved during reconnect so
-	 * only the root path is set in cifs_sb->origin_fullpath and tcon->dfs_path. And for DFS
-	 * links, the prefix path is included in both and may be changed during reconnect.  See
-	 * cifs_tree_connect().
-	 */
-	ref_path = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, cifs_remap(cifs_sb));
-	kfree(full_path);
-	full_path = NULL;
+	uuid_gen(&mnt_ctx.mount_id);
+	rc = connect_dfs_root(&mnt_ctx, &tl);
+	dfs_cache_free_tgts(&tl);
 
-	if (IS_ERR(ref_path)) {
-		rc = PTR_ERR(ref_path);
-		ref_path = NULL;
+	if (rc)
 		goto error;
-	}
-	cifs_sb->origin_fullpath = ref_path;
 
-	ref_path = kstrdup(cifs_sb->origin_fullpath, GFP_KERNEL);
-	if (!ref_path) {
-		rc = -ENOMEM;
+	rc = is_path_remote(&mnt_ctx);
+	if (rc == -EREMOTE)
+		rc = follow_dfs_link(&mnt_ctx);
+	if (rc)
 		goto error;
-	}
-	spin_lock(&cifs_tcp_ses_lock);
-	tcon->dfs_path = ref_path;
-	ref_path = NULL;
-	spin_unlock(&cifs_tcp_ses_lock);
 
+	setup_server_referral_paths(&mnt_ctx);
 	/*
-	 * After reconnecting to a different server, unique ids won't
-	 * match anymore, so we disable serverino. This prevents
-	 * dentry revalidation to think the dentry are stale (ESTALE).
+	 * After reconnecting to a different server, unique ids won't match anymore, so we disable
+	 * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
 	 */
 	cifs_autodisable_serverino(cifs_sb);
 	/*
-	 * Force the use of prefix path to support failover on DFS paths that
-	 * resolve to targets that have different prefix paths.
+	 * Force the use of prefix path to support failover on DFS paths that resolve to targets
+	 * that have different prefix paths.
 	 */
 	cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
 	kfree(cifs_sb->prepath);
 	cifs_sb->prepath = ctx->prepath;
 	ctx->prepath = NULL;
-	uuid_copy(&cifs_sb->dfs_mount_id, &mount_id);
+	uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id);
 
 out:
-	free_xid(xid);
-	cifs_try_adding_channels(cifs_sb, ses);
-	return mount_setup_tlink(cifs_sb, ses, tcon);
+	free_xid(mnt_ctx.xid);
+	cifs_try_adding_channels(cifs_sb, mnt_ctx.ses);
+	return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
 
 error:
-	kfree(ref_path);
-	kfree(full_path);
-	kfree(cifs_sb->origin_fullpath);
-	dfs_cache_put_refsrv_sessions(&mount_id);
-	mount_put_conns(cifs_sb, xid, server, ses, tcon);
+	dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id);
+	kfree(mnt_ctx.origin_fullpath);
+	kfree(mnt_ctx.leaf_fullpath);
+	mount_put_conns(&mnt_ctx);
 	return rc;
 }
 #else
 int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
 {
 	int rc = 0;
-	unsigned int xid;
-	struct cifs_ses *ses;
-	struct cifs_tcon *tcon;
-	struct TCP_Server_Info *server;
+	struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
 
-	rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
+	rc = mount_get_conns(&mnt_ctx);
 	if (rc)
 		goto error;
 
-	if (tcon) {
-		rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
+	if (mnt_ctx.tcon) {
+		rc = is_path_remote(&mnt_ctx);
 		if (rc == -EREMOTE)
 			rc = -EOPNOTSUPP;
 		if (rc)
 			goto error;
 	}
 
-	free_xid(xid);
-
-	return mount_setup_tlink(cifs_sb, ses, tcon);
+	free_xid(mnt_ctx.xid);
+	return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
 
 error:
-	mount_put_conns(cifs_sb, xid, server, ses, tcon);
+	mount_put_conns(&mnt_ctx);
 	return rc;
 }
 #endif
@@ -3877,7 +3751,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
 	kfree(cifs_sb->prepath);
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id);
-	kfree(cifs_sb->origin_fullpath);
 #endif
 	call_rcu(&cifs_sb->rcu, delayed_free);
 }
@@ -4204,104 +4077,249 @@ cifs_prune_tlinks(struct work_struct *work)
 }
 
 #ifdef CONFIG_CIFS_DFS_UPCALL
-int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
+static void mark_tcon_tcp_ses_for_reconnect(struct cifs_tcon *tcon)
+{
+	int i;
+
+	for (i = 0; i < tcon->ses->chan_count; i++) {
+		spin_lock(&GlobalMid_Lock);
+		if (tcon->ses->chans[i].server->tcpStatus != CifsExiting)
+			tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
+		spin_unlock(&GlobalMid_Lock);
+	}
+}
+
+/* Update dfs referral path of superblock */
+static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
+				  const char *target)
+{
+	int rc = 0;
+	size_t len = strlen(target);
+	char *refpath, *npath;
+
+	if (unlikely(len < 2 || *target != '\\'))
+		return -EINVAL;
+
+	if (target[1] == '\\') {
+		len += 1;
+		refpath = kmalloc(len, GFP_KERNEL);
+		if (!refpath)
+			return -ENOMEM;
+
+		scnprintf(refpath, len, "%s", target);
+	} else {
+		len += sizeof("\\");
+		refpath = kmalloc(len, GFP_KERNEL);
+		if (!refpath)
+			return -ENOMEM;
+
+		scnprintf(refpath, len, "\\%s", target);
+	}
+
+	npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
+	kfree(refpath);
+
+	if (IS_ERR(npath)) {
+		rc = PTR_ERR(npath);
+	} else {
+		mutex_lock(&server->refpath_lock);
+		kfree(server->leaf_fullpath);
+		server->leaf_fullpath = npath;
+		mutex_unlock(&server->refpath_lock);
+		server->current_fullpath = server->leaf_fullpath;
+	}
+	return rc;
+}
+
+static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host,
+				       size_t tcp_host_len, char *share, bool *target_match)
+{
+	int rc = 0;
+	const char *dfs_host;
+	size_t dfs_host_len;
+
+	*target_match = true;
+	extract_unc_hostname(share, &dfs_host, &dfs_host_len);
+
+	/* Check if hostnames or addresses match */
+	if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
+		cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
+			 dfs_host, (int)tcp_host_len, tcp_host);
+		rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
+		if (rc)
+			cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
+	}
+	return rc;
+}
+
+int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
+			      struct cifs_sb_info *cifs_sb, char *tree,
+			      struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref)
 {
 	int rc;
 	struct TCP_Server_Info *server = tcon->ses->server;
 	const struct smb_version_operations *ops = server->ops;
-	struct dfs_cache_tgt_list tl;
-	struct dfs_cache_tgt_iterator *it = NULL;
-	char *tree;
+	struct cifs_tcon *ipc = tcon->ses->tcon_ipc;
+	bool islink;
+	char *share = NULL, *prefix = NULL;
 	const char *tcp_host;
 	size_t tcp_host_len;
-	const char *dfs_host;
-	size_t dfs_host_len;
-	char *share = NULL, *prefix = NULL;
-	struct dfs_info3_param ref = {0};
-	bool isroot;
+	struct dfs_cache_tgt_iterator *tit;
+	bool target_match;
 
-	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
-	if (!tree)
-		return -ENOMEM;
+	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
 
-	/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
-	if (!tcon->dfs_path || dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl)) {
-		if (tcon->ipc) {
-			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
-			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
-		} else {
-			rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
-		}
+	islink = ref->server_type == DFS_TYPE_LINK;
+	free_dfs_info_param(ref);
+
+	tit = dfs_cache_get_tgt_iterator(tl);
+	if (!tit) {
+		rc = -ENOENT;
 		goto out;
 	}
 
-	isroot = ref.server_type == DFS_TYPE_ROOT;
-	free_dfs_info_param(&ref);
-
-	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
-
-	for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) {
-		bool target_match;
+	/* Try to tree connect to all dfs targets */
+	for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
+		const char *target = dfs_cache_get_tgt_name(tit);
+		struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl);
 
 		kfree(share);
 		kfree(prefix);
-		share = NULL;
-		prefix = NULL;
 
-		rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix);
+		/* Check if share matches with tcp ses */
+		rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix);
 		if (rc) {
-			cifs_dbg(VFS, "%s: failed to parse target share %d\n",
-				 __func__, rc);
-			continue;
+			cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
+			break;
 		}
 
-		extract_unc_hostname(share, &dfs_host, &dfs_host_len);
-
-		if (dfs_host_len != tcp_host_len
-		    || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
-			cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
-				 dfs_host, (int)tcp_host_len, tcp_host);
+		rc = target_share_matches_server(server, tcp_host, tcp_host_len, share,
+						 &target_match);
+		if (rc)
+			break;
+		if (!target_match) {
+			rc = -EHOSTUNREACH;
+			continue;
+		}
 
-			rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match);
-			if (rc) {
-				cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
+		if (ipc->need_reconnect) {
+			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
+			rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls);
+			if (rc)
 				break;
-			}
+		}
 
-			if (!target_match) {
-				cifs_dbg(FYI, "%s: skipping target\n", __func__);
+		scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
+		if (!islink) {
+			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
+			break;
+		}
+		/*
+		 * If no dfs referrals were returned from link target, then just do a TREE_CONNECT
+		 * to it.  Otherwise, cache the dfs referral and then mark current tcp ses for
+		 * reconnect so either the demultiplex thread or the echo worker will reconnect to
+		 * newly resolved target.
+		 */
+		if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
+				   ref, &ntl)) {
+			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
+			if (rc)
 				continue;
-			}
+			rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit);
+			if (!rc)
+				rc = cifs_update_super_prepath(cifs_sb, prefix);
+			break;
 		}
+		/* Target is another dfs share */
+		rc = update_server_fullpath(server, cifs_sb, target);
+		dfs_cache_free_tgts(tl);
 
-		if (tcon->ipc) {
-			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share);
-			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
+		if (!rc) {
+			rc = -EREMOTE;
+			list_replace_init(&ntl.tl_list, &tl->tl_list);
 		} else {
-			scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
-			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
-			/* Only handle prefix paths of DFS link targets */
-			if (!rc && !isroot) {
-				rc = update_super_prepath(tcon, prefix);
-				break;
-			}
+			dfs_cache_free_tgts(&ntl);
+			free_dfs_info_param(ref);
 		}
-		if (rc == -EREMOTE)
-			break;
+		break;
 	}
 
+out:
 	kfree(share);
 	kfree(prefix);
 
-	if (!rc) {
-		if (it)
-			rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it);
-		else
-			rc = -ENOENT;
+	return rc;
+}
+
+int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
+			    struct cifs_sb_info *cifs_sb, char *tree,
+			    struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref)
+{
+	int rc;
+	int num_links = 0;
+	struct TCP_Server_Info *server = tcon->ses->server;
+
+	do {
+		rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, tl, ref);
+		if (!rc || rc != -EREMOTE)
+			break;
+	} while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
+	/*
+	 * If we couldn't tree connect to any targets from last referral path, then retry from
+	 * original referral path.
+	 */
+	if (rc && server->current_fullpath != server->origin_fullpath) {
+		server->current_fullpath = server->origin_fullpath;
+		mark_tcon_tcp_ses_for_reconnect(tcon);
 	}
-	dfs_cache_free_tgts(&tl);
+
+	dfs_cache_free_tgts(tl);
+	return rc;
+}
+
+int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
+{
+	int rc;
+	struct TCP_Server_Info *server = tcon->ses->server;
+	const struct smb_version_operations *ops = server->ops;
+	struct super_block *sb = NULL;
+	struct cifs_sb_info *cifs_sb;
+	struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
+	char *tree;
+	struct dfs_info3_param ref = {0};
+
+	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
+	if (!tree)
+		return -ENOMEM;
+
+	if (tcon->ipc) {
+		scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
+		rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
+		goto out;
+	}
+
+	sb = cifs_get_tcp_super(server);
+	if (IS_ERR(sb)) {
+		rc = PTR_ERR(sb);
+		cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc);
+		goto out;
+	}
+
+	cifs_sb = CIFS_SB(sb);
+
+	/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
+	if (!server->current_fullpath ||
+	    dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) {
+		rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, cifs_sb->local_nls);
+		goto out;
+	}
+
+	rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, &tl, &ref);
+
 out:
 	kfree(tree);
+	cifs_put_tcp_super(sb);
+
 	return rc;
 }
 #else
diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
index 283745592844..1f3efa7821a0 100644
--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -1364,9 +1364,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach
 }
 
 /* Refresh dfs referral of tcon and mark it for reconnect if needed */
-static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh)
+static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon,
+			  bool force_refresh)
 {
-	const char *path = tcon->dfs_path + 1;
 	struct cifs_ses *ses;
 	struct cache_entry *ce;
 	struct dfs_info3_param *refs = NULL;
@@ -1422,6 +1422,20 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool
 	return rc;
 }
 
+static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh)
+{
+	struct TCP_Server_Info *server = tcon->ses->server;
+
+	mutex_lock(&server->refpath_lock);
+	if (strcasecmp(server->leaf_fullpath, server->origin_fullpath))
+		__refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh);
+	mutex_unlock(&server->refpath_lock);
+
+	__refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh);
+
+	return 0;
+}
+
 /**
  * dfs_cache_remount_fs - remount a DFS share
  *
@@ -1435,6 +1449,7 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool
 int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
 {
 	struct cifs_tcon *tcon;
+	struct TCP_Server_Info *server;
 	struct mount_group *mg;
 	struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL};
 	int rc;
@@ -1443,13 +1458,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
 		return -EINVAL;
 
 	tcon = cifs_sb_master_tcon(cifs_sb);
-	if (!tcon->dfs_path) {
-		cifs_dbg(FYI, "%s: not a dfs tcon\n", __func__);
+	server = tcon->ses->server;
+
+	if (!server->origin_fullpath) {
+		cifs_dbg(FYI, "%s: not a dfs mount\n", __func__);
 		return 0;
 	}
 
 	if (uuid_is_null(&cifs_sb->dfs_mount_id)) {
-		cifs_dbg(FYI, "%s: tcon has no dfs mount group id\n", __func__);
+		cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__);
 		return -EINVAL;
 	}
 
@@ -1457,7 +1474,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
 	mg = find_mount_group_locked(&cifs_sb->dfs_mount_id);
 	if (IS_ERR(mg)) {
 		mutex_unlock(&mount_group_list_lock);
-		cifs_dbg(FYI, "%s: tcon has ipc session to refresh referral\n", __func__);
+		cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__);
 		return PTR_ERR(mg);
 	}
 	kref_get(&mg->refcount);
@@ -1498,9 +1515,12 @@ static void refresh_mounts(struct cifs_ses **sessions)
 
 	spin_lock(&cifs_tcp_ses_lock);
 	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+		if (!server->is_dfs_conn)
+			continue;
+
 		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
 			list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
-				if (tcon->dfs_path) {
+				if (!tcon->ipc && !tcon->need_reconnect) {
 					tcon->tc_count++;
 					list_add_tail(&tcon->ulist, &tcons);
 				}
@@ -1510,8 +1530,16 @@ static void refresh_mounts(struct cifs_ses **sessions)
 	spin_unlock(&cifs_tcp_ses_lock);
 
 	list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
+		struct TCP_Server_Info *server = tcon->ses->server;
+
 		list_del_init(&tcon->ulist);
-		refresh_tcon(sessions, tcon, false);
+
+		mutex_lock(&server->refpath_lock);
+		if (strcasecmp(server->leaf_fullpath, server->origin_fullpath))
+			__refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false);
+		mutex_unlock(&server->refpath_lock);
+
+		__refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false);
 		cifs_put_tcon(tcon);
 	}
 }
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 699f676ded47..94143d7f58c7 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -139,9 +139,6 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
 	kfree(buf_to_free->nativeFileSystem);
 	kfree_sensitive(buf_to_free->password);
 	kfree(buf_to_free->crfid.fid);
-#ifdef CONFIG_CIFS_DFS_UPCALL
-	kfree(buf_to_free->dfs_path);
-#endif
 	kfree(buf_to_free);
 }
 
@@ -1299,69 +1296,20 @@ int match_target_ip(struct TCP_Server_Info *server,
 	return rc;
 }
 
-static void tcon_super_cb(struct super_block *sb, void *arg)
+int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
 {
-	struct super_cb_data *sd = arg;
-	struct cifs_tcon *tcon = sd->data;
-	struct cifs_sb_info *cifs_sb;
-
-	if (sd->sb)
-		return;
-
-	cifs_sb = CIFS_SB(sb);
-	if (tcon->dfs_path && cifs_sb->origin_fullpath &&
-	    !strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath))
-		sd->sb = sb;
-}
-
-static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
-{
-	return __cifs_get_super(tcon_super_cb, tcon);
-}
-
-static inline void cifs_put_tcon_super(struct super_block *sb)
-{
-	__cifs_put_super(sb);
-}
-#else
-static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
-{
-	return ERR_PTR(-EOPNOTSUPP);
-}
-
-static inline void cifs_put_tcon_super(struct super_block *sb)
-{
-}
-#endif
-
-int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
-{
-	struct super_block *sb;
-	struct cifs_sb_info *cifs_sb;
-	int rc = 0;
-
-	sb = cifs_get_tcon_super(tcon);
-	if (IS_ERR(sb))
-		return PTR_ERR(sb);
-
-	cifs_sb = CIFS_SB(sb);
-
 	kfree(cifs_sb->prepath);
 
 	if (prefix && *prefix) {
 		cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC);
-		if (!cifs_sb->prepath) {
-			rc = -ENOMEM;
-			goto out;
-		}
+		if (!cifs_sb->prepath)
+			return -ENOMEM;
 
 		convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
 	} else
 		cifs_sb->prepath = NULL;
 
 	cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
-
-out:
-	cifs_put_tcon_super(sb);
-	return rc;
+	return 0;
 }
+#endif
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 53e87466e3b2..5e6526c201fe 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -2869,6 +2869,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
 	struct fsctl_get_dfs_referral_req *dfs_req = NULL;
 	struct get_dfs_referral_rsp *dfs_rsp = NULL;
 	u32 dfs_req_size = 0, dfs_rsp_size = 0;
+	int retry_count = 0;
 
 	cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name);
 
@@ -2920,11 +2921,14 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
 				true /* is_fsctl */,
 				(char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
 				(char **)&dfs_rsp, &dfs_rsp_size);
-	} while (rc == -EAGAIN);
+		if (!is_retryable_error(rc))
+			break;
+		usleep_range(512, 2048);
+	} while (++retry_count < 5);
 
 	if (rc) {
-		if ((rc != -ENOENT) && (rc != -EOPNOTSUPP))
-			cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc);
+		if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
+			cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);
 		goto out;
 	}
 
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 8aa0372141f5..c0ea2813978b 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -156,7 +156,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
 	if (tcon == NULL)
 		return 0;
 
-	if (smb2_command == SMB2_TREE_CONNECT)
+	/*
+	 * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in
+	 * cifs_tree_connect().
+	 */
+	if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
 		return 0;
 
 	if (tcon->tidStatus == CifsExiting) {
-- 
2.35.1




  parent reply	other threads:[~2022-11-30 18:32 UTC|newest]

Thread overview: 217+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-30 18:20 [PATCH 5.15 000/206] 5.15.81-rc1 review Greg Kroah-Hartman
2022-11-30 18:20 ` [PATCH 5.15 001/206] ASoC: fsl_sai: use local device pointer Greg Kroah-Hartman
2022-11-30 18:20 ` [PATCH 5.15 002/206] ASoC: fsl_asrc fsl_esai fsl_sai: allow CONFIG_PM=N Greg Kroah-Hartman
2022-11-30 18:20 ` [PATCH 5.15 003/206] serial: Add rs485_supported to uart_port Greg Kroah-Hartman
2022-11-30 18:20 ` [PATCH 5.15 004/206] serial: fsl_lpuart: Fill in rs485_supported Greg Kroah-Hartman
2022-11-30 18:20 ` [PATCH 5.15 005/206] tty: serial: fsl_lpuart: dont break the on-going transfer when global reset Greg Kroah-Hartman
2022-11-30 18:20 ` [PATCH 5.15 006/206] sctp: remove the unnecessary sinfo_stream check in sctp_prsctp_prune_unsent Greg Kroah-Hartman
2022-11-30 18:20 ` [PATCH 5.15 007/206] sctp: clear out_curr if all frag chunks of current msg are pruned Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 008/206] cifs: introduce new helper for cifs_reconnect() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 009/206] cifs: split out dfs code from cifs_reconnect() Greg Kroah-Hartman
2022-11-30 18:21 ` Greg Kroah-Hartman [this message]
2022-11-30 18:21 ` [PATCH 5.15 011/206] cifs: Fix connections leak when tlink setup failed Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 012/206] ata: libata-scsi: simplify __ata_scsi_queuecmd() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 013/206] ata: libata-core: do not issue non-internal commands once EH is pending Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 014/206] drm/display: Dont assume dual mode adaptors support i2c sub-addressing Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 015/206] nvme: add a bogus subsystem NQN quirk for Micron MTFDKBA2T0TFH Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 016/206] nvme-pci: add NVME_QUIRK_BOGUS_NID for Micron Nitro Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 017/206] nvme-pci: disable namespace identifiers for the MAXIO MAP1001 Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 018/206] nvme-pci: disable write zeroes on various Kingston SSD Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 019/206] nvme-pci: add NVME_QUIRK_BOGUS_NID for Netac NV7000 Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 020/206] speakup: Generate speakupmap.h automatically Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 021/206] speakup: replace utils u_char with unsigned char Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 022/206] iio: ms5611: Simplify IO callback parameters Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 023/206] iio: pressure: ms5611: fixed value compensation bug Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 024/206] ceph: do not update snapshot context when there is no new snapshot Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 025/206] ceph: avoid putting the realm twice when decoding snaps fails Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 026/206] x86/sgx: Create utility to validate user provided offset and length Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 027/206] x86/sgx: Add overflow check in sgx_validate_offset_length() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 028/206] binder: validate alloc->mm in ->mmap() handler Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 029/206] ceph: Use kcalloc for allocating multiple elements Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 030/206] ceph: fix NULL pointer dereference for req->r_session Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 031/206] wifi: mac80211: fix memory free error when registering wiphy fail Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 032/206] wifi: mac80211_hwsim: fix debugfs attribute ps with rc table support Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 033/206] riscv: dts: sifive unleashed: Add PWM controlled LEDs Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 034/206] audit: fix undefined behavior in bit shift for AUDIT_BIT Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 035/206] wifi: airo: do not assign -1 to unsigned char Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 036/206] wifi: mac80211: Fix ack frame idr leak when mesh has no route Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 037/206] wifi: ath11k: Fix QCN9074 firmware boot on x86 Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 038/206] spi: stm32: fix stm32_spi_prepare_mbr() that halves spi clk for every run Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 039/206] selftests/bpf: Add verifier test for release_reference() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 040/206] Revert "net: macsec: report real_dev features when HW offloading is enabled" Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 041/206] platform/x86: ideapad-laptop: Disable touchpad_switch Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 042/206] platform/x86: touchscreen_dmi: Add info for the RCA Cambio W101 v2 2-in-1 Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 043/206] platform/x86/intel/pmt: Sapphire Rapids PMT errata fix Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 044/206] platform/x86/intel/hid: Add some ACPI device IDs Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 045/206] scsi: ibmvfc: Avoid path failures during live migration Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 046/206] scsi: scsi_debug: Make the READ CAPACITY response compliant with ZBC Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 047/206] drm: panel-orientation-quirks: Add quirk for Acer Switch V 10 (SW5-017) Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 048/206] block, bfq: fix null pointer dereference in bfq_bio_bfqg() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 049/206] arm64/syscall: Include asm/ptrace.h in syscall_wrapper header Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 050/206] nvmet: fix memory leak in nvmet_subsys_attr_model_store_locked Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 051/206] Revert "drm/amdgpu: Revert "drm/amdgpu: getting fan speed pwm for vega10 properly"" Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 052/206] ALSA: usb-audio: add quirk to fix Hamedal C20 disconnect issue Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 053/206] RISC-V: vdso: Do not add missing symbols to version section in linker script Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 054/206] MIPS: pic32: treat port as signed integer Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 055/206] xfrm: fix "disable_policy" on ipv4 early demux Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 056/206] xfrm: replay: Fix ESN wrap around for GSO Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 057/206] af_key: Fix send_acquire race with pfkey_register Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 058/206] ARM: dts: am335x-pcm-953: Define fixed regulators in root node Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 059/206] ASoC: hdac_hda: fix hda pcm buffer overflow issue Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 060/206] ASoC: sgtl5000: Reset the CHIP_CLK_CTRL reg on remove Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 061/206] ASoC: soc-pcm: Dont zero TDM masks in __soc_pcm_open() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 062/206] x86/hyperv: Restore VP assist page after cpu offlining/onlining Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 063/206] scsi: storvsc: Fix handling of srb_status and capacity change events Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 064/206] ASoC: max98373: Add checks for devm_kcalloc Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 065/206] regulator: core: fix kobject release warning and memory leak in regulator_register() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 066/206] spi: dw-dma: decrease reference count in dw_spi_dma_init_mfld() Greg Kroah-Hartman
2022-11-30 18:21 ` [PATCH 5.15 067/206] regulator: core: fix UAF in destroy_regulator() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 068/206] bus: sunxi-rsb: Remove the shutdown callback Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 069/206] bus: sunxi-rsb: Support atomic transfers Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 070/206] tee: optee: fix possible memory leak in optee_register_device() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 071/206] ARM: dts: at91: sam9g20ek: enable udc vbus gpio pinctrl Greg Kroah-Hartman
2022-11-30 18:22   ` Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 072/206] selftests: mptcp: more stable simult_flows tests Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 073/206] selftests: mptcp: fix mibit vs mbit mix up Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 074/206] net: liquidio: simplify if expression Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 075/206] rxrpc: Allow list of in-use local UDP endpoints to be viewed in /proc Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 076/206] rxrpc: Use refcount_t rather than atomic_t Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 077/206] rxrpc: Fix race between conn bundle lookup and bundle removal [ZDI-CAN-15975] Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 078/206] net: dsa: sja1105: disallow C45 transactions on the BASE-TX MDIO bus Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 079/206] nfc/nci: fix race with opening and closing Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 080/206] net: pch_gbe: fix potential memleak in pch_gbe_tx_queue() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 081/206] 9p/fd: fix issue of list_del corruption in p9_fd_cancel() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 082/206] netfilter: conntrack: Fix data-races around ct mark Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 083/206] netfilter: nf_tables: do not set up extensions for end interval Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 084/206] iavf: Fix a crash during reset task Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 085/206] iavf: Do not restart Tx queues after reset task failure Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 086/206] iavf: Fix race condition between iavf_shutdown and iavf_remove Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 087/206] ARM: mxs: fix memory leak in mxs_machine_init() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 088/206] ARM: dts: imx6q-prti6q: Fix ref/tcxo-clock-frequency properties Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 089/206] net: ethernet: mtk_eth_soc: fix error handling in mtk_open() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 090/206] net/mlx4: Check retval of mlx4_bitmap_init Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 091/206] net: mvpp2: fix possible invalid pointer dereference Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 092/206] net/qla3xxx: fix potential memleak in ql3xxx_send() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 093/206] octeontx2-af: debugsfs: fix pci device refcount leak Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 094/206] net: pch_gbe: fix pci device refcount leak while module exiting Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 095/206] nfp: fill splittable of devlink_port_attrs correctly Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 096/206] nfp: add port from netdev validation for EEPROM access Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 097/206] macsec: Fix invalid error code set Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 098/206] Drivers: hv: vmbus: fix double free in the error path of vmbus_add_channel_work() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 099/206] Drivers: hv: vmbus: fix possible memory leak in vmbus_device_register() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 100/206] netfilter: ipset: regression in ip_set_hash_ip.c Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 101/206] net/mlx5: Do not query pci info while pci disabled Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 102/206] net/mlx5: Fix FW tracer timestamp calculation Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 103/206] net/mlx5: Fix handling of entry refcount when command is not issued to FW Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 104/206] tipc: set con sock in tipc_conn_alloc Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 105/206] tipc: add an extra conn_get " Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 106/206] tipc: check skb_linearize() return value in tipc_disc_rcv() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 107/206] xfrm: Fix oops in __xfrm_state_delete() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 108/206] xfrm: Fix ignored return value in xfrm6_init() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 109/206] net: wwan: iosm: use ACPI_FREE() but not kfree() in ipc_pcie_read_bios_cfg() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 110/206] sfc: fix potential memleak in __ef100_hard_start_xmit() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 111/206] net: sparx5: fix error handling in sparx5_port_open() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 112/206] net: sched: allow act_ct to be built without NF_NAT Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 113/206] NFC: nci: fix memory leak in nci_rx_data_packet() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 114/206] regulator: twl6030: re-add TWL6032_SUBCLASS Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 115/206] bnx2x: fix pci device refcount leak in bnx2x_vf_is_pcie_pending() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 116/206] dma-buf: fix racing conflict of dma_heap_add() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 117/206] netfilter: ipset: restore allowing 64 clashing elements in hash:net,iface Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 118/206] netfilter: flowtable_offload: add missing locking Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 119/206] fs: do not update freeing inode i_io_list Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 120/206] dccp/tcp: Reset saddr on failure after inet6?_hash_connect() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 121/206] ipv4: Fix error return code in fib_table_insert() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 122/206] arcnet: fix potential memory leak in com20020_probe() Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 123/206] s390/dasd: fix no record found for raw_track_access Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 124/206] nfc: st-nci: fix incorrect validating logic in EVT_TRANSACTION Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 125/206] nfc: st-nci: fix memory leaks " Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 126/206] nfc: st-nci: fix incorrect sizing calculations " Greg Kroah-Hartman
2022-11-30 18:22 ` [PATCH 5.15 127/206] net: enetc: manage ENETC_F_QBV in priv->active_offloads only when enabled Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 128/206] net: enetc: cache accesses to &priv->si->hw Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 129/206] net: enetc: preserve TX ring priority across reconfiguration Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 130/206] octeontx2-pf: Add check for devm_kcalloc Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 131/206] octeontx2-af: Fix reference count issue in rvu_sdp_init() Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 132/206] net: thunderx: Fix the ACPI memory leak Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 133/206] s390/crashdump: fix TOD programmable field size Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 134/206] lib/vdso: use "grep -E" instead of "egrep" Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 135/206] init/Kconfig: fix CC_HAS_ASM_GOTO_TIED_OUTPUT test with dash Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 136/206] nios2: add FORCE for vmlinuz.gz Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 137/206] mmc: sdhci-brcmstb: Re-organize flags Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 138/206] mmc: sdhci-brcmstb: Enable Clock Gating to save power Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 139/206] mmc: sdhci-brcmstb: Fix SDHCI_RESET_ALL for CQHCI Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 140/206] KVM: arm64: pkvm: Fixup boot mode to reflect that the kernel resumes from EL1 Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 141/206] usb: dwc3: exynos: Fix remove() function Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 142/206] usb: cdnsp: Fix issue with Clear Feature Halt Endpoint Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 143/206] usb: cdnsp: fix issue with ZLP - added TD_SIZE = 1 Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 144/206] ext4: fix use-after-free in ext4_ext_shift_extents Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 145/206] arm64: dts: rockchip: lower rk3399-puma-haikou SD controller clock frequency Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 146/206] kbuild: fix -Wimplicit-function-declaration in license_is_gpl_compatible Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 147/206] iio: light: apds9960: fix wrong register for gesture gain Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 148/206] iio: core: Fix entry not deleted when iio_register_sw_trigger_type() fails Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 149/206] bus: ixp4xx: Dont touch bit 7 on IXP42x Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 150/206] usb: dwc3: gadget: conditionally remove requests Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 151/206] usb: dwc3: gadget: Return -ESHUTDOWN on ep disable Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 152/206] usb: dwc3: gadget: Clear ep descriptor last Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 153/206] nilfs2: fix nilfs_sufile_mark_dirty() not set segment usage as dirty Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 154/206] gcov: clang: fix the buffer overflow issue Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 155/206] mm: vmscan: fix extreme overreclaim and swap floods Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 156/206] KVM: x86: nSVM: leave nested mode on vCPU free Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 157/206] KVM: x86: forcibly leave nested mode on vCPU reset Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 158/206] KVM: x86: nSVM: harden svm_free_nested against freeing vmcb02 while still in use Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 159/206] KVM: x86: add kvm_leave_nested Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 160/206] KVM: x86: remove exit_int_info warning in svm_handle_exit Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 161/206] x86/tsx: Add a feature bit for TSX control MSR support Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 162/206] x86/pm: Add enumeration check before spec MSRs save/restore setup Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 163/206] x86/ioremap: Fix page aligned size calculation in __ioremap_caller() Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 164/206] Input: synaptics - switch touchpad on HP Laptop 15-da3001TU to RMI mode Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 165/206] ASoC: Intel: bytcht_es8316: Add quirk for the Nanote UMPC-01 Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 166/206] tools: iio: iio_generic_buffer: Fix read size Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 167/206] serial: 8250: 8250_omap: Avoid RS485 RTS glitch on ->set_termios() Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 168/206] Input: goodix - try resetting the controller when no config is set Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 169/206] Input: soc_button_array - add use_low_level_irq module parameter Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 170/206] Input: soc_button_array - add Acer Switch V 10 to dmi_use_low_level_irq[] Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 171/206] Input: i8042 - apply probe defer to more ASUS ZenBook models Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 172/206] ASoC: stm32: dfsdm: manage cb buffers cleanup Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 173/206] xen-pciback: Allow setting PCI_MSIX_FLAGS_MASKALL too Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 174/206] xen/platform-pci: add missing free_irq() in error path Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 175/206] platform/x86: asus-wmi: add missing pci_dev_put() in asus_wmi_set_xusb2pr() Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 176/206] platform/x86: acer-wmi: Enable SW_TABLET_MODE on Switch V 10 (SW5-017) Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 177/206] drm/amdgpu: disable BACO support on more cards Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 178/206] zonefs: fix zone report size in __zonefs_io_error() Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 179/206] platform/x86: hp-wmi: Ignore Smart Experience App event Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 180/206] platform/x86: ideapad-laptop: Fix interrupt storm on fn-lock toggle on some Yoga laptops Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 181/206] tcp: configurable source port perturb table size Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 182/206] net: usb: qmi_wwan: add Telit 0x103a composition Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 183/206] scsi: iscsi: Fix possible memory leak when device_register() failed Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 184/206] gpu: host1x: Avoid trying to use GART on Tegra20 Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 185/206] dm integrity: flush the journal on suspend Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 186/206] dm integrity: clear " Greg Kroah-Hartman
2022-11-30 18:23 ` [PATCH 5.15 187/206] fuse: lock inode unconditionally in fuse_fallocate() Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 188/206] wifi: wilc1000: validate pairwise and authentication suite offsets Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 189/206] wifi: wilc1000: validate length of IEEE80211_P2P_ATTR_OPER_CHANNEL attribute Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 190/206] wifi: wilc1000: validate length of IEEE80211_P2P_ATTR_CHANNEL_LIST attribute Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 191/206] wifi: wilc1000: validate number of channels Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 192/206] genirq/msi: Shutdown managed interrupts with unsatifiable affinities Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 193/206] genirq: Always limit the affinity to online CPUs Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 194/206] irqchip/gic-v3: Always trust the managed affinity provided by the core code Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 195/206] genirq: Take the proposed affinity at face value if force==true Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 196/206] btrfs: free btrfs_path before copying root refs to userspace Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 197/206] btrfs: free btrfs_path before copying fspath " Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 198/206] btrfs: free btrfs_path before copying subvol info " Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 199/206] btrfs: zoned: fix missing endianness conversion in sb_write_pointer Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 200/206] btrfs: use kvcalloc in btrfs_get_dev_zone_info Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 201/206] btrfs: sysfs: normalize the error handling branch in btrfs_init_sysfs() Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 202/206] drm/amd/dc/dce120: Fix audio register mapping, stop triggering KASAN Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 203/206] drm/amd/display: No display after resume from WB/CB Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 204/206] drm/amdgpu: Enable Aldebaran devices to report CU Occupancy Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 205/206] drm/amdgpu: always register an MMU notifier for userptr Greg Kroah-Hartman
2022-11-30 18:24 ` [PATCH 5.15 206/206] drm/i915: fix TLB invalidation for Gen12 video and compute engines Greg Kroah-Hartman
2022-11-30 20:23 ` [PATCH 5.15 000/206] 5.15.81-rc1 review Florian Fainelli
2022-12-01  1:00 ` Shuah Khan
2022-12-01  7:56 ` Bagas Sanjaya
2022-12-01 11:12 ` Naresh Kamboju
2022-12-01 11:16 ` Sudip Mukherjee
2022-12-01 22:39 ` Kelsey Steele
2022-12-01 22:45 ` Ron Economos
2022-12-02  1:44 ` Guenter Roeck
2022-12-02 12:48 ` Jon Hunter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221130180533.258073946@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=patches@lists.linux.dev \
    --cc=pc@cjr.nz \
    --cc=sashal@kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=stfrench@microsoft.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.