]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
smb: client: short-circuit negative lookups when parent dir is fully cached
authorHenrique Carvalho <henrique.carvalho@suse.com>
Tue, 9 Sep 2025 01:04:23 +0000 (22:04 -0300)
committerSteve French <stfrench@microsoft.com>
Thu, 2 Oct 2025 03:42:15 +0000 (22:42 -0500)
When the parent directory has a valid and complete cached enumeration we
can assume that negative dentries are not present in the directory, thus
we can return without issuing a request.

This reduces traffic for common ENOENT when the directory entries are
cached.

Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/dir.c

index 56c59b67ecc267947e56adb8b59763886d2846e3..bc145436eba49a9be1e984e27a3dca5a01ecb128 100644 (file)
@@ -683,6 +683,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
        const char *full_path;
        void *page;
        int retry_count = 0;
+       struct cached_fid *cfid = NULL;
 
        xid = get_xid();
 
@@ -722,6 +723,28 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
                cifs_dbg(FYI, "non-NULL inode in lookup\n");
        } else {
                cifs_dbg(FYI, "NULL inode in lookup\n");
+
+               /*
+                * We can only rely on negative dentries having the same
+                * spelling as the cached dirent if case insensitivity is
+                * forced on mount.
+                *
+                * XXX: if servers correctly announce Case Sensitivity Search
+                * on GetInfo of FileFSAttributeInformation, then we can take
+                * correct action even if case insensitive is not forced on
+                * mount.
+                */
+               if (pTcon->nocase && !open_cached_dir_by_dentry(pTcon, direntry->d_parent, &cfid)) {
+                       /*
+                        * dentry is negative and parent is fully cached:
+                        * we can assume file does not exist
+                        */
+                       if (cfid->dirents.is_valid) {
+                               close_cached_dir(cfid);
+                               goto out;
+                       }
+                       close_cached_dir(cfid);
+               }
        }
        cifs_dbg(FYI, "Full path: %s inode = 0x%p\n",
                 full_path, d_inode(direntry));
@@ -755,6 +778,8 @@ again:
                }
                newInode = ERR_PTR(rc);
        }
+
+out:
        free_dentry_path(page);
        cifs_put_tlink(tlink);
        free_xid(xid);
@@ -765,7 +790,8 @@ static int
 cifs_d_revalidate(struct inode *dir, const struct qstr *name,
                  struct dentry *direntry, unsigned int flags)
 {
-       struct inode *inode;
+       struct inode *inode = NULL;
+       struct cached_fid *cfid;
        int rc;
 
        if (flags & LOOKUP_RCU)
@@ -812,6 +838,21 @@ cifs_d_revalidate(struct inode *dir, const struct qstr *name,
 
                        return 1;
                }
+       } else {
+               struct cifs_sb_info *cifs_sb = CIFS_SB(dir->i_sb);
+               struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+
+               if (!open_cached_dir_by_dentry(tcon, direntry->d_parent, &cfid)) {
+                       /*
+                        * dentry is negative and parent is fully cached:
+                        * we can assume file does not exist
+                        */
+                       if (cfid->dirents.is_valid) {
+                               close_cached_dir(cfid);
+                               return 1;
+                       }
+                       close_cached_dir(cfid);
+               }
        }
 
        /*