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 v5 10/17] cifs: register a shrinker to manage cached_dirents
Date: Thu, 14 May 2026 23:38:16 +0530 [thread overview]
Message-ID: <20260514180823.497293-10-sprasad@microsoft.com> (raw)
In-Reply-To: <20260514180823.497293-1-sprasad@microsoft.com>
From: Shyam Prasad N <sprasad@microsoft.com>
Since the cached_dirents are now backed by folioq, we do not need
a timed cleanup of these cfids anymore. This change registers a
shrinker with the mm layer for the dircache. If mm needs to free up
memory or flush the cache, it can inform cifs about this using the
shrinker interface.
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
fs/smb/client/cifs_debug.c | 18 +------
fs/smb/client/cifsfs.c | 104 ++++++++++++++++++++++++++++++++++++-
fs/smb/client/cifsfs.h | 4 ++
3 files changed, 108 insertions(+), 18 deletions(-)
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index b35863eb2a660..131af7333fc58 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -375,22 +375,8 @@ static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *
if (rc)
return rc;
- if (v == 0) {
- struct TCP_Server_Info *server;
- struct cifs_ses *ses;
- struct cifs_tcon *tcon;
-
- spin_lock(&cifs_tcp_ses_lock);
- list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
- list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
- if (cifs_ses_exiting(ses))
- continue;
- list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
- invalidate_all_cached_dirs(tcon, false);
- }
- }
- spin_unlock(&cifs_tcp_ses_lock);
- }
+ if (v == 0)
+ cifs_shrink_dir_caches(false, ULONG_MAX);
return count;
}
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 9f76b0347fa9d..14cb3d37a8607 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -29,6 +29,7 @@
#include <linux/uuid.h>
#include <linux/xattr.h>
#include <linux/mm.h>
+#include <linux/shrinker.h>
#include <linux/key-type.h>
#include <uapi/linux/magic.h>
#include <net/ipv6.h>
@@ -123,10 +124,95 @@ MODULE_PARM_DESC(dir_cache_timeout, "Number of seconds to cache directory conten
"Range: 1 to 65000 seconds, 0 to disable caching dir contents");
/* Module-wide total cached dirents (in bytes) across all tcons */
atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0);
+static struct shrinker *cifs_dircache_shrinker;
atomic_t cifs_sillycounter;
atomic_t cifs_tmpcounter;
+/**
+ * cifs_shrink_dir_caches - shrink dir caches by a specific size
+ * @wait: wait for server responses to close
+ * @nr_to_free: number of pages to shrink the dir cache by (pass ULONG_MAX to
+ * free everything)
+ *
+ * Shrinks dir caches by a specific target size.
+ */
+unsigned long cifs_shrink_dir_caches(bool wait, unsigned long nr_to_free)
+{
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ u64 before, after, freed_bytes = 0;
+ u64 target_bytes;
+
+ before = atomic64_read(&cifs_dircache_bytes_used);
+ if (nr_to_free == ULONG_MAX)
+ target_bytes = U64_MAX;
+ else
+ target_bytes = (u64)nr_to_free * PAGE_SIZE;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ if (cifs_ses_exiting(ses))
+ continue;
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ invalidate_all_cached_dirs(tcon, false);
+ after = atomic64_read(&cifs_dircache_bytes_used);
+ if (after < before)
+ freed_bytes = before - after;
+ if (freed_bytes >= target_bytes)
+ goto out_unlock;
+ }
+ }
+ }
+out_unlock:
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ if (wait)
+ flush_workqueue(cfid_put_wq);
+
+ after = atomic64_read(&cifs_dircache_bytes_used);
+ if (after >= before)
+ return 0;
+ return (unsigned long)(before - after);
+}
+
+static unsigned long cifs_dircache_shrinker_count(struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ u64 bytes = atomic64_read(&cifs_dircache_bytes_used);
+
+ (void)shrink;
+ (void)sc;
+
+ return DIV_ROUND_UP_ULL(bytes, PAGE_SIZE);
+}
+
+static unsigned long cifs_dircache_shrinker_scan(struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ unsigned long freed_bytes;
+
+ (void)shrink;
+
+ if (!sc->nr_to_scan)
+ return 0;
+
+ if (!atomic64_read(&cifs_dircache_bytes_used))
+ return SHRINK_STOP;
+
+ /*
+ * Shrinker scan can run from reclaim context, so avoid synchronously
+ * flushing worker queues here to prevent long stalls/deadlocks.
+ */
+ freed_bytes = cifs_shrink_dir_caches(false, max_t(unsigned long, 1, sc->nr_to_scan));
+ if (!freed_bytes)
+ return SHRINK_STOP;
+
+ return DIV_ROUND_UP_ULL(freed_bytes, PAGE_SIZE);
+}
+
#ifdef CONFIG_CIFS_STATS2
unsigned int slow_rsp_threshold = 1;
module_param(slow_rsp_threshold, uint, 0644);
@@ -2011,10 +2097,19 @@ init_cifs(void)
if (rc)
goto out_destroy_mids;
+ cifs_dircache_shrinker = shrinker_alloc(0, "cifs-dircache");
+ if (!cifs_dircache_shrinker) {
+ rc = -ENOMEM;
+ goto out_destroy_request_bufs;
+ }
+ cifs_dircache_shrinker->count_objects = cifs_dircache_shrinker_count;
+ cifs_dircache_shrinker->scan_objects = cifs_dircache_shrinker_scan;
+ shrinker_register(cifs_dircache_shrinker);
+
#ifdef CONFIG_CIFS_DFS_UPCALL
rc = dfs_cache_init();
if (rc)
- goto out_destroy_request_bufs;
+ goto out_free_dircache_shrinker;
#endif /* CONFIG_CIFS_DFS_UPCALL */
#ifdef CONFIG_CIFS_UPCALL
rc = init_cifs_spnego();
@@ -2056,8 +2151,11 @@ init_cifs(void)
#endif
#ifdef CONFIG_CIFS_DFS_UPCALL
dfs_cache_destroy();
-out_destroy_request_bufs:
+out_free_dircache_shrinker:
#endif
+ shrinker_free(cifs_dircache_shrinker);
+ cifs_dircache_shrinker = NULL;
+out_destroy_request_bufs:
cifs_destroy_request_bufs();
out_destroy_mids:
destroy_mids();
@@ -2090,6 +2188,8 @@ exit_cifs(void)
cifs_dbg(NOISY, "exit_smb3\n");
unregister_filesystem(&cifs_fs_type);
unregister_filesystem(&smb3_fs_type);
+ shrinker_free(cifs_dircache_shrinker);
+ cifs_dircache_shrinker = NULL;
cifs_release_automount_timer();
exit_cifs_idmap();
#ifdef CONFIG_CIFS_SWN_UPCALL
diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
index c455b15f27782..d08394a3c5ce5 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -122,6 +122,10 @@ extern const struct dentry_operations cifs_ci_dentry_ops;
struct vfsmount *cifs_d_automount(struct path *path);
+/* Functions related to dir cache */
+unsigned long cifs_shrink_dir_caches(bool wait,
+ unsigned long nr_to_free);
+
/* Functions related to symlinks */
const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
struct delayed_call *done);
--
2.43.0
next prev parent reply other threads:[~2026-05-14 18:08 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-14 18:08 [PATCH v5 01/17] cifs: invalidate cfid on unlink/rename/rmdir nspmangalore
2026-05-14 18:08 ` [PATCH v5 02/17] cifs: define variable sized buffer for querydir responses nspmangalore
2026-05-14 18:08 ` [PATCH v5 03/17] cifs: optimize readdir for small directories nspmangalore
2026-05-14 18:39 ` Steve French
2026-05-14 18:08 ` [PATCH v5 04/17] cifs: optimize readdir for larger directories nspmangalore
2026-05-14 22:39 ` Steve French
2026-05-14 18:08 ` [PATCH v5 05/17] cifs: reorganize cached dir helpers nspmangalore
2026-05-14 18:08 ` [PATCH v5 06/17] cifs: make cfid locks more granular nspmangalore
2026-05-14 18:08 ` [PATCH v5 07/17] cifs: query dir should reuse cfid even if not fully cached nspmangalore
2026-05-14 18:08 ` [PATCH v5 08/17] cifs: back cached_dirents with page cache nspmangalore
2026-05-14 18:08 ` [PATCH v5 09/17] cifs: in place changes to cached_dirents when dir lease is held nspmangalore
2026-05-14 18:08 ` nspmangalore [this message]
2026-05-14 18:08 ` [PATCH v5 11/17] cifs: option to disable time-based eviction of cache nspmangalore
2026-05-14 18:08 ` [PATCH v5 12/17] cifs: option to set unlimited number of cached dirs nspmangalore
2026-05-14 18:08 ` [PATCH v5 13/17] cifs: allow dcache population to happen asynchronously nspmangalore
2026-05-14 18:08 ` [PATCH v5 14/17] cifs: trace points for cached_dir operations nspmangalore
2026-05-14 18:08 ` [PATCH v5 15/17] cifs: discard functions to ensure that mid callbacks get called nspmangalore
2026-05-14 18:08 ` [PATCH v5 16/17] cifs: keep cfids in rbtree for efficient lookups nspmangalore
2026-05-14 20:43 ` Enzo Matsumiya
2026-05-15 10:49 ` Shyam Prasad N
2026-05-14 18:08 ` [PATCH v5 17/17] 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=20260514180823.497293-10-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