Commit be4fde79 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French

cifs: fix dentry lookups in directory handle cache

Get rid of any prefix paths in @path before lookup_positive_unlocked()
as it will call ->lookup() which already adds those prefix paths
through build_path_from_dentry().

This has caused a performance regression when mounting shares with a
prefix path where readdir(2) would end up retrying several times to
open bad directory names that contained duplicate prefix paths.

Fix this by skipping any prefix paths in @path before calling
lookup_positive_unlocked().

Fixes: e4029e07 ("cifs: find and use the dentry for cached non-root directories also")
Cc: stable@vger.kernel.org # 6.1+
Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 7e0e76d9
...@@ -99,6 +99,23 @@ path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path) ...@@ -99,6 +99,23 @@ path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path)
return dentry; return dentry;
} }
static const char *path_no_prefix(struct cifs_sb_info *cifs_sb,
const char *path)
{
size_t len = 0;
if (!*path)
return path;
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
cifs_sb->prepath) {
len = strlen(cifs_sb->prepath) + 1;
if (unlikely(len > strlen(path)))
return ERR_PTR(-EINVAL);
}
return path + len;
}
/* /*
* Open the and cache a directory handle. * Open the and cache a directory handle.
* If error then *cfid is not initialized. * If error then *cfid is not initialized.
...@@ -125,6 +142,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, ...@@ -125,6 +142,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
struct dentry *dentry = NULL; struct dentry *dentry = NULL;
struct cached_fid *cfid; struct cached_fid *cfid;
struct cached_fids *cfids; struct cached_fids *cfids;
const char *npath;
if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
is_smb1_server(tcon->ses->server)) is_smb1_server(tcon->ses->server))
...@@ -160,6 +178,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, ...@@ -160,6 +178,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
return 0; return 0;
} }
/*
* Skip any prefix paths in @path as lookup_positive_unlocked() ends up
* calling ->lookup() which already adds those through
* build_path_from_dentry(). Also, do it earlier as we might reconnect
* below when trying to send compounded request and then potentially
* having a different prefix path (e.g. after DFS failover).
*/
npath = path_no_prefix(cifs_sb, path);
if (IS_ERR(npath)) {
rc = PTR_ERR(npath);
kfree(utf16_path);
return rc;
}
/* /*
* We do not hold the lock for the open because in case * We do not hold the lock for the open because in case
* SMB2_open needs to reconnect. * SMB2_open needs to reconnect.
...@@ -252,10 +284,10 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, ...@@ -252,10 +284,10 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
(char *)&cfid->file_all_info)) (char *)&cfid->file_all_info))
cfid->file_all_info_is_valid = true; cfid->file_all_info_is_valid = true;
if (!path[0]) if (!npath[0])
dentry = dget(cifs_sb->root); dentry = dget(cifs_sb->root);
else { else {
dentry = path_to_dentry(cifs_sb, path); dentry = path_to_dentry(cifs_sb, npath);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
rc = -ENOENT; rc = -ENOENT;
goto oshr_free; goto oshr_free;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment