summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-07-03 12:00:10 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-07-03 12:37:15 -0300
commit33ef9fe668688c3c390c0048dce382b846bc22c5 (patch)
treec9fc0f918f357e0da21a93379c7d26a83068c5b5
parent89fa862ca44a88bf70a03f82647f6ed1e09bd7ac (diff)
downloadlinux-33ef9fe668688c3c390c0048dce382b846bc22c5.tar.gz
linux-33ef9fe668688c3c390c0048dce382b846bc22c5.tar.bz2
linux-33ef9fe668688c3c390c0048dce382b846bc22c5.zip
smb: client: add lookup_cached_dir()
Remove find_or_create_cached_dir(), and move the lookup part into lookup_cached_dir() and the remaining allocation part into init_cached_dir(). Also remove the @lookup_only arg from open_cached_dir() and replace calls where it was true to use lookup_cached_dir(). This allows better distiction of actions (e.g. can/want a real remote open if not cached vs a simple lookup) and more flexibility for callers. Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/cached_dir.c208
-rw-r--r--fs/smb/client/cached_dir.h12
-rw-r--r--fs/smb/client/inode.c12
-rw-r--r--fs/smb/client/readdir.c4
-rw-r--r--fs/smb/client/smb2inode.c34
-rw-r--r--fs/smb/client/smb2ops.c14
6 files changed, 143 insertions, 141 deletions
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 116470b1dfea..56bafd96cc5e 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -12,65 +12,15 @@
#include "smb2proto.h"
#include "cached_dir.h"
-static struct cached_fid *init_cached_dir(const char *path);
+static struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char *path);
static void free_cached_dir(struct cached_fid *cfid);
static void smb2_close_cached_fid(struct kref *ref);
-static void cfids_laundromat_worker(struct work_struct *work);
struct cached_dir_dentry {
struct list_head entry;
struct dentry *dentry;
};
-static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
- const char *path,
- bool lookup_only,
- __u32 max_cached_dirs)
-{
- struct cached_fid *cfid;
-
- list_for_each_entry(cfid, &cfids->entries, entry) {
- if (!strcmp(cfid->path, path)) {
- /*
- * If it doesn't have a lease it is either not yet
- * fully cached or it may be in the process of
- * being deleted due to a lease break.
- */
- if (!cfid->time || !cfid->has_lease) {
- return NULL;
- }
- kref_get(&cfid->refcount);
- return cfid;
- }
- }
- if (lookup_only) {
- return NULL;
- }
- if (cfids->num_entries >= max_cached_dirs) {
- return NULL;
- }
- cfid = init_cached_dir(path);
- if (cfid == NULL) {
- return NULL;
- }
- cfid->cfids = cfids;
- cfids->num_entries++;
- list_add(&cfid->entry, &cfids->entries);
- cfid->on_list = true;
- kref_get(&cfid->refcount);
- /*
- * Set @cfid->has_lease to true during construction so that the lease
- * reference can be put in cached_dir_lease_break() due to a potential
- * lease break right after the request is sent or while @cfid is still
- * being cached, or if a reconnection is triggered during construction.
- * Concurrent processes won't be to use it yet due to @cfid->time being
- * zero.
- */
- cfid->has_lease = true;
-
- return cfid;
-}
-
static struct dentry *
path_to_dentry(struct cifs_sb_info *cifs_sb, const char *path)
{
@@ -128,13 +78,69 @@ static const char *path_no_prefix(struct cifs_sb_info *cifs_sb,
}
/*
+ * Unlocked lookup of cached dir by @key and @type.
+ * This function does no other validation checks.
+ *
+ * Caller must lock @cfids->cfid_list_lock.
+ *
+ * Return: matching cfid with ref taken, NULL otherwise.
+ */
+static struct cached_fid *__lookup_cached_dir(struct cached_fids *cfids, const void *key, int type)
+{
+ struct cached_fid *cfid, *found = NULL;
+
+ list_for_each_entry(cfid, &cfids->entries, entry) {
+ if (type == CFID_LOOKUP_PATH) {
+ if (!strcmp(cfid->path, key)) {
+ kref_get(&cfid->refcount);
+ found = cfid;
+ break;
+ }
+ } else if (type == CFID_LOOKUP_DENTRY) {
+ if (cfid->dentry == key) {
+ kref_get(&cfid->refcount);
+ found = cfid;
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
+/*
+ * Locked lookup of cached dir by @key and @type.
+ *
+ * Return: valid (has lease/time) cfid with ref taken, NULL otherwise.
+ */
+struct cached_fid *lookup_cached_dir(struct cached_fids *cfids, const void *key, int type)
+{
+ struct cached_fid *cfid;
+
+ spin_lock(&cfids->cfid_list_lock);
+ cfid = __lookup_cached_dir(cfids, key, type);
+
+ /*
+ * If it doesn't have a lease it is either not yet
+ * fully cached or it may be in the process of
+ * being deleted due to a lease break.
+ */
+ if (cfid && (!cfid->time || !cfid->has_lease)) {
+ /* this is the lookup ref */
+ kref_put(&cfid->refcount, smb2_close_cached_fid);
+ cfid = NULL;
+ }
+ spin_unlock(&cfids->cfid_list_lock);
+
+ return cfid;
+}
+
+/*
* Open the and cache a directory handle.
* If error then *cfid is not initialized.
*/
-int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
- const char *path,
- struct cifs_sb_info *cifs_sb,
- bool lookup_only, struct cached_fid **ret_cfid)
+int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, const char *path,
+ struct cifs_sb_info *cifs_sb, struct cached_fid **ret_cfid)
{
struct cifs_ses *ses;
struct TCP_Server_Info *server;
@@ -173,8 +179,24 @@ replay_again:
/* reinitialize for possible replay */
flags = 0;
oplock = SMB2_OPLOCK_LEVEL_II;
- server = cifs_pick_channel(ses);
+ cfid = lookup_cached_dir(cfids, path, CFID_LOOKUP_PATH);
+ /*
+ * Return cached fid if it is valid (has a lease and has a time).
+ * Ref taken on lookup if valid.
+ *
+ * Otherwise, it is either a new entry or laundromat worker removed it
+ * from @cfids->entries. Caller will put last reference if the latter.
+ */
+ if (cfid) {
+ *ret_cfid = cfid;
+ return 0;
+ }
+
+ if (cfids->num_entries >= tcon->max_cached_dirs)
+ return -ENOENT;
+
+ server = cifs_pick_channel(ses);
if (!server->ops->new_lease_key)
return -EIO;
@@ -182,25 +204,11 @@ replay_again:
if (!utf16_path)
return -ENOMEM;
- spin_lock(&cfids->cfid_list_lock);
- cfid = find_or_create_cached_dir(cfids, path, lookup_only, tcon->max_cached_dirs);
- if (cfid == NULL) {
- spin_unlock(&cfids->cfid_list_lock);
+ cfid = init_cached_dir(cfids, path);
+ if (!cfid) {
kfree(utf16_path);
- return -ENOENT;
- }
- /*
- * Return cached fid if it is valid (has a lease and has a time).
- * Otherwise, it is either a new entry or laundromat worker removed it
- * from @cfids->entries. Caller will put last reference if the latter.
- */
- if (cfid->has_lease && cfid->time) {
- spin_unlock(&cfids->cfid_list_lock);
- *ret_cfid = cfid;
- kfree(utf16_path);
- return 0;
+ return -ENOMEM;
}
- spin_unlock(&cfids->cfid_list_lock);
pfid = &cfid->fid;
@@ -405,30 +413,6 @@ out:
return rc;
}
-int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
- struct dentry *dentry,
- struct cached_fid **ret_cfid)
-{
- struct cached_fid *cfid;
- struct cached_fids *cfids = tcon->cfids;
-
- if (cfids == NULL)
- return -EOPNOTSUPP;
-
- spin_lock(&cfids->cfid_list_lock);
- list_for_each_entry(cfid, &cfids->entries, entry) {
- if (dentry && cfid->dentry == dentry) {
- cifs_dbg(FYI, "found a cached file handle by dentry\n");
- kref_get(&cfid->refcount);
- *ret_cfid = cfid;
- spin_unlock(&cfids->cfid_list_lock);
- return 0;
- }
- }
- spin_unlock(&cfids->cfid_list_lock);
- return -ENOENT;
-}
-
static void
smb2_close_cached_fid(struct kref *ref)
{
@@ -461,19 +445,20 @@ void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb)
{
struct cached_fid *cfid = NULL;
- int rc;
- rc = open_cached_dir(xid, tcon, name, cifs_sb, true, &cfid);
- if (rc) {
+ spin_lock(&tcon->cfids->cfid_list_lock);
+ cfid = __lookup_cached_dir(tcon->cfids, name, CFID_LOOKUP_PATH);
+ if (!cfid) {
+ spin_unlock(&tcon->cfids->cfid_list_lock);
cifs_dbg(FYI, "no cached dir found for rmdir(%s)\n", name);
return;
}
- spin_lock(&cfid->cfids->cfid_list_lock);
+
if (cfid->has_lease) {
cfid->has_lease = false;
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
- spin_unlock(&cfid->cfids->cfid_list_lock);
+ spin_unlock(&tcon->cfids->cfid_list_lock);
close_cached_dir(cfid);
}
@@ -653,7 +638,7 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
return false;
}
-static struct cached_fid *init_cached_dir(const char *path)
+static struct cached_fid *init_cached_dir(struct cached_fids *cfids, const char *path)
{
struct cached_fid *cfid;
@@ -673,6 +658,23 @@ static struct cached_fid *init_cached_dir(const char *path)
mutex_init(&cfid->dirents.de_mutex);
spin_lock_init(&cfid->fid_lock);
kref_init(&cfid->refcount);
+
+ cfid->cfids = cfids;
+ cfids->num_entries++;
+ list_add(&cfid->entry, &cfids->entries);
+ cfid->on_list = true;
+ kref_get(&cfid->refcount);
+
+ /*
+ * Set @cfid->has_lease to true during construction so that the lease
+ * reference can be put in cached_dir_lease_break() due to a potential
+ * lease break right after the request is sent or while @cfid is still
+ * being cached, or if a reconnection is triggered during construction.
+ * Concurrent processes won't be to use it yet due to @cfid->time being
+ * zero.
+ */
+ cfid->has_lease = true;
+
return cfid;
}
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index 070b0962de98..fd314e7a18ee 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -64,15 +64,19 @@ struct cached_fids {
struct delayed_work laundromat_work;
};
+enum {
+ CFID_LOOKUP_PATH,
+ CFID_LOOKUP_DENTRY,
+};
+
extern struct cached_fids *init_cached_dirs(void);
extern void free_cached_dirs(struct cached_fids *cfids);
+extern struct cached_fid *lookup_cached_dir(struct cached_fids *cfids,
+ const void *key, int type);
extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
const char *path,
struct cifs_sb_info *cifs_sb,
- bool lookup_only, struct cached_fid **cfid);
-extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
- struct dentry *dentry,
- struct cached_fid **cfid);
+ struct cached_fid **cfid);
extern void close_cached_dir(struct cached_fid *cfid);
extern void drop_cached_dir_by_name(const unsigned int xid,
struct cifs_tcon *tcon,
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 75be4b46bc6f..d8728da54512 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -2608,12 +2608,14 @@ cifs_dentry_needs_reval(struct dentry *dentry)
if (!lookupCacheEnabled)
return true;
- if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) {
- if (cfid->time && cifs_i->time > cfid->time) {
- close_cached_dir(cfid);
- return false;
- }
+ cfid = lookup_cached_dir(tcon->cfids, dentry->d_parent, CFID_LOOKUP_DENTRY);
+ if (cfid) {
+ bool valid = (cifs_i->time > cfid->time);
+
close_cached_dir(cfid);
+
+ if (valid)
+ return false;
}
/*
* depending on inode type, check if attribute caching disabled for
diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c
index 4e5460206397..cc6762d950d2 100644
--- a/fs/smb/client/readdir.c
+++ b/fs/smb/client/readdir.c
@@ -1065,7 +1065,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
tcon = tlink_tcon(cifsFile->tlink);
}
- rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
+ rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
cifs_put_tlink(tlink);
if (rc)
goto cache_not_found;
@@ -1136,7 +1136,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
tcon = tlink_tcon(cifsFile->tlink);
rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path,
&current_entry, &num_to_fill);
- open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
+ open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
if (rc) {
cifs_dbg(FYI, "fce error %d\n", rc);
goto rddir2_exit;
diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c
index 2a3e46b8e15a..f5fd9fbcc096 100644
--- a/fs/smb/client/smb2inode.c
+++ b/fs/smb/client/smb2inode.c
@@ -960,25 +960,23 @@ int smb2_query_path_info(const unsigned int xid,
* is fast enough (always using the compounded version).
*/
if (!tcon->posix_extensions) {
- if (*full_path) {
- rc = -ENOENT;
- } else {
- rc = open_cached_dir(xid, tcon, full_path,
- cifs_sb, false, &cfid);
- }
- /* If it is a root and its handle is cached then use it */
- if (!rc) {
- if (cfid->file_all_info_is_valid) {
- memcpy(&data->fi, &cfid->file_all_info,
- sizeof(data->fi));
- } else {
- rc = SMB2_query_info(xid, tcon,
- cfid->fid.persistent_fid,
- cfid->fid.volatile_fid,
- &data->fi);
+ if (!*full_path) {
+ rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+
+ /* If it is a root and its handle is cached then use it */
+ if (!rc) {
+ if (cfid->file_all_info_is_valid) {
+ memcpy(&data->fi, &cfid->file_all_info,
+ sizeof(data->fi));
+ } else {
+ rc = SMB2_query_info(xid, tcon,
+ cfid->fid.persistent_fid,
+ cfid->fid.volatile_fid,
+ &data->fi);
+ }
+ close_cached_dir(cfid);
+ return rc;
}
- close_cached_dir(cfid);
- return rc;
}
cmds[num_cmds++] = SMB2_OP_QUERY_INFO;
} else {
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 1468c16ea9b8..02543b0d4473 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -873,7 +873,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
.fid = &fid,
};
- rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid);
+ rc = open_cached_dir(xid, tcon, "", cifs_sb, &cfid);
if (rc == 0)
memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid));
else
@@ -943,13 +943,10 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
bool islink;
int rc, rc2;
- rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
- if (!rc) {
- if (cfid->has_lease) {
- close_cached_dir(cfid);
- return 0;
- }
+ cfid = lookup_cached_dir(tcon->cfids, full_path, CFID_LOOKUP_PATH);
+ if (cfid) {
close_cached_dir(cfid);
+ return 0;
}
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
@@ -2716,8 +2713,7 @@ replay_again:
* We can only call this for things we know are directories.
*/
if (!strcmp(path, ""))
- open_cached_dir(xid, tcon, path, cifs_sb, false,
- &cfid); /* cfid null if open dir failed */
+ open_cached_dir(xid, tcon, path, cifs_sb, &cfid); /* cfid null if open dir failed */
rqst[0].rq_iov = vars->open_iov;
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;