diff options
| author | Paulo Alcantara <pc@manguebit.com> | 2025-01-10 15:58:08 -0300 | 
|---|---|---|
| committer | Steve French <stfrench@microsoft.com> | 2025-01-19 19:34:00 -0600 | 
| commit | 5433c629e8d4eee77233a2bc88075886dc6e37ef (patch) | |
| tree | 77903039a72dbaa46def60bb87923bb29ab0b507 | |
| parent | 4b1b4c8be9dee9aa1a751cfa3954b2fcfdfe9c3d (diff) | |
smb: client: optimize referral walk on failed link targets
If a link referral request sent to root server was successful but
client failed to connect to all link targets, there is no need to
retry same link referral on a different root server.  Set an end
marker for the DFS root referral so the client will not attempt to
re-send link referrals to different root servers on failures.
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
| -rw-r--r-- | fs/smb/client/dfs.c | 69 | ||||
| -rw-r--r-- | fs/smb/client/dfs.h | 44 | 
2 files changed, 56 insertions, 57 deletions
| diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c index b7de814e39bd..dad521336b5e 100644 --- a/fs/smb/client/dfs.c +++ b/fs/smb/client/dfs.c @@ -98,15 +98,16 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx,  	return rc;  } -static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx, -			 struct dfs_info3_param *tgt, -			 struct dfs_ref_walk *rw) +static int setup_dfs_ref(struct dfs_info3_param *tgt, struct dfs_ref_walk *rw)  { -	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; -	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; +	struct cifs_sb_info *cifs_sb = rw->mnt_ctx->cifs_sb; +	struct smb3_fs_context *ctx = rw->mnt_ctx->fs_ctx;  	char *ref_path, *full_path;  	int rc; +	set_root_smb_session(rw->mnt_ctx); +	ref_walk_ses(rw) = ctx->dfs_root_ses; +  	full_path = smb3_fs_context_fullpath(ctx, CIFS_DIR_SEP(cifs_sb));  	if (IS_ERR(full_path))  		return PTR_ERR(full_path); @@ -123,35 +124,22 @@ static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,  	}  	ref_walk_path(rw) = ref_path;  	ref_walk_fpath(rw) = full_path; -	ref_walk_ses(rw) = ctx->dfs_root_ses; -	return 0; + +	return dfs_get_referral(rw->mnt_ctx, +				ref_walk_path(rw) + 1, +				ref_walk_tl(rw));  } -static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, -			       struct dfs_ref_walk *rw) +static int __dfs_referral_walk(struct dfs_ref_walk *rw)  { -	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; +	struct smb3_fs_context *ctx = rw->mnt_ctx->fs_ctx; +	struct cifs_mount_ctx *mnt_ctx = rw->mnt_ctx;  	struct dfs_info3_param tgt = {};  	int rc = -ENOENT;  again:  	do {  		ctx->dfs_root_ses = ref_walk_ses(rw); -		if (ref_walk_empty(rw)) { -			rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1, -					      NULL, ref_walk_tl(rw)); -			if (rc) { -				rc = cifs_mount_get_tcon(mnt_ctx); -				if (!rc) -					rc = cifs_is_path_remote(mnt_ctx); -				continue; -			} -			if (!ref_walk_num_tgts(rw)) { -				rc = -ENOENT; -				continue; -			} -		} -  		while (ref_walk_next_tgt(rw)) {  			rc = parse_dfs_target(ctx, rw, &tgt);  			if (rc) @@ -162,32 +150,29 @@ again:  			if (rc)  				continue; -			ref_walk_set_tgt_hint(rw);  			if (tgt.flags & DFSREF_STORAGE_SERVER) {  				rc = cifs_mount_get_tcon(mnt_ctx);  				if (!rc)  					rc = cifs_is_path_remote(mnt_ctx); -				if (!rc) +				if (!rc) { +					ref_walk_set_tgt_hint(rw);  					break; +				}  				if (rc != -EREMOTE)  					continue;  			} -			set_root_smb_session(mnt_ctx);  			rc = ref_walk_advance(rw);  			if (!rc) { -				rc = setup_dfs_ref(mnt_ctx, &tgt, rw); -				if (!rc) { -					rc = -EREMOTE; -					goto again; -				} +				rc = setup_dfs_ref(&tgt, rw); +				if (rc) +					break; +				ref_walk_mark_end(rw); +				goto again;  			} -			if (rc != -ELOOP) -				goto out;  		}  	} while (rc && ref_walk_descend(rw)); -out:  	free_dfs_info_param(&tgt);  	return rc;  } @@ -204,10 +189,10 @@ static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,  		return rc;  	} -	ref_walk_init(*rw); -	rc = setup_dfs_ref(mnt_ctx, NULL, *rw); +	ref_walk_init(*rw, mnt_ctx); +	rc = setup_dfs_ref(NULL, *rw);  	if (!rc) -		rc = __dfs_referral_walk(mnt_ctx, *rw); +		rc = __dfs_referral_walk(*rw);  	return rc;  } @@ -297,7 +282,7 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)  	 * to respond with PATH_NOT_COVERED to requests that include the prefix.  	 */  	if (!nodfs) { -		rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL); +		rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL);  		if (rc) {  			cifs_dbg(FYI, "%s: no dfs referral for %s: %d\n",  				 __func__, ctx->UNC + 1, rc); @@ -317,10 +302,8 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)  		cifs_mount_put_conns(mnt_ctx);  		rc = get_session(mnt_ctx, NULL);  	} -	if (!rc) { -		set_root_smb_session(mnt_ctx); +	if (!rc)  		rc = __dfs_mount_share(mnt_ctx); -	}  	return rc;  } diff --git a/fs/smb/client/dfs.h b/fs/smb/client/dfs.h index 1aa2bc65b3bc..ed4cd7cf1ec6 100644 --- a/fs/smb/client/dfs.h +++ b/fs/smb/client/dfs.h @@ -12,6 +12,7 @@  #include "dfs_cache.h"  #include "cifs_unicode.h"  #include <linux/namei.h> +#include <linux/errno.h>  #define DFS_INTERLINK(v) \  	(((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER)) @@ -25,8 +26,9 @@ struct dfs_ref {  };  struct dfs_ref_walk { -	struct dfs_ref *ref; -	struct dfs_ref refs[MAX_NESTED_LINKS]; +	struct cifs_mount_ctx	*mnt_ctx; +	struct dfs_ref		*ref; +	struct dfs_ref		refs[MAX_NESTED_LINKS];  };  #define ref_walk_start(w)	((w)->refs) @@ -35,7 +37,6 @@ struct dfs_ref_walk {  #define ref_walk_descend(w)	(--ref_walk_cur(w) >= ref_walk_start(w))  #define ref_walk_tit(w)	(ref_walk_cur(w)->tit) -#define ref_walk_empty(w)	(!ref_walk_tit(w))  #define ref_walk_path(w)	(ref_walk_cur(w)->path)  #define ref_walk_fpath(w)	(ref_walk_cur(w)->full_path)  #define ref_walk_tl(w)		(&ref_walk_cur(w)->tl) @@ -51,9 +52,11 @@ static inline struct dfs_ref_walk *ref_walk_alloc(void)  	return rw;  } -static inline void ref_walk_init(struct dfs_ref_walk *rw) +static inline void ref_walk_init(struct dfs_ref_walk *rw, +				 struct cifs_mount_ctx *mnt_ctx)  {  	memset(rw, 0, sizeof(*rw)); +	rw->mnt_ctx = mnt_ctx;  	ref_walk_cur(rw) = ref_walk_start(rw);  } @@ -93,15 +96,23 @@ static inline int ref_walk_advance(struct dfs_ref_walk *rw)  static inline struct dfs_cache_tgt_iterator *  ref_walk_next_tgt(struct dfs_ref_walk *rw)  { -	struct dfs_cache_tgt_iterator *tit;  	struct dfs_ref *ref = ref_walk_cur(rw); +	struct dfs_cache_tgt_iterator *tit; + +	if (IS_ERR(ref->tit)) +		return NULL;  	if (!ref->tit)  		tit = dfs_cache_get_tgt_iterator(&ref->tl);  	else  		tit = dfs_cache_get_next_tgt(&ref->tl, ref->tit); + +	if (!tit) { +		ref->tit = ERR_PTR(-ENOENT); +		return NULL; +	}  	ref->tit = tit; -	return tit; +	return ref->tit;  }  static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw, @@ -112,11 +123,6 @@ static inline int ref_walk_get_tgt(struct dfs_ref_walk *rw,  					  ref_walk_tit(rw), tgt);  } -static inline int ref_walk_num_tgts(struct dfs_ref_walk *rw) -{ -	return dfs_cache_get_nr_tgts(ref_walk_tl(rw)); -} -  static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw)  {  	dfs_cache_noreq_update_tgthint(ref_walk_path(rw) + 1, @@ -136,6 +142,15 @@ static inline void ref_walk_set_tcon(struct dfs_ref_walk *rw,  	}  } +static inline void ref_walk_mark_end(struct dfs_ref_walk *rw) +{ +	struct dfs_ref *ref = ref_walk_cur(rw) - 1; + +	WARN_ON_ONCE(ref < ref_walk_start(rw)); +	dfs_cache_noreq_update_tgthint(ref->path + 1, ref->tit); +	ref->tit = ERR_PTR(-ENOENT); /* end marker */ +} +  int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,  			      struct smb3_fs_context *ctx);  int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx); @@ -145,15 +160,16 @@ static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path)  	return dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb));  } -static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *path, -				   struct dfs_info3_param *ref, struct dfs_cache_tgt_list *tl) +static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, +				   const char *path, +				   struct dfs_cache_tgt_list *tl)  {  	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;  	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;  	struct cifs_ses *rses = ctx->dfs_root_ses ?: mnt_ctx->ses;  	return dfs_cache_find(mnt_ctx->xid, rses, cifs_sb->local_nls, -			      cifs_remap(cifs_sb), path, ref, tl); +			      cifs_remap(cifs_sb), path, NULL, tl);  }  /* | 
