summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnzo Matsumiya <ematsumiya@suse.de>2025-09-08 10:09:32 -0300
committerEnzo Matsumiya <ematsumiya@suse.de>2025-09-08 10:09:32 -0300
commit3f134bd4192d4b5285b254167b392d67bf89e7b5 (patch)
tree1ca87737d34eb2a13084c993413686a853bae9d9
parent3c670b5d27ff05fffe4b2ea764c75ca37b191a8c (diff)
downloadlinux-3f134bd4192d4b5285b254167b392d67bf89e7b5.tar.gz
linux-3f134bd4192d4b5285b254167b392d67bf89e7b5.tar.bz2
linux-3f134bd4192d4b5285b254167b392d67bf89e7b5.zip
smb: client: remove cached_dir_offload_close/close_work
Make put_work an 'async dput' and then move cfid to dying list so laundromat can clean it up (put last ref). Other changes: - move SMB2_close() from release callback to laundromat Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
-rw-r--r--fs/smb/client/cached_dir.c95
-rw-r--r--fs/smb/client/cached_dir.h1
2 files changed, 41 insertions, 55 deletions
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index f61fef810a23..7c992141576c 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -434,9 +434,7 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
static void
smb2_close_cached_fid(struct kref *ref)
{
- struct cached_fid *cfid = container_of(ref, struct cached_fid,
- refcount);
- int rc;
+ struct cached_fid *cfid = container_of(ref, struct cached_fid, refcount);
spin_lock(&cfid->cfids->cfid_list_lock);
if (cfid->on_list) {
@@ -449,13 +447,6 @@ smb2_close_cached_fid(struct kref *ref)
dput(cfid->dentry);
cfid->dentry = NULL;
- if (cfid->is_open) {
- rc = SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
- cfid->fid.volatile_fid);
- if (rc) /* should we retry on -EBUSY or -EAGAIN? */
- cifs_dbg(VFS, "close cached dir rc %d\n", rc);
- }
-
free_cached_dir(cfid);
}
@@ -530,6 +521,9 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
list_add_tail(&tmp_list->entry, &entry);
}
spin_unlock(&cfids->cfid_list_lock);
+
+ /* run laundromat now as it might not have been queued */
+ mod_delayed_work(cfid_put_wq, &cfids->laundromat_work, 0);
}
spin_unlock(&cifs_sb->tlink_tree_lock);
@@ -583,30 +577,15 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
flush_delayed_work(&cfids->laundromat_work);
}
-static void
-cached_dir_offload_close(struct work_struct *work)
-{
- struct cached_fid *cfid = container_of(work,
- struct cached_fid, close_work);
- struct cifs_tcon *tcon = cfid->tcon;
-
- WARN_ON(cfid->on_list);
-
- kref_put(&cfid->refcount, smb2_close_cached_fid);
- cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cached_close);
-}
-
/*
- * Release the cached directory's dentry, and then queue work to drop cached
- * directory itself (closing on server if needed).
- *
- * Must be called with a reference to the cached_fid and a reference to the
- * tcon.
+ * Release the cached directory's dentry and schedule immediate cleanup on laundromat.
+ * Must be called with a reference to the cached_fid and a reference to the tcon.
*/
static void cached_dir_put_work(struct work_struct *work)
{
- struct cached_fid *cfid = container_of(work, struct cached_fid,
- put_work);
+ struct cached_fid *cfid = container_of(work, struct cached_fid, put_work);
+ struct cached_fids *cfids = cfid->cfids;
+ struct cifs_tcon *tcon = cfid->tcon;
struct dentry *dentry;
spin_lock(&cfid->fid_lock);
@@ -615,7 +594,16 @@ static void cached_dir_put_work(struct work_struct *work)
spin_unlock(&cfid->fid_lock);
dput(dentry);
- queue_work(serverclose_wq, &cfid->close_work);
+
+ /* move to dying list so laundromat can clean it up */
+ spin_lock(&cfids->cfid_list_lock);
+ list_move(&cfid->entry, &cfids->dying);
+ cfid->on_list = false;
+ cfids->num_entries--;
+ spin_unlock(&cfids->cfid_list_lock);
+
+ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cached_close);
+ mod_delayed_work(cfid_put_wq, &cfids->laundromat_work, 0);
}
bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
@@ -634,13 +622,6 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
SMB2_LEASE_KEY_SIZE)) {
cfid->has_lease = false;
cfid->time = 0;
- /*
- * We found a lease remove it from the list
- * so no threads can access it.
- */
- list_del(&cfid->entry);
- cfid->on_list = false;
- cfids->num_entries--;
++tcon->tc_count;
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
@@ -667,7 +648,6 @@ static struct cached_fid *init_cached_dir(const char *path)
return NULL;
}
- INIT_WORK(&cfid->close_work, cached_dir_offload_close);
INIT_WORK(&cfid->put_work, cached_dir_put_work);
INIT_LIST_HEAD(&cfid->entry);
INIT_LIST_HEAD(&cfid->dirents.entries);
@@ -681,7 +661,6 @@ static void free_cached_dir(struct cached_fid *cfid)
{
struct cached_dirent *dirent, *q;
- WARN_ON(work_pending(&cfid->close_work));
WARN_ON(work_pending(&cfid->put_work));
dput(cfid->dentry);
@@ -735,28 +714,36 @@ static void cfids_laundromat_worker(struct work_struct *work)
list_for_each_entry_safe(cfid, q, &entry, entry) {
list_del(&cfid->entry);
+ /*
+ * If a cfid reached here, we must cleanup anything unrelated to it, i.e. dentry and
+ * remote fid.
+ *
+ * For the cfid itself, we only drop only our own (kref_init). If there are
+ * still concurrent ref-holders, they'll drop it later (cfid is already invalid at
+ * this point, so can't be found anymore).
+ */
spin_lock(&cfid->fid_lock);
dentry = cfid->dentry;
cfid->dentry = NULL;
spin_unlock(&cfid->fid_lock);
dput(dentry);
+
if (cfid->is_open) {
- spin_lock(&cifs_tcp_ses_lock);
- ++cfid->tcon->tc_count;
- trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count,
- netfs_trace_tcon_ref_get_cached_laundromat);
- spin_unlock(&cifs_tcp_ses_lock);
- queue_work(serverclose_wq, &cfid->close_work);
- } else
- /*
- * Drop the ref-count from above, either the lease-ref (if there
- * was one) or the extra one acquired.
- */
- kref_put(&cfid->refcount, smb2_close_cached_fid);
+ int rc = SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
+ cfid->fid.volatile_fid);
+
+ cfid->is_open = false;
+
+ /* SMB2_close should handle -EBUSY or -EAGAIN */
+ if (rc)
+ cifs_dbg(VFS, "close cached dir rc %d\n", rc);
+ }
+
+ kref_put(&cfid->refcount, smb2_close_cached_fid);
}
- queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
- dir_cache_timeout * HZ);
+
+ queue_delayed_work(cfid_put_wq, &cfids->laundromat_work, dir_cache_timeout * HZ);
}
struct cached_fids *init_cached_dirs(void)
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index a3757a736d3e..e5445e3a7bd3 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -45,7 +45,6 @@ struct cached_fid {
struct cifs_tcon *tcon;
struct dentry *dentry;
struct work_struct put_work;
- struct work_struct close_work;
struct smb2_file_all_info file_all_info;
struct cached_dirents dirents;
};