smb: improve directory cache reuse for readdir operations

commit 72dd7961a4bb4fa1fc456169a61dd12e68e50645 upstream.

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@microsoft.com>
Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
Cc: stable@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Bharath SM
2025-06-11 16:59:02 +05:30
committed by Greg Kroah-Hartman
parent 202d7e8389
commit a504536c29
2 changed files with 19 additions and 17 deletions

View File

@@ -21,10 +21,10 @@ struct cached_dirent {
struct cached_dirents { struct cached_dirents {
bool is_valid:1; bool is_valid:1;
bool is_failed:1; bool is_failed:1;
struct dir_context *ctx; /* struct file *file; /*
* Only used to make sure we only take entries * Used to associate the cache with a single
* from a single context. Never dereferenced. * open file instance.
*/ */
struct mutex de_mutex; struct mutex de_mutex;
int pos; /* Expected ctx->pos */ int pos; /* Expected ctx->pos */
struct list_head entries; struct list_head entries;

View File

@@ -850,9 +850,9 @@ static bool emit_cached_dirents(struct cached_dirents *cde,
} }
static void update_cached_dirents_count(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; return;
if (cde->is_valid || cde->is_failed) if (cde->is_valid || cde->is_failed)
return; return;
@@ -861,9 +861,9 @@ static void update_cached_dirents_count(struct cached_dirents *cde,
} }
static void finished_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; return;
if (cde->is_valid || cde->is_failed) if (cde->is_valid || cde->is_failed)
return; return;
@@ -876,11 +876,12 @@ static void finished_cached_dirents_count(struct cached_dirents *cde,
static void add_cached_dirent(struct cached_dirents *cde, static void add_cached_dirent(struct cached_dirents *cde,
struct dir_context *ctx, struct dir_context *ctx,
const char *name, int namelen, const char *name, int namelen,
struct cifs_fattr *fattr) struct cifs_fattr *fattr,
struct file *file)
{ {
struct cached_dirent *de; struct cached_dirent *de;
if (cde->ctx != ctx) if (cde->file != file)
return; return;
if (cde->is_valid || cde->is_failed) if (cde->is_valid || cde->is_failed)
return; return;
@@ -910,7 +911,8 @@ static void add_cached_dirent(struct cached_dirents *cde,
static bool cifs_dir_emit(struct dir_context *ctx, static bool cifs_dir_emit(struct dir_context *ctx,
const char *name, int namelen, const char *name, int namelen,
struct cifs_fattr *fattr, struct cifs_fattr *fattr,
struct cached_fid *cfid) struct cached_fid *cfid,
struct file *file)
{ {
bool rc; bool rc;
ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
@@ -922,7 +924,7 @@ static bool cifs_dir_emit(struct dir_context *ctx,
if (cfid) { if (cfid) {
mutex_lock(&cfid->dirents.de_mutex); mutex_lock(&cfid->dirents.de_mutex);
add_cached_dirent(&cfid->dirents, ctx, name, namelen, add_cached_dirent(&cfid->dirents, ctx, name, namelen,
fattr); fattr, file);
mutex_unlock(&cfid->dirents.de_mutex); mutex_unlock(&cfid->dirents.de_mutex);
} }
@@ -1022,7 +1024,7 @@ static int cifs_filldir(char *find_entry, struct file *file,
cifs_prime_dcache(file_dentry(file), &name, &fattr); cifs_prime_dcache(file_dentry(file), &name, &fattr);
return !cifs_dir_emit(ctx, name.name, name.len, return !cifs_dir_emit(ctx, name.name, name.len,
&fattr, cfid); &fattr, cfid, file);
} }
@@ -1073,8 +1075,8 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
* we need to initialize scanning and storing the * we need to initialize scanning and storing the
* directory content. * directory content.
*/ */
if (ctx->pos == 0 && cfid->dirents.ctx == NULL) { if (ctx->pos == 0 && cfid->dirents.file == NULL) {
cfid->dirents.ctx = ctx; cfid->dirents.file = file;
cfid->dirents.pos = 2; cfid->dirents.pos = 2;
} }
/* /*
@@ -1142,7 +1144,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
} else { } else {
if (cfid) { if (cfid) {
mutex_lock(&cfid->dirents.de_mutex); 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); mutex_unlock(&cfid->dirents.de_mutex);
} }
cifs_dbg(FYI, "Could not find entry\n"); cifs_dbg(FYI, "Could not find entry\n");
@@ -1183,7 +1185,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
ctx->pos++; ctx->pos++;
if (cfid) { if (cfid) {
mutex_lock(&cfid->dirents.de_mutex); 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); mutex_unlock(&cfid->dirents.de_mutex);
} }