Linux CIFS filesystem development
 help / color / mirror / Atom feed
* [RFC PATCH for-next] smb: client: parallelize multichannel write issue
@ 2026-06-26 16:19 Henrique Carvalho
  2026-06-26 19:54 ` Enzo Matsumiya
  0 siblings, 1 reply; 2+ messages in thread
From: Henrique Carvalho @ 2026-06-26 16:19 UTC (permalink / raw)
  To: sfrench; +Cc: pc, ronniesahlberg, sprasad, tom, bharathsm, ematsumiya,
	linux-cifs

I'm sending this as an RFC PATCH first so the approach and results can
be sanity checked more broadly.

The netfs writeback path issues write subrequests through the filesystem
issue_write() callback. For multichannel, those subrequests may target
different channels, but the issue callback is still entered serially by
the netfs issuing context.

As a result, while one channel is running the write issue path, write
subrequests for other channels may be left waiting to be issued. This
can limit multichannel writeback throughput because the channels are not
kept busy independently.

For multichannel sessions, queue the existing write issue path to a
workqueue. This lets the netfs issuing context return quickly and
continue issuing subsequent write subrequests for other channels.
Single-channel sessions keep the existing synchronous issue path.

Preliminary fio testing showed improvments in throughput by up to 2.5x
in 4MiB writes with larger dirty limits (1g/256m), 1.4x improvement for
1GiB writes with larger dirty limits, and is neutral when dirty limits
keep the pipeline shallow (4m/1m).

Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
 fs/smb/client/cifsfs.c    | 14 +++++++++-
 fs/smb/client/cifsglob.h  |  1 +
 fs/smb/client/cifsproto.h |  1 +
 fs/smb/client/file.c      | 57 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index ce23924f01b3..2ade5b47ef12 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -163,6 +163,7 @@ struct workqueue_struct	*cifsoplockd_wq;
 struct workqueue_struct	*deferredclose_wq;
 struct workqueue_struct	*serverclose_wq;
 struct workqueue_struct	*cfid_put_wq;
+struct workqueue_struct	*cifs_write_issue_wq;
 __u32 cifs_lock_secret;
 
 /*
@@ -2009,9 +2010,17 @@ init_cifs(void)
 		goto out_destroy_serverclose_wq;
 	}
 
+	cifs_write_issue_wq = alloc_workqueue("cifs_write_issue",
+					      WQ_UNBOUND | WQ_MEM_RECLAIM,
+					      0);
+	if (!cifs_write_issue_wq) {
+		rc = -ENOMEM;
+		goto out_destroy_cfid_put_wq;
+	}
+
 	rc = cifs_init_inodecache();
 	if (rc)
-		goto out_destroy_cfid_put_wq;
+		goto out_destroy_write_issue_wq;
 
 	rc = cifs_init_netfs();
 	if (rc)
@@ -2079,6 +2088,8 @@ init_cifs(void)
 	cifs_destroy_netfs();
 out_destroy_inodecache:
 	cifs_destroy_inodecache();
+out_destroy_write_issue_wq:
+	destroy_workqueue(cifs_write_issue_wq);
 out_destroy_cfid_put_wq:
 	destroy_workqueue(cfid_put_wq);
 out_destroy_serverclose_wq:
@@ -2119,6 +2130,7 @@ exit_cifs(void)
 	destroy_mids();
 	cifs_destroy_netfs();
 	cifs_destroy_inodecache();
+	destroy_workqueue(cifs_write_issue_wq);
 	destroy_workqueue(deferredclose_wq);
 	destroy_workqueue(cifsoplockd_wq);
 	destroy_workqueue(decrypt_wq);
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 82e0adc1dabd..591353f261a8 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2137,6 +2137,7 @@ extern struct workqueue_struct *cifsoplockd_wq;
 extern struct workqueue_struct *deferredclose_wq;
 extern struct workqueue_struct *serverclose_wq;
 extern struct workqueue_struct *cfid_put_wq;
+extern struct workqueue_struct *cifs_write_issue_wq;
 extern __u32 cifs_lock_secret;
 
 extern mempool_t *cifs_sm_req_poolp;
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 79d891f7df1a..113ffeec0e2f 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -59,6 +59,7 @@ int init_cifs_idmap(void);
 void exit_cifs_idmap(void);
 int init_cifs_spnego(void);
 void exit_cifs_spnego(void);
+int cifs_init_write_issue_wq(void);
 const char *build_path_from_dentry(struct dentry *direntry, void *page);
 char *__build_path_from_dentry_optional_prefix(struct dentry *direntry,
 					       void *page, const char *tree,
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index b60344125f27..3a070962a1f5 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -38,6 +38,46 @@
 #include <trace/events/netfs.h>
 
 static int cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush);
+static void __cifs_issue_write(struct netfs_io_subrequest *subreq);
+
+struct cifs_issue_write_work {
+	struct work_struct work;
+	struct netfs_io_subrequest *subreq;
+};
+
+static bool cifs_write_is_mchan(struct cifs_ses *ses)
+{
+	bool is_mchan;
+
+	spin_lock(&ses->chan_lock);
+	is_mchan = ses->chan_count > 1;
+	spin_unlock(&ses->chan_lock);
+
+	return is_mchan;
+}
+
+static void cifs_issue_write_work_fn(struct work_struct *work)
+{
+	struct cifs_issue_write_work *w = container_of(work, struct cifs_issue_write_work, work);
+
+	__cifs_issue_write(w->subreq);
+	kfree(w);
+}
+
+static int cifs_issue_parallel_write(struct cifs_ses *ses,
+				      struct netfs_io_subrequest *subreq)
+{
+	struct cifs_issue_write_work *w = kmalloc_obj(*w, GFP_NOFS);
+
+	if (!w)
+		return -ENOMEM;
+
+	w->subreq = subreq;
+	INIT_WORK(&w->work, cifs_issue_write_work_fn);
+	queue_work(cifs_write_issue_wq, &w->work);
+
+	return 0;
+}
 
 /*
  * Prepare a subrequest to upload to the server.  We need to allocate credits
@@ -108,7 +148,7 @@ static void cifs_prepare_write(struct netfs_io_subrequest *subreq)
 /*
  * Issue a subrequest to upload to the server.
  */
-static void cifs_issue_write(struct netfs_io_subrequest *subreq)
+static void __cifs_issue_write(struct netfs_io_subrequest *subreq)
 {
 	struct cifs_io_subrequest *wdata =
 		container_of(subreq, struct cifs_io_subrequest, subreq);
@@ -142,6 +182,21 @@ static void cifs_issue_write(struct netfs_io_subrequest *subreq)
 	goto out;
 }
 
+static void cifs_issue_write(struct netfs_io_subrequest *subreq)
+{
+	struct cifs_io_subrequest *wdata = container_of(subreq, struct cifs_io_subrequest, subreq);
+	struct cifs_ses *ses = tlink_tcon(wdata->req->cfile->tlink)->ses;
+
+	if (cifs_write_is_mchan(ses)) {
+		int err = cifs_issue_parallel_write(ses, subreq);
+
+		if (!err)
+			return;
+	}
+
+	__cifs_issue_write(subreq);
+}
+
 static void cifs_netfs_invalidate_cache(struct netfs_io_request *wreq)
 {
 	cifs_invalidate_cache(wreq->inode, 0);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-06-26 19:54 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26 16:19 [RFC PATCH for-next] smb: client: parallelize multichannel write issue Henrique Carvalho
2026-06-26 19:54 ` Enzo Matsumiya

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox