]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
smb: client: introduce multichannel async work during mount
authorHenrique Carvalho <henrique.carvalho@suse.com>
Mon, 19 Jan 2026 16:42:12 +0000 (13:42 -0300)
committerSteve French <stfrench@microsoft.com>
Sun, 8 Feb 2026 23:07:43 +0000 (17:07 -0600)
Mounts can experience large delays when servers advertise interfaces
that are unreachable from the client.

To fix this, decouple channel addition from the synchronous mount path
by introducing struct mchan_mount and running channel setup as
background work.

Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsglob.h
fs/smb/client/connect.c

index 103c92442c2824a65b4a8c6ff4de23e9b4fc7c42..5e70487decfbd9c812a6a009f1eb04dfcde41d5d 100644 (file)
@@ -1798,6 +1798,11 @@ struct cifs_mount_ctx {
        struct cifs_tcon *tcon;
 };
 
+struct mchan_mount {
+       struct work_struct work;
+       struct cifs_ses *ses;
+};
+
 static inline void __free_dfs_info_param(struct dfs_info3_param *param)
 {
        kfree(param->path_name);
index 60c76375f0f50eaea49788465d93f389e7acf4d7..d5661e9382d3119338900edc4c456bb9997d79f8 100644 (file)
@@ -64,6 +64,10 @@ static int generic_ip_connect(struct TCP_Server_Info *server);
 static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
 static void cifs_prune_tlinks(struct work_struct *work);
 
+static struct mchan_mount *mchan_mount_alloc(struct cifs_ses *ses);
+static void mchan_mount_free(struct mchan_mount *mchan_mount);
+static void mchan_mount_work_fn(struct work_struct *work);
+
 /*
  * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
  * get their ip addresses changed at some point.
@@ -3899,15 +3903,64 @@ out:
        return rc;
 }
 
+static struct mchan_mount *
+mchan_mount_alloc(struct cifs_ses *ses)
+{
+       struct mchan_mount *mchan_mount;
+
+       mchan_mount = kzalloc(sizeof(*mchan_mount), GFP_KERNEL);
+       if (!mchan_mount)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_WORK(&mchan_mount->work, mchan_mount_work_fn);
+
+       spin_lock(&cifs_tcp_ses_lock);
+       cifs_smb_ses_inc_refcount(ses);
+       spin_unlock(&cifs_tcp_ses_lock);
+       mchan_mount->ses = ses;
+
+       return mchan_mount;
+}
+
+static void
+mchan_mount_free(struct mchan_mount *mchan_mount)
+{
+       cifs_put_smb_ses(mchan_mount->ses);
+       kfree(mchan_mount);
+}
+
+static void
+mchan_mount_work_fn(struct work_struct *work)
+{
+       struct mchan_mount *mchan_mount = container_of(work, struct mchan_mount, work);
+
+       smb3_update_ses_channels(mchan_mount->ses,
+                                mchan_mount->ses->server,
+                                false /* from_reconnect */,
+                                false /* disable_mchan */);
+
+       mchan_mount_free(mchan_mount);
+}
+
 #ifdef CONFIG_CIFS_DFS_UPCALL
 int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
 {
        struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
+       struct mchan_mount *mchan_mount = NULL;
        int rc;
 
        rc = dfs_mount_share(&mnt_ctx);
        if (rc)
                goto error;
+
+       if (ctx->multichannel) {
+               mchan_mount = mchan_mount_alloc(mnt_ctx.ses);
+               if (IS_ERR(mchan_mount)) {
+                       rc = PTR_ERR(mchan_mount);
+                       goto error;
+               }
+       }
+
        if (!ctx->dfs_conn)
                goto out;
 
@@ -3926,17 +3979,19 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
        ctx->prepath = NULL;
 
 out:
-       smb3_update_ses_channels(mnt_ctx.ses, mnt_ctx.server,
-                                 false /* from_reconnect */,
-                                 false /* disable_mchan */);
        rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
        if (rc)
                goto error;
 
+       if (ctx->multichannel)
+               queue_work(cifsiod_wq, &mchan_mount->work);
+
        free_xid(mnt_ctx.xid);
        return rc;
 
 error:
+       if (ctx->multichannel && !IS_ERR_OR_NULL(mchan_mount))
+               mchan_mount_free(mchan_mount);
        cifs_mount_put_conns(&mnt_ctx);
        return rc;
 }