diff options
| author | Enzo Matsumiya <ematsumiya@suse.de> | 2025-07-03 12:00:10 -0300 |
|---|---|---|
| committer | Enzo Matsumiya <ematsumiya@suse.de> | 2025-07-03 12:37:15 -0300 |
| commit | 33ef9fe668688c3c390c0048dce382b846bc22c5 (patch) | |
| tree | c9fc0f918f357e0da21a93379c7d26a83068c5b5 | |
| parent | 89fa862ca44a88bf70a03f82647f6ed1e09bd7ac (diff) | |
| download | linux-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.c | 208 | ||||
| -rw-r--r-- | fs/smb/client/cached_dir.h | 12 | ||||
| -rw-r--r-- | fs/smb/client/inode.c | 12 | ||||
| -rw-r--r-- | fs/smb/client/readdir.c | 4 | ||||
| -rw-r--r-- | fs/smb/client/smb2inode.c | 34 | ||||
| -rw-r--r-- | fs/smb/client/smb2ops.c | 14 |
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, ¤t_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; |
