From: nspmangalore@gmail.com
To: linux-cifs@vger.kernel.org, smfrench@gmail.com, pc@manguebit.com,
bharathsm@microsoft.com, dhowells@redhat.com,
henrique.carvalho@suse.com, ematsumiya@suse.de
Cc: Shyam Prasad N <sprasad@microsoft.com>
Subject: [PATCH 7/7] cifs: reorganize cached dir helpers
Date: Tue, 14 Apr 2026 19:29:18 +0530 [thread overview]
Message-ID: <20260414135918.279802-7-sprasad@microsoft.com> (raw)
In-Reply-To: <20260414135918.279802-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 e9917e5204b00..10fe703d353d1 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 19d5592512e4b..09f1f488059c9 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
prev parent reply other threads:[~2026-04-14 13:59 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-14 13:59 [PATCH 1/7] cifs: change_conf needs to be called for session setup nspmangalore
2026-04-14 13:59 ` [PATCH 2/7] cifs: abort open_cached_dir if we don't request leases nspmangalore
2026-04-14 13:59 ` [PATCH 3/7] cifs: invalidate cfid on unlink/rename/rmdir nspmangalore
2026-04-14 13:59 ` [PATCH 4/7] cifs: define variable sized buffer for querydir responses nspmangalore
2026-04-14 13:59 ` [PATCH 5/7] cifs: optimize readdir for small directories nspmangalore
2026-04-14 13:59 ` [PATCH 6/7] cifs: optimize readdir for larger directories nspmangalore
2026-04-15 23:08 ` Steve French
2026-04-15 23:11 ` Steve French
2026-04-14 13:59 ` 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=20260414135918.279802-7-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.com \
--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