]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
smb: client: optimize referral walk on failed link targets
authorPaulo Alcantara <pc@manguebit.com>
Fri, 10 Jan 2025 18:58:08 +0000 (15:58 -0300)
committerSteve French <stfrench@microsoft.com>
Mon, 20 Jan 2025 01:34:00 +0000 (19:34 -0600)
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>
fs/smb/client/dfs.c
fs/smb/client/dfs.h

index b7de814e39bddeec354b33870a1ca22fa06adfc3..dad521336b5eebb3856dac1ab84244fc55bd4f2b 100644 (file)
@@ -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;
 }
 
index 1aa2bc65b3bc2c43f953d01f753ff26a42f0511d..ed4cd7cf1ec645a9fa8b6910d44ccfeb4ceaed05 100644 (file)
@@ -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);
 }
 
 /*