From: nspmangalore@gmail.com
To: linux-cifs@vger.kernel.org, smfrench@gmail.com, pc@manguebit.org,
bharathsm@microsoft.com, dhowells@redhat.com,
henrique.carvalho@suse.com, ematsumiya@suse.de
Cc: Shyam Prasad N <sprasad@microsoft.com>
Subject: [PATCH v4 15/19] cifs: allow dcache population to happen asynchronously
Date: Fri, 1 May 2026 16:50:18 +0530 [thread overview]
Message-ID: <20260501112023.338005-15-sprasad@microsoft.com> (raw)
In-Reply-To: <20260501112023.338005-1-sprasad@microsoft.com>
From: Shyam Prasad N <sprasad@microsoft.com>
Today cifs_readdir populates the dcache inline whenever it
emits dentries. This introduces a perf bottleneck in
readdir performance.
This change attempts to make dcache population asynchronous
and parallel by doing this inside workers. It introduces a new
flag that lets reval and lookup wait for dentry population instead.
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
fs/smb/client/cifs_fs_sb.h | 1 +
fs/smb/client/cifsfs.c | 22 +++++++++-
fs/smb/client/cifsglob.h | 2 +
fs/smb/client/readdir.c | 88 +++++++++++++++++++++++++++++++++++++-
4 files changed, 110 insertions(+), 3 deletions(-)
diff --git a/fs/smb/client/cifs_fs_sb.h b/fs/smb/client/cifs_fs_sb.h
index 84e7e366b0ff4..0efc1483f3ab4 100644
--- a/fs/smb/client/cifs_fs_sb.h
+++ b/fs/smb/client/cifs_fs_sb.h
@@ -71,5 +71,6 @@ struct cifs_sb_info {
* Available once the mount has completed.
*/
struct dentry *root;
+ bool sb_dying:1; /* superblock is being destroyed, skip dcache ops */
};
#endif /* _CIFS_FS_SB_H */
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 79a6a4c297ee3..f8f3b27ba60bc 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -69,6 +69,7 @@ bool disable_legacy_dialects; /* false by default */
bool enable_gcm_256 = true;
bool require_gcm_256; /* false by default */
bool enable_negotiate_signing; /* false by default */
+bool dcache_populate_async; /* false by default */
unsigned int global_secflags = CIFSSEC_DEF;
/* unsigned int ntlmv2_support = 0; */
@@ -241,6 +242,9 @@ MODULE_PARM_DESC(require_gcm_256, "Require strongest (256 bit) GCM encryption. D
module_param(enable_negotiate_signing, bool, 0644);
MODULE_PARM_DESC(enable_negotiate_signing, "Enable negotiating packet signing algorithm with server. Default: n/N/0");
+module_param(dcache_populate_async, bool, 0644);
+MODULE_PARM_DESC(dcache_populate_async, "Enable asynchronous dcache population during readdir to improve performance on large directories. Default: n/N/0");
+
module_param(disable_legacy_dialects, bool, 0644);
MODULE_PARM_DESC(disable_legacy_dialects, "To improve security it may be "
"helpful to restrict the ability to "
@@ -395,6 +399,11 @@ static void cifs_kill_sb(struct super_block *sb)
* and close all deferred file handles before we kill the sb.
*/
if (cifs_sb->root) {
+ /* Mark superblock as dying to skip expensive dcache ops in flight */
+ cifs_sb->sb_dying = true;
+ /* Wait for dcache work to complete and clean up */
+ flush_workqueue(cifs_dcache_wq);
+
close_all_cached_dirs(cifs_sb);
cifs_close_all_deferred_files_sb(cifs_sb);
@@ -2082,9 +2091,17 @@ init_cifs(void)
goto out_destroy_serverclose_wq;
}
+ /* WQ_UNBOUND allows dcache work to run on any CPU for parallelism */
+ cifs_dcache_wq = alloc_workqueue("cifs_dcache",
+ WQ_UNBOUND | WQ_FREEZABLE, 0);
+ if (!cifs_dcache_wq) {
+ rc = -ENOMEM;
+ goto out_destroy_cfid_put_wq;
+ }
+
rc = cifs_init_inodecache();
if (rc)
- goto out_destroy_cfid_put_wq;
+ goto out_destroy_dcache_wq;
rc = cifs_init_netfs();
if (rc)
@@ -2164,6 +2181,8 @@ init_cifs(void)
cifs_destroy_netfs();
out_destroy_inodecache:
cifs_destroy_inodecache();
+out_destroy_dcache_wq:
+ destroy_workqueue(cifs_dcache_wq);
out_destroy_cfid_put_wq:
destroy_workqueue(cfid_put_wq);
out_destroy_serverclose_wq:
@@ -2212,6 +2231,7 @@ exit_cifs(void)
destroy_workqueue(fileinfo_put_wq);
destroy_workqueue(serverclose_wq);
destroy_workqueue(cfid_put_wq);
+ destroy_workqueue(cifs_dcache_wq);
destroy_workqueue(cifsiod_wq);
cifs_proc_clean();
}
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 2a3fad071564a..278bf2bf11e97 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2144,6 +2144,7 @@ extern unsigned int sign_CIFS_PDUs; /* enable smb packet signing */
extern bool enable_gcm_256; /* allow optional negotiate of strongest signing (aes-gcm-256) */
extern bool require_gcm_256; /* require use of strongest signing (aes-gcm-256) */
extern bool enable_negotiate_signing; /* request use of faster (GMAC) signing if available */
+extern bool dcache_populate_async; /* enable async dcache population during readdir */
extern bool linuxExtEnabled;/*enable Linux/Unix CIFS extensions*/
extern unsigned int CIFSMaxBufSize; /* max size not including hdr */
extern unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */
@@ -2165,6 +2166,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_dcache_wq;
extern __u32 cifs_lock_secret;
extern mempool_t *cifs_sm_req_poolp;
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
index ef81fdb503c0a..a1202a82be4e8 100644
--- a/fs/smb/client/readdir.c
+++ b/fs/smb/client/readdir.c
@@ -13,6 +13,7 @@
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <linux/workqueue.h>
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_unicode.h"
@@ -24,6 +25,19 @@
#include "cached_dir.h"
#include "reparse.h"
+/* Workqueue for async dcache population */
+struct workqueue_struct *cifs_dcache_wq;
+
+/* Work item for async dcache population */
+struct cifs_dcache_work {
+ struct work_struct work;
+ struct dentry *parent; /* dget() reference to parent dir */
+ char *name; /* kstrdup() copy of filename */
+ unsigned int namelen;
+ struct cifs_fattr fattr; /* Copy of attributes */
+ struct cached_fid *cfid; /* ref-counted cfid for cifs_complete_pending_dcache */
+};
+
/*
* To be safe - for UCS to UTF-8 with strings loaded with the rare long
* characters alloc more to account for such multibyte target UTF-8
@@ -171,6 +185,65 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
dput(dentry);
}
+/*
+ * Async dcache population work handler.
+ * Delegates to cifs_prime_dcache then signals completion to unblock waiters.
+ */
+static void cifs_dcache_work_handler(struct work_struct *work)
+{
+ struct cifs_dcache_work *dcache_work =
+ container_of(work, struct cifs_dcache_work, work);
+ struct qstr name = QSTR_INIT(dcache_work->name, dcache_work->namelen);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(dcache_work->parent->d_sb);
+
+ cifs_dbg(FYI, "%s: async dcache for %s\n", __func__, name.name);
+
+ /* Skip expensive dcache operations if superblock is being torn down */
+ if (!cifs_sb->sb_dying) {
+ cifs_prime_dcache(dcache_work->parent, &name, &dcache_work->fattr);
+ cifs_complete_pending_dcache(dcache_work->cfid, dcache_work->name,
+ dcache_work->namelen);
+ }
+ close_cached_dir(dcache_work->cfid);
+ dput(dcache_work->parent);
+ kfree(dcache_work->name);
+ kfree(dcache_work);
+}
+
+/*
+ * Queue async dcache population work.
+ * Returns true if work was queued, false if sync fallback needed.
+ */
+static bool cifs_queue_dcache_work(struct dentry *parent, const char *name,
+ unsigned int namelen, struct cifs_fattr *fattr,
+ struct cached_fid *cfid)
+{
+ struct cifs_dcache_work *work;
+
+ if (!cfid)
+ return false;
+
+ work = kzalloc(sizeof(*work), GFP_KERNEL);
+ if (!work)
+ return false;
+
+ work->name = kstrndup(name, namelen, GFP_KERNEL);
+ if (!work->name) {
+ kfree(work);
+ return false;
+ }
+
+ work->parent = dget(parent);
+ work->namelen = namelen;
+ memcpy(&work->fattr, fattr, sizeof(work->fattr));
+ kref_get(&cfid->refcount);
+ work->cfid = cfid;
+
+ INIT_WORK(&work->work, cifs_dcache_work_handler);
+ queue_work(cifs_dcache_wq, &work->work);
+ return true;
+}
+
static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
{
@@ -923,8 +996,19 @@ static int cifs_filldir(char *find_entry, struct file *file,
*/
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
- add_to_cached_dir(cfid, ctx, name.name, name.len, &fattr, file);
- cifs_prime_dcache(file_dentry(file), &name, &fattr);
+ /* queue async dcache population if enabled; fallback to sync if disabled or queueing fails */
+ bool cached = add_to_cached_dir(cfid, ctx, name.name, name.len, &fattr, file);
+
+ if (dcache_populate_async && cached &&
+ cifs_queue_dcache_work(file_dentry(file), name.name, name.len,
+ &fattr, cfid)) {
+ /* Async: handler will call cifs_prime_dcache + cifs_complete_pending_dcache */
+ cifs_dbg(FYI, "Queued async dcache population for %.*s\n", name.len, name.name);
+ } else {
+ cifs_prime_dcache(file_dentry(file), &name, &fattr);
+ if (cached)
+ cifs_complete_pending_dcache(cfid, name.name, name.len);
+ }
return !cifs_dir_emit(ctx, name.name, name.len, &fattr);
}
--
2.43.0
next prev parent reply other threads:[~2026-05-01 11:20 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-01 11:20 [PATCH v4 01/19] cifs: change_conf needs to be called for session setup nspmangalore
2026-05-01 11:20 ` [PATCH v4 02/19] cifs: abort open_cached_dir if we don't request leases nspmangalore
2026-05-06 14:16 ` Bharath SM
2026-05-01 11:20 ` [PATCH v4 03/19] cifs: invalidate cfid on unlink/rename/rmdir nspmangalore
2026-05-01 11:20 ` [PATCH v4 04/19] cifs: define variable sized buffer for querydir responses nspmangalore
2026-05-01 11:20 ` [PATCH v4 05/19] cifs: optimize readdir for small directories nspmangalore
2026-05-01 11:20 ` [PATCH v4 06/19] cifs: optimize readdir for larger directories nspmangalore
2026-05-01 11:20 ` [PATCH v4 07/19] cifs: reorganize cached dir helpers nspmangalore
2026-05-01 11:20 ` [PATCH v4 08/19] cifs: make cfid locks more granular nspmangalore
2026-05-01 11:20 ` [PATCH v4 09/19] cifs: query dir should reuse cfid even if not fully cached nspmangalore
2026-05-01 11:20 ` [PATCH v4 10/19] cifs: back cached_dirents with page cache nspmangalore
2026-05-01 11:20 ` [PATCH v4 11/19] cifs: in place changes to cached_dirents when dir lease is held nspmangalore
2026-05-01 11:20 ` [PATCH v4 12/19] cifs: register a shrinker to manage cached_dirents nspmangalore
2026-05-01 11:20 ` [PATCH v4 13/19] cifs: option to disable time-based eviction of cache nspmangalore
2026-05-01 15:47 ` Steve French
2026-05-04 12:28 ` Shyam Prasad N
2026-05-01 11:20 ` [PATCH v4 14/19] cifs: option to set unlimited number of cached dirs nspmangalore
2026-05-01 11:20 ` nspmangalore [this message]
2026-05-01 11:20 ` [PATCH v4 16/19] cifs: trace points for cached_dir operations nspmangalore
2026-05-01 11:20 ` [PATCH v4 17/19] cifs: discard functions to ensure that mid callbacks get called nspmangalore
2026-05-01 11:20 ` [PATCH v4 18/19] cifs: keep cfids in rbtree for efficient lookups nspmangalore
2026-05-01 11:20 ` [PATCH v4 19/19] cifs: invalidate cached_dirents if population aborted nspmangalore
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260501112023.338005-15-sprasad@microsoft.com \
--to=nspmangalore@gmail.com \
--cc=bharathsm@microsoft.com \
--cc=dhowells@redhat.com \
--cc=ematsumiya@suse.de \
--cc=henrique.carvalho@suse.com \
--cc=linux-cifs@vger.kernel.org \
--cc=pc@manguebit.org \
--cc=smfrench@gmail.com \
--cc=sprasad@microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox