[RFC PATCH 05/11] smb/server: keep notify watches on file handles

ChenXiaoSong <[email protected]> Thu, 23 Jul 2026 03:16:33 +0000
Newsgroups org.kernel.vger.linux-cifs
Message-ID <[email protected]>
From: ChenXiaoSong <[email protected]>

Keep one notify watch on each file handle. Reuse it for later requests,
and remove it when the handle is closed.

Signed-off-by: ChenXiaoSong <[email protected]>
---
 fs/smb/server/notify.c    | 47 +++++++++++++++++++++++++++++++++++----
 fs/smb/server/notify.h    |  2 ++
 fs/smb/server/vfs_cache.c |  3 +++
 fs/smb/server/vfs_cache.h |  4 ++++
 4 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/fs/smb/server/notify.c b/fs/smb/server/notify.c
index 1c020752a8ed..82de53963dc0 100644
--- a/fs/smb/server/notify.c
+++ b/fs/smb/server/notify.c
@@ -182,6 +182,21 @@ static int ksmbd_notify_add(struct ksmbd_file *fp, u32 mask, u32 filter,
 	struct fsnotify_mark *mark;
 	int err = 0;
 
+	mutex_lock(&fp->notify_lock);
+	if (fp->notify) {
+		spin_lock(&fp->notify->lock);
+		fp->notify->filter |= filter;
+		fp->notify->mask |= mask;
+		spin_unlock(&fp->notify->lock);
+		fsnotify_modify_mark_mask(fp->notify->mark, mask, 0);
+		ksmbd_debug(NOTIFY,
+			    "Updated fsnotify mark, inode %llu, mask 0x%x\n",
+			    (unsigned long long)file_inode(fp->filp)->i_ino,
+			    fp->notify->mark->mask);
+		*notify_out = fp->notify;
+		goto out;
+	}
+
 	notify = kzalloc_obj(*notify, KSMBD_DEFAULT_GFP);
 	if (!notify) {
 		pr_err("Failed to allocate notify watch\n");
@@ -203,6 +218,7 @@ static int ksmbd_notify_add(struct ksmbd_file *fp, u32 mask, u32 filter,
 	}
 
 	notify->mark = mark;
+	fp->notify = notify;
 	*notify_out = notify;
 	ksmbd_debug(NOTIFY,
 		    "Added fsnotify mark, inode %llu, mask 0x%x, filter 0x%x, "
@@ -211,9 +227,36 @@ static int ksmbd_notify_add(struct ksmbd_file *fp, u32 mask, u32 filter,
 		    filter, max_buffer_size);
 
 out:
+	mutex_unlock(&fp->notify_lock);
 	return err;
 }
 
+/**
+ * ksmbd_notify_remove() - remove the notify watch for a closing handle
+ * @fp: file handle whose watch is being removed
+ *
+ * A cancelled CHANGE_NOTIFY request leaves this watch installed. The watch is
+ * owned by @fp and removed only when the file handle is finally closed.
+ */
+void ksmbd_notify_remove(struct ksmbd_file *fp)
+{
+	struct ksmbd_notify *notify;
+
+	mutex_lock(&fp->notify_lock);
+	notify = fp->notify;
+	fp->notify = NULL;
+	mutex_unlock(&fp->notify_lock);
+	if (!notify)
+		return;
+
+	ksmbd_debug(NOTIFY,
+		    "Removing fsnotify mark, inode %llu, mask 0x%x\n",
+		    (unsigned long long)file_inode(fp->filp)->i_ino,
+		    notify->mark->mask);
+	ksmbd_notify_destroy_mark(notify->group, notify->mark);
+	kfree(notify);
+}
+
 static struct ksmbd_file *
 ksmbd_notify_validate_req(struct ksmbd_work *work,
 			  struct smb2_change_notify_req *req,
@@ -417,10 +460,6 @@ int ksmbd_handle_notify(struct ksmbd_work *work,
 		kfree(argv);
 	if (notify_req)
 		kfree(notify_req);
-	if (notify) {
-		ksmbd_notify_destroy_mark(notify->group, notify->mark);
-		kfree(notify);
-	}
 	if (fp)
 		ksmbd_fd_put(work, fp);
 	return err;
diff --git a/fs/smb/server/notify.h b/fs/smb/server/notify.h
index 3118ad09c841..9d022ba9090f 100644
--- a/fs/smb/server/notify.h
+++ b/fs/smb/server/notify.h
@@ -12,11 +12,13 @@
 #define __SMB_SERVER_NOTIFY_H__
 
 struct ksmbd_work;
+struct ksmbd_file;
 struct smb2_change_notify_req;
 struct smb2_change_notify_rsp;
 
 int ksmbd_handle_notify(struct ksmbd_work *work,
 			struct smb2_change_notify_req *req,
 			struct smb2_change_notify_rsp *rsp);
+void ksmbd_notify_remove(struct ksmbd_file *fp);
 
 #endif /* __SMB_SERVER_NOTIFY_H__ */
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index d0fd954e9ec8..64bef7ce5fd2 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -18,6 +18,7 @@
 #include "vfs.h"
 #include "connection.h"
 #include "misc.h"
+#include "notify.h"
 #include "mgmt/tree_connect.h"
 #include "mgmt/user_session.h"
 #include "mgmt/user_config.h"
@@ -625,6 +626,7 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
 	close_id_del_oplock(fp);
 	filp = fp->filp;
 
+	ksmbd_notify_remove(fp);
 	__ksmbd_inode_close(fp);
 	if (!IS_ERR_OR_NULL(filp))
 		fput(filp);
@@ -1175,6 +1177,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
 	INIT_LIST_HEAD(&fp->node);
 	INIT_LIST_HEAD(&fp->lock_list);
 	spin_lock_init(&fp->f_lock);
+	mutex_init(&fp->notify_lock);
 	mutex_init(&fp->readdir_lock);
 	atomic_set(&fp->refcount, 1);
 
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index 11bfbb981004..aff7db9127e0 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -33,6 +33,7 @@
 #define SMB2_NO_FID		(0xFFFFFFFFFFFFFFFFULL)
 
 struct ksmbd_conn;
+struct ksmbd_notify;
 struct ksmbd_session;
 
 struct ksmbd_lock {
@@ -96,6 +97,9 @@ struct ksmbd_file {
 	u64				durable_volatile_id;
 
 	spinlock_t			f_lock;
+	/* Protects notify watch creation and removal. */
+	struct mutex			notify_lock;
+	struct ksmbd_notify		*notify;
 
 	struct ksmbd_inode		*f_ci;
 	struct ksmbd_inode		*f_parent_ci;
-- 
2.54.0