[PATCH] smb: improve directory cache reuse for readdir operations
Steve French
smfrench at gmail.com
Wed Jun 11 15:12:07 UTC 2025
merged this updated patch into cifs-2.6.git for-next, running xfstests
on it now.
Looks very promising, and we have a couple more dir lease
optimizations to try out that should also help a lot with perf, and
reducing load on servers by sending fewer metadata ops over the wire
On Wed, Jun 11, 2025 at 6:29 AM Bharath SM <bharathsm.hsk at gmail.com> wrote:
>
> Currently, cached directory contents were not reused across subsequent
> 'ls' operations because the cache validity check relied on comparing
> the ctx pointer, which changes with each readdir invocation. As a
> result, the cached dir entries was not marked as valid and the cache was
> not utilized for subsequent 'ls' operations.
>
> This change uses the file pointer, which remains consistent across all
> readdir calls for a given directory instance, to associate and validate
> the cache. As a result, cached directory contents can now be
> correctly reused, improving performance for repeated directory listings.
>
> Performance gains with local windows SMB server:
>
> Without the patch and default actimeo=1:
> 1000 directory enumeration operations on dir with 10k files took 135.0s
>
> With this patch and actimeo=0:
> 1000 directory enumeration operations on dir with 10k files took just 5.1s
>
> Signed-off-by: Bharath SM <bharathsm at microsoft.com>
> ---
> fs/smb/client/cached_dir.h | 8 ++++----
> fs/smb/client/readdir.c | 28 +++++++++++++++-------------
> 2 files changed, 19 insertions(+), 17 deletions(-)
>
> diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
> index 1dfe79d947a6..bc8a812ff95f 100644
> --- a/fs/smb/client/cached_dir.h
> +++ b/fs/smb/client/cached_dir.h
> @@ -21,10 +21,10 @@ struct cached_dirent {
> struct cached_dirents {
> bool is_valid:1;
> bool is_failed:1;
> - struct dir_context *ctx; /*
> - * Only used to make sure we only take entries
> - * from a single context. Never dereferenced.
> - */
> + struct file *file; /*
> + * Used to associate the cache with a single
> + * open file instance.
> + */
> struct mutex de_mutex;
> int pos; /* Expected ctx->pos */
> struct list_head entries;
> diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
> index f9f11cbf89be..ba0193cf9033 100644
> --- a/fs/smb/client/readdir.c
> +++ b/fs/smb/client/readdir.c
> @@ -851,9 +851,9 @@ static bool emit_cached_dirents(struct cached_dirents *cde,
> }
>
> static void update_cached_dirents_count(struct cached_dirents *cde,
> - struct dir_context *ctx)
> + struct file *file)
> {
> - if (cde->ctx != ctx)
> + if (cde->file != file)
> return;
> if (cde->is_valid || cde->is_failed)
> return;
> @@ -862,9 +862,9 @@ static void update_cached_dirents_count(struct cached_dirents *cde,
> }
>
> static void finished_cached_dirents_count(struct cached_dirents *cde,
> - struct dir_context *ctx)
> + struct dir_context *ctx, struct file *file)
> {
> - if (cde->ctx != ctx)
> + if (cde->file != file)
> return;
> if (cde->is_valid || cde->is_failed)
> return;
> @@ -877,11 +877,12 @@ static void finished_cached_dirents_count(struct cached_dirents *cde,
> static void add_cached_dirent(struct cached_dirents *cde,
> struct dir_context *ctx,
> const char *name, int namelen,
> - struct cifs_fattr *fattr)
> + struct cifs_fattr *fattr,
> + struct file *file)
> {
> struct cached_dirent *de;
>
> - if (cde->ctx != ctx)
> + if (cde->file != file)
> return;
> if (cde->is_valid || cde->is_failed)
> return;
> @@ -911,7 +912,8 @@ static void add_cached_dirent(struct cached_dirents *cde,
> static bool cifs_dir_emit(struct dir_context *ctx,
> const char *name, int namelen,
> struct cifs_fattr *fattr,
> - struct cached_fid *cfid)
> + struct cached_fid *cfid,
> + struct file *file)
> {
> bool rc;
> ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
> @@ -923,7 +925,7 @@ static bool cifs_dir_emit(struct dir_context *ctx,
> if (cfid) {
> mutex_lock(&cfid->dirents.de_mutex);
> add_cached_dirent(&cfid->dirents, ctx, name, namelen,
> - fattr);
> + fattr, file);
> mutex_unlock(&cfid->dirents.de_mutex);
> }
>
> @@ -1023,7 +1025,7 @@ static int cifs_filldir(char *find_entry, struct file *file,
> cifs_prime_dcache(file_dentry(file), &name, &fattr);
>
> return !cifs_dir_emit(ctx, name.name, name.len,
> - &fattr, cfid);
> + &fattr, cfid, file);
> }
>
>
> @@ -1074,8 +1076,8 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
> * we need to initialize scanning and storing the
> * directory content.
> */
> - if (ctx->pos == 0 && cfid->dirents.ctx == NULL) {
> - cfid->dirents.ctx = ctx;
> + if (ctx->pos == 0 && cfid->dirents.file == NULL) {
> + cfid->dirents.file = file;
> cfid->dirents.pos = 2;
> }
> /*
> @@ -1143,7 +1145,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
> } else {
> if (cfid) {
> mutex_lock(&cfid->dirents.de_mutex);
> - finished_cached_dirents_count(&cfid->dirents, ctx);
> + finished_cached_dirents_count(&cfid->dirents, ctx, file);
> mutex_unlock(&cfid->dirents.de_mutex);
> }
> cifs_dbg(FYI, "Could not find entry\n");
> @@ -1184,7 +1186,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, ctx);
> + update_cached_dirents_count(&cfid->dirents, file);
> mutex_unlock(&cfid->dirents.de_mutex);
> }
>
> --
> 2.43.0
>
--
Thanks,
Steve
More information about the samba-technical
mailing list