Skip to content

Commit d87c48c

Browse files
Ronnie Sahlbergsmfrench
authored andcommitted
cifs: cache the dirents for entries in a cached directory
This adds caching of the directory entries for a cached directory while we keep a lease on the directory. Reviewed-by: Paulo Alcantara (SUSE) <[email protected]> Reviewed-by: Enzo Matsumiya <[email protected]> Signed-off-by: Ronnie Sahlberg <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 5752bf6 commit d87c48c

File tree

4 files changed

+213
-8
lines changed

4 files changed

+213
-8
lines changed

fs/cifs/cifsglob.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,27 @@ struct cifs_fattr {
10521052
u32 cf_cifstag;
10531053
};
10541054

1055+
struct cached_dirent {
1056+
struct list_head entry;
1057+
char *name;
1058+
int namelen;
1059+
loff_t pos;
1060+
1061+
struct cifs_fattr fattr;
1062+
};
1063+
1064+
struct cached_dirents {
1065+
bool is_valid:1;
1066+
bool is_failed:1;
1067+
struct dir_context *ctx; /*
1068+
* Only used to make sure we only take entries
1069+
* from a single context. Never dereferenced.
1070+
*/
1071+
struct mutex de_mutex;
1072+
int pos; /* Expected ctx->pos */
1073+
struct list_head entries;
1074+
};
1075+
10551076
struct cached_fid {
10561077
bool is_valid:1; /* Do we have a useable root fid */
10571078
bool file_all_info_is_valid:1;
@@ -1064,6 +1085,7 @@ struct cached_fid {
10641085
struct dentry *dentry;
10651086
struct work_struct lease_break;
10661087
struct smb2_file_all_info file_all_info;
1088+
struct cached_dirents dirents;
10671089
};
10681090

10691091
/*

fs/cifs/misc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ tconInfoAlloc(void)
114114
kfree(ret_buf);
115115
return NULL;
116116
}
117+
INIT_LIST_HEAD(&ret_buf->crfid.dirents.entries);
118+
mutex_init(&ret_buf->crfid.dirents.de_mutex);
117119

118120
atomic_inc(&tconInfoAllocCount);
119121
ret_buf->status = TID_NEW;

fs/cifs/readdir.c

Lines changed: 173 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -840,9 +840,109 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
840840
return rc;
841841
}
842842

843+
static bool emit_cached_dirents(struct cached_dirents *cde,
844+
struct dir_context *ctx)
845+
{
846+
struct cached_dirent *dirent;
847+
int rc;
848+
849+
list_for_each_entry(dirent, &cde->entries, entry) {
850+
if (ctx->pos >= dirent->pos)
851+
continue;
852+
ctx->pos = dirent->pos;
853+
rc = dir_emit(ctx, dirent->name, dirent->namelen,
854+
dirent->fattr.cf_uniqueid,
855+
dirent->fattr.cf_dtype);
856+
if (!rc)
857+
return rc;
858+
}
859+
return true;
860+
}
861+
862+
static void update_cached_dirents_count(struct cached_dirents *cde,
863+
struct dir_context *ctx)
864+
{
865+
if (cde->ctx != ctx)
866+
return;
867+
if (cde->is_valid || cde->is_failed)
868+
return;
869+
870+
cde->pos++;
871+
}
872+
873+
static void finished_cached_dirents_count(struct cached_dirents *cde,
874+
struct dir_context *ctx)
875+
{
876+
if (cde->ctx != ctx)
877+
return;
878+
if (cde->is_valid || cde->is_failed)
879+
return;
880+
if (ctx->pos != cde->pos)
881+
return;
882+
883+
cde->is_valid = 1;
884+
}
885+
886+
static void add_cached_dirent(struct cached_dirents *cde,
887+
struct dir_context *ctx,
888+
const char *name, int namelen,
889+
struct cifs_fattr *fattr)
890+
{
891+
struct cached_dirent *de;
892+
893+
if (cde->ctx != ctx)
894+
return;
895+
if (cde->is_valid || cde->is_failed)
896+
return;
897+
if (ctx->pos != cde->pos) {
898+
cde->is_failed = 1;
899+
return;
900+
}
901+
de = kzalloc(sizeof(*de), GFP_ATOMIC);
902+
if (de == NULL) {
903+
cde->is_failed = 1;
904+
return;
905+
}
906+
de->namelen = namelen;
907+
de->name = kstrndup(name, namelen, GFP_ATOMIC);
908+
if (de->name == NULL) {
909+
kfree(de);
910+
cde->is_failed = 1;
911+
return;
912+
}
913+
de->pos = ctx->pos;
914+
915+
memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
916+
917+
list_add_tail(&de->entry, &cde->entries);
918+
}
919+
920+
static bool cifs_dir_emit(struct dir_context *ctx,
921+
const char *name, int namelen,
922+
struct cifs_fattr *fattr,
923+
struct cached_fid *cfid)
924+
{
925+
bool rc;
926+
ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
927+
928+
rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
929+
if (!rc)
930+
return rc;
931+
932+
if (cfid) {
933+
mutex_lock(&cfid->dirents.de_mutex);
934+
add_cached_dirent(&cfid->dirents, ctx, name, namelen,
935+
fattr);
936+
mutex_unlock(&cfid->dirents.de_mutex);
937+
}
938+
939+
return rc;
940+
}
941+
843942
static int cifs_filldir(char *find_entry, struct file *file,
844-
struct dir_context *ctx,
845-
char *scratch_buf, unsigned int max_len)
943+
struct dir_context *ctx,
944+
char *scratch_buf, unsigned int max_len,
945+
struct cached_fid *cfid)
846946
{
847947
struct cifsFileInfo *file_info = file->private_data;
848948
struct super_block *sb = file_inode(file)->i_sb;
@@ -851,7 +951,6 @@ static int cifs_filldir(char *find_entry, struct file *file,
851951
struct cifs_fattr fattr;
852952
struct qstr name;
853953
int rc = 0;
854-
ino_t ino;
855954

856955
rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
857956
file_info->srch_inf.unicode);
@@ -931,8 +1030,8 @@ static int cifs_filldir(char *find_entry, struct file *file,
9311030

9321031
cifs_prime_dcache(file_dentry(file), &name, &fattr);
9331032

934-
ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
935-
return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype);
1033+
return !cifs_dir_emit(ctx, name.name, name.len,
1034+
&fattr, cfid);
9361035
}
9371036

9381037

@@ -941,15 +1040,18 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
9411040
int rc = 0;
9421041
unsigned int xid;
9431042
int i;
1043+
struct tcon_link *tlink = NULL;
9441044
struct cifs_tcon *tcon;
945-
struct cifsFileInfo *cifsFile = NULL;
1045+
struct cifsFileInfo *cifsFile;
9461046
char *current_entry;
9471047
int num_to_fill = 0;
9481048
char *tmp_buf = NULL;
9491049
char *end_of_smb;
9501050
unsigned int max_len;
9511051
const char *full_path;
9521052
void *page = alloc_dentry_path();
1053+
struct cached_fid *cfid = NULL;
1054+
struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
9531055

9541056
xid = get_xid();
9551057

@@ -959,6 +1061,56 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
9591061
goto rddir2_exit;
9601062
}
9611063

1064+
if (file->private_data == NULL) {
1065+
tlink = cifs_sb_tlink(cifs_sb);
1066+
if (IS_ERR(tlink))
1067+
goto cache_not_found;
1068+
tcon = tlink_tcon(tlink);
1069+
} else {
1070+
cifsFile = file->private_data;
1071+
tcon = tlink_tcon(cifsFile->tlink);
1072+
}
1073+
1074+
rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
1075+
cifs_put_tlink(tlink);
1076+
if (rc)
1077+
goto cache_not_found;
1078+
1079+
mutex_lock(&cfid->dirents.de_mutex);
1080+
/*
1081+
* If this was reading from the start of the directory
1082+
* we need to initialize scanning and storing the
1083+
* directory content.
1084+
*/
1085+
if (ctx->pos == 0 && cfid->dirents.ctx == NULL) {
1086+
cfid->dirents.ctx = ctx;
1087+
cfid->dirents.pos = 2;
1088+
}
1089+
/*
1090+
* If we already have the entire directory cached then
1091+
* we can just serve the cache.
1092+
*/
1093+
if (cfid->dirents.is_valid) {
1094+
if (!dir_emit_dots(file, ctx)) {
1095+
mutex_unlock(&cfid->dirents.de_mutex);
1096+
goto rddir2_exit;
1097+
}
1098+
emit_cached_dirents(&cfid->dirents, ctx);
1099+
mutex_unlock(&cfid->dirents.de_mutex);
1100+
goto rddir2_exit;
1101+
}
1102+
mutex_unlock(&cfid->dirents.de_mutex);
1103+
1104+
/* Drop the cache while calling initiate_cifs_search and
1105+
* find_cifs_entry in case there will be reconnects during
1106+
* query_directory.
1107+
*/
1108+
if (cfid) {
1109+
close_cached_dir(cfid);
1110+
cfid = NULL;
1111+
}
1112+
1113+
cache_not_found:
9621114
/*
9631115
* Ensure FindFirst doesn't fail before doing filldir() for '.' and
9641116
* '..'. Otherwise we won't be able to notify VFS in case of failure.
@@ -977,7 +1129,6 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
9771129
is in current search buffer?
9781130
if it before then restart search
9791131
if after then keep searching till find it */
980-
9811132
cifsFile = file->private_data;
9821133
if (cifsFile->srch_inf.endOfSearch) {
9831134
if (cifsFile->srch_inf.emptyDir) {
@@ -993,12 +1144,18 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
9931144
tcon = tlink_tcon(cifsFile->tlink);
9941145
rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path,
9951146
&current_entry, &num_to_fill);
1147+
open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
9961148
if (rc) {
9971149
cifs_dbg(FYI, "fce error %d\n", rc);
9981150
goto rddir2_exit;
9991151
} else if (current_entry != NULL) {
10001152
cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
10011153
} else {
1154+
if (cfid) {
1155+
mutex_lock(&cfid->dirents.de_mutex);
1156+
finished_cached_dirents_count(&cfid->dirents, ctx);
1157+
mutex_unlock(&cfid->dirents.de_mutex);
1158+
}
10021159
cifs_dbg(FYI, "Could not find entry\n");
10031160
goto rddir2_exit;
10041161
}
@@ -1028,14 +1185,20 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
10281185
*/
10291186
*tmp_buf = 0;
10301187
rc = cifs_filldir(current_entry, file, ctx,
1031-
tmp_buf, max_len);
1188+
tmp_buf, max_len, cfid);
10321189
if (rc) {
10331190
if (rc > 0)
10341191
rc = 0;
10351192
break;
10361193
}
10371194

10381195
ctx->pos++;
1196+
if (cfid) {
1197+
mutex_lock(&cfid->dirents.de_mutex);
1198+
update_cached_dirents_count(&cfid->dirents, ctx);
1199+
mutex_unlock(&cfid->dirents.de_mutex);
1200+
}
1201+
10391202
if (ctx->pos ==
10401203
cifsFile->srch_inf.index_of_last_entry) {
10411204
cifs_dbg(FYI, "last entry in buf at pos %lld %s\n",
@@ -1050,6 +1213,8 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
10501213
kfree(tmp_buf);
10511214

10521215
rddir2_exit:
1216+
if (cfid)
1217+
close_cached_dir(cfid);
10531218
free_dentry_path(page);
10541219
free_xid(xid);
10551220
return rc;

fs/cifs/smb2ops.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,7 @@ smb2_close_cached_fid(struct kref *ref)
699699
{
700700
struct cached_fid *cfid = container_of(ref, struct cached_fid,
701701
refcount);
702+
struct cached_dirent *dirent, *q;
702703

703704
if (cfid->is_valid) {
704705
cifs_dbg(FYI, "clear cached root file handle\n");
@@ -718,6 +719,21 @@ smb2_close_cached_fid(struct kref *ref)
718719
dput(cfid->dentry);
719720
cfid->dentry = NULL;
720721
}
722+
/*
723+
* Delete all cached dirent names
724+
*/
725+
mutex_lock(&cfid->dirents.de_mutex);
726+
list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
727+
list_del(&dirent->entry);
728+
kfree(dirent->name);
729+
kfree(dirent);
730+
}
731+
cfid->dirents.is_valid = 0;
732+
cfid->dirents.is_failed = 0;
733+
cfid->dirents.ctx = NULL;
734+
cfid->dirents.pos = 0;
735+
mutex_unlock(&cfid->dirents.de_mutex);
736+
721737
}
722738

723739
void close_cached_dir(struct cached_fid *cfid)

0 commit comments

Comments
 (0)