From 556bb341f9f2ead773f52ae466296ea0a9c927f8 Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Mon, 19 Jan 2026 13:42:12 -0300 Subject: [PATCH] smb: client: introduce multichannel async work during mount 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 Signed-off-by: Henrique Carvalho Signed-off-by: Steve French --- fs/smb/client/cifsglob.h | 5 ++++ fs/smb/client/connect.c | 61 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 103c92442c282..5e70487decfbd 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -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); diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 60c76375f0f50..d5661e9382d31 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -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; } -- 2.47.3