diff options
| author | Henrique Carvalho <henrique.carvalho@suse.com> | 2026-01-19 19:42:12 +0300 |
|---|---|---|
| committer | Steve French <stfrench@microsoft.com> | 2026-02-09 02:07:43 +0300 |
| commit | 556bb341f9f2ead773f52ae466296ea0a9c927f8 (patch) | |
| tree | b19cfab592725f2c8a49ac111fb59272c0920e86 | |
| parent | c3c06e42e1527716c54f3ad2ced6a034b5f3a489 (diff) | |
| download | linux-556bb341f9f2ead773f52ae466296ea0a9c927f8.tar.xz | |
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 <sprasad@microsoft.com>
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
| -rw-r--r-- | fs/smb/client/cifsglob.h | 5 | ||||
| -rw-r--r-- | 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 103c92442c28..5e70487decfb 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 60c76375f0f5..d5661e9382d3 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; } |
