cifs: Optimize readdir on reparse points
When listing a directory with thounsands of files and most of them are reparse points, we simply marked all those dentries for revalidation and then sending additional (compounded) create/getinfo/close requests for each of them. Instead, upon receiving a response from an SMB2_QUERY_DIRECTORY (FileIdFullDirectoryInformation) command, the directory entries that have a file attribute of FILE_ATTRIBUTE_REPARSE_POINT will contain an EaSize field with a reparse tag in it, so we parse it and mark the dentry for revalidation only if it is a DFS or a symlink. Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
7935799e04
commit
046aca3c25
|
@ -1693,6 +1693,7 @@ struct cifs_fattr {
|
||||||
struct timespec64 cf_atime;
|
struct timespec64 cf_atime;
|
||||||
struct timespec64 cf_mtime;
|
struct timespec64 cf_mtime;
|
||||||
struct timespec64 cf_ctime;
|
struct timespec64 cf_ctime;
|
||||||
|
u32 cf_cifstag;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void free_dfs_info_param(struct dfs_info3_param *param)
|
static inline void free_dfs_info_param(struct dfs_info3_param *param)
|
||||||
|
|
|
@ -139,6 +139,28 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool reparse_file_needs_reval(const struct cifs_fattr *fattr)
|
||||||
|
{
|
||||||
|
if (!(fattr->cf_cifsattrs & ATTR_REPARSE))
|
||||||
|
return false;
|
||||||
|
/*
|
||||||
|
* The DFS tags should be only intepreted by server side as per
|
||||||
|
* MS-FSCC 2.1.2.1, but let's include them anyway.
|
||||||
|
*
|
||||||
|
* Besides, if cf_cifstag is unset (0), then we still need it to be
|
||||||
|
* revalidated to know exactly what reparse point it is.
|
||||||
|
*/
|
||||||
|
switch (fattr->cf_cifstag) {
|
||||||
|
case IO_REPARSE_TAG_DFS:
|
||||||
|
case IO_REPARSE_TAG_DFSR:
|
||||||
|
case IO_REPARSE_TAG_SYMLINK:
|
||||||
|
case IO_REPARSE_TAG_NFS:
|
||||||
|
case 0:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
||||||
{
|
{
|
||||||
|
@ -158,7 +180,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
||||||
* is a symbolic link, DFS referral or a reparse point with a direct
|
* is a symbolic link, DFS referral or a reparse point with a direct
|
||||||
* access like junctions, deduplicated files, NFS symlinks.
|
* access like junctions, deduplicated files, NFS symlinks.
|
||||||
*/
|
*/
|
||||||
if (fattr->cf_cifsattrs & ATTR_REPARSE)
|
if (reparse_file_needs_reval(fattr))
|
||||||
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
|
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
|
||||||
|
|
||||||
/* non-unix readdir doesn't provide nlink */
|
/* non-unix readdir doesn't provide nlink */
|
||||||
|
@ -194,19 +216,37 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info)
|
||||||
|
{
|
||||||
|
const FILE_DIRECTORY_INFO *fi = info;
|
||||||
|
|
||||||
|
memset(fattr, 0, sizeof(*fattr));
|
||||||
|
fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes);
|
||||||
|
fattr->cf_eof = le64_to_cpu(fi->EndOfFile);
|
||||||
|
fattr->cf_bytes = le64_to_cpu(fi->AllocationSize);
|
||||||
|
fattr->cf_createtime = le64_to_cpu(fi->CreationTime);
|
||||||
|
fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime);
|
||||||
|
fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime);
|
||||||
|
fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
|
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
|
||||||
struct cifs_sb_info *cifs_sb)
|
struct cifs_sb_info *cifs_sb)
|
||||||
{
|
{
|
||||||
memset(fattr, 0, sizeof(*fattr));
|
__dir_info_to_fattr(fattr, info);
|
||||||
fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes);
|
cifs_fill_common_info(fattr, cifs_sb);
|
||||||
fattr->cf_eof = le64_to_cpu(info->EndOfFile);
|
}
|
||||||
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
|
|
||||||
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
|
|
||||||
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
|
|
||||||
fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
|
|
||||||
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
|
|
||||||
|
|
||||||
|
static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr,
|
||||||
|
SEARCH_ID_FULL_DIR_INFO *info,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
__dir_info_to_fattr(fattr, info);
|
||||||
|
|
||||||
|
/* See MS-FSCC 2.4.18 FileIdFullDirectoryInformation */
|
||||||
|
if (fattr->cf_cifsattrs & ATTR_REPARSE)
|
||||||
|
fattr->cf_cifstag = le32_to_cpu(info->EaSize);
|
||||||
cifs_fill_common_info(fattr, cifs_sb);
|
cifs_fill_common_info(fattr, cifs_sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,6 +795,11 @@ static int cifs_filldir(char *find_entry, struct file *file,
|
||||||
(FIND_FILE_STANDARD_INFO *)find_entry,
|
(FIND_FILE_STANDARD_INFO *)find_entry,
|
||||||
cifs_sb);
|
cifs_sb);
|
||||||
break;
|
break;
|
||||||
|
case SMB_FIND_FILE_ID_FULL_DIR_INFO:
|
||||||
|
cifs_fulldir_info_to_fattr(&fattr,
|
||||||
|
(SEARCH_ID_FULL_DIR_INFO *)find_entry,
|
||||||
|
cifs_sb);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
cifs_dir_info_to_fattr(&fattr,
|
cifs_dir_info_to_fattr(&fattr,
|
||||||
(FILE_DIRECTORY_INFO *)find_entry,
|
(FILE_DIRECTORY_INFO *)find_entry,
|
||||||
|
|
Loading…
Reference in New Issue