smb: improve directory cache reuse for readdir operations

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>

authored by

Bharath SM and committed by
Steve French
72dd7961 b64af6bc

+19 -17
+4 -4
fs/smb/client/cached_dir.h
··· 21 struct cached_dirents { 22 bool is_valid:1; 23 bool is_failed:1; 24 - struct dir_context *ctx; /* 25 - * Only used to make sure we only take entries 26 - * from a single context. Never dereferenced. 27 - */ 28 struct mutex de_mutex; 29 int pos; /* Expected ctx->pos */ 30 struct list_head entries;
··· 21 struct cached_dirents { 22 bool is_valid:1; 23 bool is_failed:1; 24 + struct file *file; /* 25 + * Used to associate the cache with a single 26 + * open file instance. 27 + */ 28 struct mutex de_mutex; 29 int pos; /* Expected ctx->pos */ 30 struct list_head entries;
+15 -13
fs/smb/client/readdir.c
··· 851 } 852 853 static void update_cached_dirents_count(struct cached_dirents *cde, 854 - struct dir_context *ctx) 855 { 856 - if (cde->ctx != ctx) 857 return; 858 if (cde->is_valid || cde->is_failed) 859 return; ··· 862 } 863 864 static void finished_cached_dirents_count(struct cached_dirents *cde, 865 - struct dir_context *ctx) 866 { 867 - if (cde->ctx != ctx) 868 return; 869 if (cde->is_valid || cde->is_failed) 870 return; ··· 877 static void add_cached_dirent(struct cached_dirents *cde, 878 struct dir_context *ctx, 879 const char *name, int namelen, 880 - struct cifs_fattr *fattr) 881 { 882 struct cached_dirent *de; 883 884 - if (cde->ctx != ctx) 885 return; 886 if (cde->is_valid || cde->is_failed) 887 return; ··· 912 static bool cifs_dir_emit(struct dir_context *ctx, 913 const char *name, int namelen, 914 struct cifs_fattr *fattr, 915 - struct cached_fid *cfid) 916 { 917 bool rc; 918 ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); ··· 925 if (cfid) { 926 mutex_lock(&cfid->dirents.de_mutex); 927 add_cached_dirent(&cfid->dirents, ctx, name, namelen, 928 - fattr); 929 mutex_unlock(&cfid->dirents.de_mutex); 930 } 931 ··· 1025 cifs_prime_dcache(file_dentry(file), &name, &fattr); 1026 1027 return !cifs_dir_emit(ctx, name.name, name.len, 1028 - &fattr, cfid); 1029 } 1030 1031 ··· 1076 * we need to initialize scanning and storing the 1077 * directory content. 1078 */ 1079 - if (ctx->pos == 0 && cfid->dirents.ctx == NULL) { 1080 - cfid->dirents.ctx = ctx; 1081 cfid->dirents.pos = 2; 1082 } 1083 /* ··· 1145 } else { 1146 if (cfid) { 1147 mutex_lock(&cfid->dirents.de_mutex); 1148 - finished_cached_dirents_count(&cfid->dirents, ctx); 1149 mutex_unlock(&cfid->dirents.de_mutex); 1150 } 1151 cifs_dbg(FYI, "Could not find entry\n"); ··· 1186 ctx->pos++; 1187 if (cfid) { 1188 mutex_lock(&cfid->dirents.de_mutex); 1189 - update_cached_dirents_count(&cfid->dirents, ctx); 1190 mutex_unlock(&cfid->dirents.de_mutex); 1191 } 1192
··· 851 } 852 853 static void update_cached_dirents_count(struct cached_dirents *cde, 854 + struct file *file) 855 { 856 + if (cde->file != file) 857 return; 858 if (cde->is_valid || cde->is_failed) 859 return; ··· 862 } 863 864 static void finished_cached_dirents_count(struct cached_dirents *cde, 865 + struct dir_context *ctx, struct file *file) 866 { 867 + if (cde->file != file) 868 return; 869 if (cde->is_valid || cde->is_failed) 870 return; ··· 877 static void add_cached_dirent(struct cached_dirents *cde, 878 struct dir_context *ctx, 879 const char *name, int namelen, 880 + struct cifs_fattr *fattr, 881 + struct file *file) 882 { 883 struct cached_dirent *de; 884 885 + if (cde->file != file) 886 return; 887 if (cde->is_valid || cde->is_failed) 888 return; ··· 911 static bool cifs_dir_emit(struct dir_context *ctx, 912 const char *name, int namelen, 913 struct cifs_fattr *fattr, 914 + struct cached_fid *cfid, 915 + struct file *file) 916 { 917 bool rc; 918 ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); ··· 923 if (cfid) { 924 mutex_lock(&cfid->dirents.de_mutex); 925 add_cached_dirent(&cfid->dirents, ctx, name, namelen, 926 + fattr, file); 927 mutex_unlock(&cfid->dirents.de_mutex); 928 } 929 ··· 1023 cifs_prime_dcache(file_dentry(file), &name, &fattr); 1024 1025 return !cifs_dir_emit(ctx, name.name, name.len, 1026 + &fattr, cfid, file); 1027 } 1028 1029 ··· 1074 * we need to initialize scanning and storing the 1075 * directory content. 1076 */ 1077 + if (ctx->pos == 0 && cfid->dirents.file == NULL) { 1078 + cfid->dirents.file = file; 1079 cfid->dirents.pos = 2; 1080 } 1081 /* ··· 1143 } else { 1144 if (cfid) { 1145 mutex_lock(&cfid->dirents.de_mutex); 1146 + finished_cached_dirents_count(&cfid->dirents, ctx, file); 1147 mutex_unlock(&cfid->dirents.de_mutex); 1148 } 1149 cifs_dbg(FYI, "Could not find entry\n"); ··· 1184 ctx->pos++; 1185 if (cfid) { 1186 mutex_lock(&cfid->dirents.de_mutex); 1187 + update_cached_dirents_count(&cfid->dirents, file); 1188 mutex_unlock(&cfid->dirents.de_mutex); 1189 } 1190