summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGil Portnoy <dddhkts1@gmail.com>2026-06-26 22:11:14 +0300
committerSteve French <stfrench@microsoft.com>2026-06-30 21:29:46 -0500
commitc706195e5e06402d8d1d20908978cdc82eae6185 (patch)
treea3a8821527092316196edf8fc43a2fbf446f1053
parent38637163501fd9e2f684b8cd275d0db5d79f37c6 (diff)
ksmbd: close superseded durable handles through refcount handoff
ksmbd_close_disconnected_durable_delete_on_close() collects disconnected durable handles for a name being superseded by a new delete-on-close open, drops ci->m_lock, then closes each collected handle directly with __ksmbd_close_fd(). That bypasses the FP_CLOSED and refcount handoff used by the other close paths. If a durable reconnect or the durable scavenger already took a reference to the same fp, the direct __ksmbd_close_fd() can free the ksmbd_file while that other holder still owns a live reference. Claim the disconnected durable handle before unlinking it from m_fp_list. While holding ci->m_lock and global_ft.lock, only take ownership when the durable lifetime reference is the only remaining reference. Then take a transient reference, remove the fp from global_ft, mark it FP_CLOSED, and move it to the local dispose list. If another holder already has a reference, leave the fp linked and let that holder complete its path. The dispose loop then drops both references owned by the claim. This keeps the force-close path in the same refcount handoff model as the durable scavenger and avoids leaving a live reconnected fp detached from m_fp_list. Fixes: 166e4c07023b ("ksmbd: supersede disconnected delete-on-close durable handle") Signed-off-by: Gil Portnoy <dddhkts1@gmail.com> Co-developed-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/smb/server/vfs_cache.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 3c9443ac3522..73d28942dc0a 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -581,7 +581,20 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry)
if (fp->conn || !fp->is_durable ||
fp->f_state != FP_INITED)
continue;
- list_move_tail(&fp->node, &dispose);
+
+ /*
+ * Claim the close before unlinking fp from m_fp_list.
+ * refcount == 1 means only the durable lifetime ref is
+ * left. Add a transient ref so final close can drop both.
+ */
+ write_lock(&global_ft.lock);
+ if (atomic_read(&fp->refcount) == 1) {
+ atomic_inc(&fp->refcount);
+ __ksmbd_remove_durable_fd(fp);
+ ksmbd_mark_fp_closed(fp);
+ list_move_tail(&fp->node, &dispose);
+ }
+ write_unlock(&global_ft.lock);
}
}
up_write(&ci->m_lock);
@@ -589,16 +602,18 @@ bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry)
/*
* Drop our lookup reference before closing so the last __ksmbd_close_fd()
* can drop m_count to zero and unlink the delete-on-close file. The
- * collected handles still hold references, so ci stays valid until they
- * are closed below.
+ * collected handles still hold the transient reference taken above, so
+ * ci stays valid until they are closed below.
*/
ksmbd_inode_put(ci);
while (!list_empty(&dispose)) {
fp = list_first_entry(&dispose, struct ksmbd_file, node);
list_del_init(&fp->node);
- __ksmbd_close_fd(NULL, fp);
- closed = true;
+ if (atomic_sub_and_test(2, &fp->refcount)) {
+ __ksmbd_close_fd(NULL, fp);
+ closed = true;
+ }
}
return closed;