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 19/19] cifs: invalidate cached_dirents if population aborted
Date: Fri, 1 May 2026 16:50:22 +0530 [thread overview]
Message-ID: <20260501112023.338005-19-sprasad@microsoft.com> (raw)
In-Reply-To: <20260501112023.338005-1-sprasad@microsoft.com>
From: Shyam Prasad N <sprasad@microsoft.com>
In order to make sure that parallel readdirs do not populate
the cfid->cached_dirents, only the first readdir is given
"ownership" of populating cached_dirents. However, if the
next readdir on the same FD never arrives, we will always
miss the dirent cache.
This change introduces a 10-second timeout which will be used
by laundromat thread to check if the cached_dirents can be
invalidated. Ten seconds is a long enough interval between
successive readdir calls.
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
fs/smb/client/cached_dir.c | 65 ++++++++++++++++++++++++++++++++++----
fs/smb/client/cached_dir.h | 1 +
2 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 14c87ac1a4ad4..b626045745ca2 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -23,12 +23,18 @@ static void cfids_laundromat_worker(struct work_struct *work);
#define CACHED_DIRENT_HASH_BITS 7
#define CACHED_DIR_DENTRY_HT_BITS 8
+#define CACHED_DIR_POPULATE_TIMEOUT 10
struct cached_dir_dentry {
struct list_head entry;
struct dentry *dentry;
};
+struct cached_dir_invalidate_entry {
+ struct list_head entry;
+ struct cached_fid *cfid;
+};
+
/* Generic helpers */
bool cached_dir_is_valid(struct cached_fid *cfid)
{
@@ -496,6 +502,7 @@ static void fail_cached_dir_locked(struct cached_dirents *cde)
* can claim this slot and repopulate the cache.
*/
cde->file = NULL;
+ cde->last_populate_time = 0;
}
/* insert cached_dirent into lookup hashtable */
@@ -799,6 +806,7 @@ bool emit_cached_dir_if_valid(struct cached_fid *cfid,
cfid->dirents.file = file;
cfid->dirents.dir_inode = file_inode(file);
cfid->dirents.pos = 2;
+ cfid->dirents.last_populate_time = jiffies;
cached_dir_reset_insert_cursor_locked(&cfid->dirents);
/*
* A previous population attempt may have failed and left
@@ -851,6 +859,28 @@ static void finished_cached_dirents_count(struct cached_dirents *cde,
cached_mapping->folio_is_eof = 1;
cde->is_valid = 1;
+ cde->last_populate_time = 0;
+}
+
+static void maybe_invalidate_stale_cached_dirents(struct cached_fid *cfid)
+{
+ struct cached_dirents *cde = &cfid->dirents;
+
+ mutex_lock(&cde->de_mutex);
+ if (cde->last_populate_time && !cde->is_valid && !cde->is_failed &&
+ cde->file &&
+ time_after(jiffies,
+ cde->last_populate_time + HZ * CACHED_DIR_POPULATE_TIMEOUT))
+ fail_cached_dir_locked(cde);
+ mutex_unlock(&cde->de_mutex);
+}
+
+static unsigned long cached_dir_laundromat_interval_seconds(void)
+{
+ if (!dir_cache_timeout)
+ return CACHED_DIR_POPULATE_TIMEOUT;
+
+ return min_t(unsigned int, dir_cache_timeout, CACHED_DIR_POPULATE_TIMEOUT);
}
/* update the cached_dirent for a given name in list */
@@ -992,6 +1022,8 @@ bool add_to_cached_dir(struct cached_fid *cfid,
old_bytes = cfid->dirents.bytes_used;
added = add_cached_dirent(&cfid->dirents, ctx, name, namelen,
fattr, file);
+ if (added)
+ cfid->dirents.last_populate_time = jiffies;
new_entries = cfid->dirents.entries_count;
new_bytes = cfid->dirents.bytes_used;
mutex_unlock(&cfid->dirents.de_mutex);
@@ -2111,7 +2143,9 @@ static void cfids_laundromat_worker(struct work_struct *work)
{
struct cached_fids *cfids;
struct cached_fid *cfid, *q;
+ struct cached_dir_invalidate_entry *inv, *inv_q;
LIST_HEAD(entry);
+ LIST_HEAD(invalidate_list);
cfids = container_of(work, struct cached_fids, laundromat_work.work);
@@ -2121,6 +2155,9 @@ static void cfids_laundromat_worker(struct work_struct *work)
for (struct rb_node *rb_node = rb_first(&cfids->entries), *next_node;
rb_node; rb_node = next_node) {
+ struct cached_dir_invalidate_entry *inv_ent;
+ unsigned long last_populate_time;
+
next_node = rb_next(rb_node);
cfid = rb_entry(rb_node, struct cached_fid, node);
spin_lock(&cfid->cfid_lock);
@@ -2144,11 +2181,29 @@ static void cfids_laundromat_worker(struct work_struct *work)
kref_get(&cfid->refcount);
}
} else {
+ last_populate_time = READ_ONCE(cfid->dirents.last_populate_time);
+ if (last_populate_time &&
+ time_after(jiffies,
+ last_populate_time + HZ * CACHED_DIR_POPULATE_TIMEOUT)) {
+ inv_ent = kmalloc_obj(*inv_ent, GFP_ATOMIC);
+ if (inv_ent) {
+ kref_get(&cfid->refcount);
+ inv_ent->cfid = cfid;
+ list_add_tail(&inv_ent->entry, &invalidate_list);
+ }
+ }
spin_unlock(&cfid->cfid_lock);
}
}
spin_unlock(&cfids->cfid_list_lock);
+ list_for_each_entry_safe(inv, inv_q, &invalidate_list, entry) {
+ list_del(&inv->entry);
+ maybe_invalidate_stale_cached_dirents(inv->cfid);
+ close_cached_dir(inv->cfid);
+ kfree(inv);
+ }
+
list_for_each_entry_safe(cfid, q, &entry, dying_entry) {
list_del(&cfid->dying_entry);
@@ -2169,9 +2224,8 @@ static void cfids_laundromat_worker(struct work_struct *work)
*/
close_cached_dir(cfid);
}
- if (dir_cache_timeout)
- queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
- dir_cache_timeout * HZ);
+ queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
+ cached_dir_laundromat_interval_seconds() * HZ);
}
struct cached_fids *init_cached_dirs(void)
@@ -2192,9 +2246,8 @@ struct cached_fids *init_cached_dirs(void)
INIT_LIST_HEAD(&cfids->dying);
INIT_DELAYED_WORK(&cfids->laundromat_work, cfids_laundromat_worker);
- if (dir_cache_timeout)
- queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
- dir_cache_timeout * HZ);
+ queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
+ cached_dir_laundromat_interval_seconds() * HZ);
atomic_long_set(&cfids->total_dirents_entries, 0);
atomic64_set(&cfids->total_dirents_bytes, 0);
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index 58dde9452ec9b..eca0a0ca3674c 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -120,6 +120,7 @@ struct cached_dirents {
struct inode *dir_inode;
struct mutex de_mutex;
loff_t pos; /* Expected ctx->pos */
+ unsigned long last_populate_time; /* jiffies of last successful populate progress */
struct folio_queue *folioq;
struct list_head entry_list;
unsigned int entry_list_count;
--
2.43.0
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 ` [PATCH v4 15/19] cifs: allow dcache population to happen asynchronously nspmangalore
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 ` nspmangalore [this message]
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-19-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