Commit 5433c629 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

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: default avatarPaulo Alcantara (Red Hat) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 4b1b4c8b
Loading
Loading
Loading
Loading
+26 −43
Original line number Diff line number Diff line
@@ -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 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
			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;
				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;
}

+30 −14
Original line number Diff line number Diff line
@@ -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,6 +26,7 @@ struct dfs_ref {
};

struct dfs_ref_walk {
	struct cifs_mount_ctx	*mnt_ctx;
	struct dfs_ref		*ref;
	struct dfs_ref		refs[MAX_NESTED_LINKS];
};
@@ -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);
}

/*