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 05/17] cifs: reorganize cached dir helpers
Date: Thu, 14 May 2026 23:38:11 +0530 [thread overview]
Message-ID: <20260514180823.497293-5-sprasad@microsoft.com> (raw)
In-Reply-To: <20260514180823.497293-1-sprasad@microsoft.com>
From: Shyam Prasad N <sprasad@microsoft.com>
Currently, we have helper functions for cfid and dirent caching spread
across cached_dir.c and readdir.c, with de_mutex locking done inside
the calling functions. This change neatly wraps them in helper functions
and keeps all such functions in cached_dir.c.
This code also splits the logic of dirent emit into cache and to VFS into
different functions.
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
fs/smb/client/cached_dir.c | 207 +++++++++++++++++++++++++++++++++++++
fs/smb/client/cached_dir.h | 18 +++-
fs/smb/client/readdir.c | 167 ++----------------------------
3 files changed, 231 insertions(+), 161 deletions(-)
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 88d5e9a32f28b..13a197f1c35ce 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -23,6 +23,213 @@ struct cached_dir_dentry {
struct dentry *dentry;
};
+static bool emit_cached_dirents(struct cached_dirents *cde,
+ struct dir_context *ctx)
+{
+ struct cached_dirent *dirent;
+ bool rc;
+
+ lockdep_assert_held(&cde->de_mutex);
+
+ list_for_each_entry(dirent, &cde->entries, entry) {
+ /*
+ * Skip all early entries prior to the current lseek()
+ * position.
+ */
+ if (ctx->pos > dirent->pos)
+ continue;
+ /*
+ * We recorded the current ->pos value for the dirent
+ * when we stored it in the cache.
+ * However, this sequence of ->pos values may have holes
+ * in it, for example dot-dirs returned from the server
+ * are suppressed.
+ * Handle this by forcing ctx->pos to be the same as the
+ * ->pos of the current dirent we emit from the cache.
+ * This means that when we emit these entries from the cache
+ * we now emit them with the same ->pos value as in the
+ * initial scan.
+ */
+ ctx->pos = dirent->pos;
+ rc = dir_emit(ctx, dirent->name, dirent->namelen,
+ dirent->fattr.cf_uniqueid,
+ dirent->fattr.cf_dtype);
+ if (!rc)
+ return rc;
+ ctx->pos++;
+ }
+ return true;
+}
+
+static bool add_cached_dirent(struct cached_dirents *cde,
+ struct dir_context *ctx, const char *name,
+ int namelen, struct cifs_fattr *fattr,
+ struct file *file)
+{
+ struct cached_dirent *de;
+
+ lockdep_assert_held(&cde->de_mutex);
+
+ if (cde->file != file)
+ return false;
+ if (cde->is_valid || cde->is_failed)
+ return false;
+ if (ctx->pos != cde->pos) {
+ cde->is_failed = 1;
+ return false;
+ }
+ de = kzalloc_obj(*de, GFP_KERNEL);
+ if (de == NULL) {
+ cde->is_failed = 1;
+ return false;
+ }
+ de->namelen = namelen;
+ de->name = kstrndup(name, namelen, GFP_KERNEL);
+ if (de->name == NULL) {
+ kfree(de);
+ cde->is_failed = 1;
+ return false;
+ }
+ de->pos = ctx->pos;
+
+ memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
+
+ list_add_tail(&de->entry, &cde->entries);
+ /* update accounting */
+ cde->entries_count++;
+ cde->bytes_used += sizeof(*de) + (size_t)namelen + 1;
+ return true;
+}
+
+bool emit_cached_dir_if_valid(struct cached_fid *cfid,
+ struct file *file,
+ struct dir_context *ctx)
+{
+ if (!cfid)
+ return false;
+
+ mutex_lock(&cfid->dirents.de_mutex);
+ /*
+ * If this was reading from the start of the directory
+ * we need to initialize scanning and storing the
+ * directory content.
+ */
+ if (ctx->pos == 0 && cfid->dirents.file == NULL) {
+ cfid->dirents.file = file;
+ cfid->dirents.pos = 2;
+ }
+
+ if (!cfid->dirents.is_valid) {
+ mutex_unlock(&cfid->dirents.de_mutex);
+ return false;
+ }
+
+ if (dir_emit_dots(file, ctx))
+ emit_cached_dirents(&cfid->dirents, ctx);
+
+ mutex_unlock(&cfid->dirents.de_mutex);
+ return true;
+}
+
+bool add_to_cached_dir(struct cached_fid *cfid,
+ struct dir_context *ctx,
+ const char *name,
+ int namelen,
+ struct cifs_fattr *fattr,
+ struct file *file)
+{
+ size_t delta_bytes;
+ bool added = false;
+
+ if (!cfid)
+ return false;
+
+ /* Cost of this entry */
+ delta_bytes = sizeof(struct cached_dirent) + (size_t)namelen + 1;
+
+ mutex_lock(&cfid->dirents.de_mutex);
+ added = add_cached_dirent(&cfid->dirents, ctx, name, namelen,
+ fattr, file);
+ mutex_unlock(&cfid->dirents.de_mutex);
+
+ if (added) {
+ /* per-tcon then global for consistency with free path */
+ atomic64_add((long long)delta_bytes, &cfid->cfids->total_dirents_bytes);
+ atomic_long_inc(&cfid->cfids->total_dirents_entries);
+ atomic64_add((long long)delta_bytes, &cifs_dircache_bytes_used);
+ }
+
+ return added;
+}
+
+static void update_cached_dirents_count(struct cached_dirents *cde,
+ struct file *file)
+{
+ if (cde->file != file)
+ return;
+ if (cde->is_valid || cde->is_failed)
+ return;
+
+ cde->pos++;
+}
+
+static void finished_cached_dirents_count(struct cached_dirents *cde,
+ struct dir_context *ctx,
+ struct file *file)
+{
+ if (cde->file != file)
+ return;
+ if (cde->is_valid || cde->is_failed)
+ return;
+ if (ctx->pos != cde->pos)
+ return;
+
+ cde->is_valid = 1;
+}
+
+void update_pos_cached_dir(struct cached_fid *cfid,
+ struct file *file)
+{
+ if (!cfid)
+ return;
+
+ mutex_lock(&cfid->dirents.de_mutex);
+ update_cached_dirents_count(&cfid->dirents, file);
+ mutex_unlock(&cfid->dirents.de_mutex);
+}
+
+void complete_cached_dir(struct cached_fid *cfid,
+ struct dir_context *ctx,
+ struct file *file)
+{
+ if (!cfid)
+ return;
+
+ mutex_lock(&cfid->dirents.de_mutex);
+ finished_cached_dirents_count(&cfid->dirents, ctx, file);
+ mutex_unlock(&cfid->dirents.de_mutex);
+}
+
+struct cached_dirent *lookup_cached_dirent(struct cached_dirents *cde,
+ const char *name,
+ unsigned int namelen)
+{
+ struct cached_dirent *entry;
+
+ if (!cde)
+ return NULL;
+
+ lockdep_assert_held(&cde->de_mutex);
+
+ list_for_each_entry(entry, &cde->entries, entry) {
+ if (entry->namelen == namelen &&
+ memcmp(entry->name, name, namelen) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
const char *path,
bool lookup_only,
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index fc756836da95a..724d57a8a5bc6 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -8,7 +8,6 @@
#ifndef _CACHED_DIR_H
#define _CACHED_DIR_H
-
struct cached_dirent {
struct list_head entry;
char *name;
@@ -87,6 +86,23 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, const char *path,
int open_cached_dir_by_dentry(struct cifs_tcon *tcon, struct dentry *dentry,
struct cached_fid **ret_cfid);
void close_cached_dir(struct cached_fid *cfid);
+bool emit_cached_dir_if_valid(struct cached_fid *cfid,
+ struct file *file,
+ struct dir_context *ctx);
+bool add_to_cached_dir(struct cached_fid *cfid,
+ struct dir_context *ctx,
+ const char *name,
+ int namelen,
+ struct cifs_fattr *fattr,
+ struct file *file);
+void update_pos_cached_dir(struct cached_fid *cfid,
+ struct file *file);
+void complete_cached_dir(struct cached_fid *cfid,
+ struct dir_context *ctx,
+ struct file *file);
+struct cached_dirent *lookup_cached_dirent(struct cached_dirents *cde,
+ const char *name,
+ unsigned int namelen);
void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb);
void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
index 8a444f97e0ae9..907e235ad1b8f 100644
--- a/fs/smb/client/readdir.c
+++ b/fs/smb/client/readdir.c
@@ -817,136 +817,13 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
return rc;
}
-static bool emit_cached_dirents(struct cached_dirents *cde,
- struct dir_context *ctx)
-{
- struct cached_dirent *dirent;
- bool rc;
-
- list_for_each_entry(dirent, &cde->entries, entry) {
- /*
- * Skip all early entries prior to the current lseek()
- * position.
- */
- if (ctx->pos > dirent->pos)
- continue;
- /*
- * We recorded the current ->pos value for the dirent
- * when we stored it in the cache.
- * However, this sequence of ->pos values may have holes
- * in it, for example dot-dirs returned from the server
- * are suppressed.
- * Handle this by forcing ctx->pos to be the same as the
- * ->pos of the current dirent we emit from the cache.
- * This means that when we emit these entries from the cache
- * we now emit them with the same ->pos value as in the
- * initial scan.
- */
- ctx->pos = dirent->pos;
- rc = dir_emit(ctx, dirent->name, dirent->namelen,
- dirent->fattr.cf_uniqueid,
- dirent->fattr.cf_dtype);
- if (!rc)
- return rc;
- ctx->pos++;
- }
- return true;
-}
-
-static void update_cached_dirents_count(struct cached_dirents *cde,
- struct file *file)
-{
- if (cde->file != file)
- return;
- if (cde->is_valid || cde->is_failed)
- return;
-
- cde->pos++;
-}
-
-static void finished_cached_dirents_count(struct cached_dirents *cde,
- struct dir_context *ctx, struct file *file)
-{
- if (cde->file != file)
- return;
- if (cde->is_valid || cde->is_failed)
- return;
- if (ctx->pos != cde->pos)
- return;
-
- cde->is_valid = 1;
-}
-
-static bool add_cached_dirent(struct cached_dirents *cde,
- struct dir_context *ctx, const char *name,
- int namelen, struct cifs_fattr *fattr,
- struct file *file)
-{
- struct cached_dirent *de;
-
- if (cde->file != file)
- return false;
- if (cde->is_valid || cde->is_failed)
- return false;
- if (ctx->pos != cde->pos) {
- cde->is_failed = 1;
- return false;
- }
- de = kzalloc_obj(*de, GFP_ATOMIC);
- if (de == NULL) {
- cde->is_failed = 1;
- return false;
- }
- de->namelen = namelen;
- de->name = kstrndup(name, namelen, GFP_ATOMIC);
- if (de->name == NULL) {
- kfree(de);
- cde->is_failed = 1;
- return false;
- }
- de->pos = ctx->pos;
-
- memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
-
- list_add_tail(&de->entry, &cde->entries);
- /* update accounting */
- cde->entries_count++;
- cde->bytes_used += sizeof(*de) + (size_t)namelen + 1;
- return true;
-}
-
static bool cifs_dir_emit(struct dir_context *ctx,
const char *name, int namelen,
- struct cifs_fattr *fattr,
- struct cached_fid *cfid,
- struct file *file)
+ struct cifs_fattr *fattr)
{
- size_t delta_bytes = 0;
- bool rc, added = false;
ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
- rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
- if (!rc)
- return rc;
-
- if (cfid) {
- /* Cost of this entry */
- delta_bytes = sizeof(struct cached_dirent) + (size_t)namelen + 1;
-
- mutex_lock(&cfid->dirents.de_mutex);
- added = add_cached_dirent(&cfid->dirents, ctx, name, namelen,
- fattr, file);
- mutex_unlock(&cfid->dirents.de_mutex);
-
- if (added) {
- /* per-tcon then global for consistency with free path */
- atomic64_add((long long)delta_bytes, &cfid->cfids->total_dirents_bytes);
- atomic_long_inc(&cfid->cfids->total_dirents_entries);
- atomic64_add((long long)delta_bytes, &cifs_dircache_bytes_used);
- }
- }
-
- return rc;
+ return dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
}
static int cifs_filldir(char *find_entry, struct file *file,
@@ -1040,10 +917,10 @@ 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);
- return !cifs_dir_emit(ctx, name.name, name.len,
- &fattr, cfid, file);
+ return !cifs_dir_emit(ctx, name.name, name.len, &fattr);
}
@@ -1088,30 +965,8 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
if (rc)
goto cache_not_found;
- mutex_lock(&cfid->dirents.de_mutex);
- /*
- * If this was reading from the start of the directory
- * we need to initialize scanning and storing the
- * directory content.
- */
- if (ctx->pos == 0 && cfid->dirents.file == NULL) {
- cfid->dirents.file = file;
- cfid->dirents.pos = 2;
- }
- /*
- * If we already have the entire directory cached then
- * we can just serve the cache.
- */
- if (cfid->dirents.is_valid) {
- if (!dir_emit_dots(file, ctx)) {
- mutex_unlock(&cfid->dirents.de_mutex);
- goto rddir2_exit;
- }
- emit_cached_dirents(&cfid->dirents, ctx);
- mutex_unlock(&cfid->dirents.de_mutex);
+ if (emit_cached_dir_if_valid(cfid, file, ctx))
goto rddir2_exit;
- }
- mutex_unlock(&cfid->dirents.de_mutex);
/* Drop the cache while calling initiate_cifs_search and
* find_cifs_entry in case there will be reconnects during
@@ -1161,11 +1016,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
} else if (current_entry != NULL) {
cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
} else {
- if (cfid) {
- mutex_lock(&cfid->dirents.de_mutex);
- finished_cached_dirents_count(&cfid->dirents, ctx, file);
- mutex_unlock(&cfid->dirents.de_mutex);
- }
+ complete_cached_dir(cfid, ctx, file);
cifs_dbg(FYI, "Could not find entry\n");
goto rddir2_exit;
}
@@ -1202,11 +1053,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
}
ctx->pos++;
- if (cfid) {
- mutex_lock(&cfid->dirents.de_mutex);
- update_cached_dirents_count(&cfid->dirents, file);
- mutex_unlock(&cfid->dirents.de_mutex);
- }
+ update_pos_cached_dir(cfid, file);
if (ctx->pos ==
cifsFile->srch_inf.index_of_last_entry) {
--
2.43.0
next prev parent reply other threads:[~2026-05-14 18:08 UTC|newest]
Thread overview: 20+ 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 ` nspmangalore [this message]
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 ` [PATCH v5 10/17] cifs: register a shrinker to manage cached_dirents nspmangalore
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-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-5-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.