From: ChenXiaoSong <chenxiaosong@chenxiaosong.com>
To: linkinjeon@kernel.org, tom@talpey.com, senozhatsky@chromium.org,
chenxiaosong@chenxiaosong.com
Cc: linux-cifs@vger.kernel.org, ChenXiaoSong <chenxiaosong@kylinos.cn>
Subject: [PATCH 01/12] smb/server: move change notify handling into notify.c
Date: Sat, 26 Sep 2026 09:05:07 +0000 [thread overview]
Message-ID: <20260926090518.78547-2-chenxiaosong@chenxiaosong.com> (raw)
In-Reply-To: <20260926090518.78547-1-chenxiaosong@chenxiaosong.com>
From: ChenXiaoSong <chenxiaosong@kylinos.cn>
Move the SMB2 CHANGE_NOTIFY implementation out of smb2pdu.c into a
dedicated notify.c file.
Factor out two helper functions ksmbd_notify_validate_req() and
ksmbd_notify_wait().
No functional change.
Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
fs/smb/server/Makefile | 3 +-
fs/smb/server/notify.c | 183 ++++++++++++++++++++++++++++++++++++++++
fs/smb/server/notify.h | 24 ++++++
fs/smb/server/smb2pdu.c | 110 +-----------------------
4 files changed, 213 insertions(+), 107 deletions(-)
create mode 100644 fs/smb/server/notify.c
create mode 100644 fs/smb/server/notify.h
diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile
index 9bc87695a53c..5daae1727b32 100644
--- a/fs/smb/server/Makefile
+++ b/fs/smb/server/Makefile
@@ -10,7 +10,8 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \
smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
- ksmbd_spnego_negtokentarg.asn1.o asn1.o compress.o
+ ksmbd_spnego_negtokentarg.asn1.o asn1.o compress.o \
+ notify.o
$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h
diff --git a/fs/smb/server/notify.c b/fs/smb/server/notify.c
new file mode 100644
index 000000000000..7e324af36b47
--- /dev/null
+++ b/fs/smb/server/notify.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * SMB2 CHANGE_NOTIFY
+ *
+ * Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+ *
+ * Author(s): ChenXiaoSong <chenxiaosong@kylinos.cn>
+ * Gael Blivet <gael.blivet@gmail.com>
+ *
+ */
+
+#include "glob.h"
+#include "../common/smb2status.h"
+#include "connection.h"
+#include "ksmbd_work.h"
+#include "notify.h"
+#include "smb_common.h"
+#include "smb2pdu.h"
+#include "vfs_cache.h"
+#include "mgmt/user_session.h"
+
+struct ksmbd_notify_req {
+ wait_queue_head_t wait;
+};
+
+/*
+ * Cancel handler for a pending CHANGE_NOTIFY. Called either by
+ * smb2_cancel() (conn->request_lock held, work->state already set to
+ * KSMBD_WORK_CANCELLED by the caller) or by
+ * set_close_state_blocked_works() (vfs_cache.c, fp->f_lock held,
+ * work->state already set to KSMBD_WORK_CLOSED by the caller) -- both
+ * callers hold a spinlock across this call, so it must not sleep.
+ * wake_up() only wakes the waiter in smb2_notify(); it does not touch
+ * fp->blocked_works itself, matching smb2_remove_blocked_lock()'s same
+ * non-mutating style for the equivalent byte-range-lock wait.
+ */
+static void smb2_notify_cancel(void **argv)
+{
+ struct ksmbd_notify_req *notify_req = argv[0];
+
+ wake_up(¬ify_req->wait);
+}
+
+static struct ksmbd_file *
+ksmbd_notify_validate_req(struct ksmbd_work *work,
+ struct smb2_change_notify_req *req,
+ struct smb2_change_notify_rsp *rsp)
+{
+ struct ksmbd_file *fp;
+
+ if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) {
+ pr_err("Notify request is not the last compound command\n");
+ rsp->hdr.Status = STATUS_INTERNAL_ERROR;
+ return ERR_PTR(-EIO);
+ }
+
+ fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId,
+ req->PersistentFileId);
+ if (!fp) {
+ pr_err("Invalid file id for notify, fid %llu:%llu\n",
+ le64_to_cpu(req->PersistentFileId),
+ le64_to_cpu(req->VolatileFileId));
+ rsp->hdr.Status = STATUS_FILE_CLOSED;
+ return ERR_PTR(-ENOENT);
+ }
+
+ return fp;
+}
+
+static int ksmbd_notify_wait(struct ksmbd_work *work,
+ struct ksmbd_file *fp,
+ struct ksmbd_notify_req *notify_req)
+{
+ int err;
+
+ /*
+ * Handle close holds the file-table write lock while it marks the
+ * handle closed and walks blocked_works. Hold the matching read lock
+ * across the state check and registration so close cannot finish its
+ * walk between the lookup above and this list insertion.
+ */
+ read_lock(&work->sess->file_table.lock);
+ if (fp->f_state != FP_INITED) {
+ read_unlock(&work->sess->file_table.lock);
+ return -ENOENT;
+ }
+ spin_lock(&fp->f_lock);
+ list_add_tail(&work->fp_entry, &fp->blocked_works);
+ spin_unlock(&fp->f_lock);
+ read_unlock(&work->sess->file_table.lock);
+
+ smb2_send_interim_resp(work, STATUS_PENDING);
+
+ err = wait_event_interruptible(notify_req->wait,
+ READ_ONCE(work->state) !=
+ KSMBD_WORK_ACTIVE);
+ if (err && READ_ONCE(work->state) == KSMBD_WORK_ACTIVE) {
+ pr_err("Notify wait interrupted, async id %d: %d\n",
+ work->async_id, err);
+ /*
+ * Woken by a signal, not a real cancel/close. There is no
+ * notification backend yet to report anything else against,
+ * so treat this the same as a client-side cancel.
+ */
+ WRITE_ONCE(work->state, KSMBD_WORK_CANCELLED);
+ }
+
+ spin_lock(&fp->f_lock);
+ list_del_init(&work->fp_entry);
+ spin_unlock(&fp->f_lock);
+
+ return err;
+}
+
+/**
+ * ksmbd_handle_notify() - handle an SMB2 change notify request
+ * @work: smb work containing notify command buffer
+ * @req: SMB2 change notify request
+ * @rsp: SMB2 change notify response
+ *
+ * Return: 0 on success, otherwise error
+ */
+int ksmbd_handle_notify(struct ksmbd_work *work,
+ struct smb2_change_notify_req *req,
+ struct smb2_change_notify_rsp *rsp)
+{
+ struct ksmbd_notify_req notify_req = {};
+ struct ksmbd_file *fp = NULL;
+ void **argv = NULL;
+ bool async_work = false;
+ int err = 0;
+
+ fp = ksmbd_notify_validate_req(work, req, rsp);
+ if (IS_ERR(fp)) {
+ err = PTR_ERR(fp);
+ fp = NULL;
+ goto out;
+ }
+
+ argv = kmalloc_obj(*argv, KSMBD_DEFAULT_GFP);
+ if (!argv) {
+ pr_err("Failed to allocate notify cancel arguments\n");
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+ err = -ENOMEM;
+ goto out;
+ }
+ init_waitqueue_head(¬ify_req.wait);
+ argv[0] = ¬ify_req;
+
+ err = setup_async_work(work, smb2_notify_cancel, argv);
+ if (err) {
+ pr_err("Failed to set up asynchronous notify work: %d\n", err);
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto out;
+ }
+ async_work = true;
+
+ err = ksmbd_notify_wait(work, fp, ¬ify_req);
+ if (err == -ENOENT) {
+ rsp->hdr.Status = STATUS_NOTIFY_CLEANUP;
+ goto out;
+ }
+
+ if (work->state == KSMBD_WORK_CLOSED) {
+ rsp->hdr.Status = STATUS_NOTIFY_CLEANUP;
+ } else {
+ rsp->hdr.Status = STATUS_CANCELLED;
+ }
+ smb2_send_interim_resp(work, rsp->hdr.Status);
+ work->send_no_response = 1;
+
+out:
+ if (rsp->hdr.Status != STATUS_SUCCESS && !work->send_no_response)
+ smb2_set_err_rsp(work);
+ if (async_work)
+ release_async_work(work);
+ else
+ kfree(argv);
+ if (fp)
+ ksmbd_fd_put(work, fp);
+ return err;
+}
diff --git a/fs/smb/server/notify.h b/fs/smb/server/notify.h
new file mode 100644
index 000000000000..8de46e07b02e
--- /dev/null
+++ b/fs/smb/server/notify.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ * SMB2 CHANGE_NOTIFY
+ *
+ * Copyright (C) 2026 KylinSoft Co., Ltd. All rights reserved.
+ *
+ * Author(s): ChenXiaoSong <chenxiaosong@kylinos.cn>
+ * Gael Blivet <gael.blivet@gmail.com>
+ *
+ */
+
+#ifndef __SMB_SERVER_NOTIFY_H__
+#define __SMB_SERVER_NOTIFY_H__
+
+struct ksmbd_work;
+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);
+
+#endif /* __SMB_SERVER_NOTIFY_H__ */
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 7b9080508a3f..1f45a0836fe9 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -33,6 +33,7 @@
#include "vfs.h"
#include "vfs_cache.h"
#include "misc.h"
+#include "notify.h"
#include "server.h"
#include "smb_common.h"
@@ -11818,28 +11819,6 @@ int smb2_oplock_break(struct ksmbd_work *work)
return 0;
}
-struct ksmbd_notify_req {
- wait_queue_head_t wait;
-};
-
-/*
- * Cancel handler for a pending CHANGE_NOTIFY. Called either by
- * smb2_cancel() (conn->request_lock held, work->state already set to
- * KSMBD_WORK_CANCELLED by the caller) or by
- * set_close_state_blocked_works() (vfs_cache.c, fp->f_lock held,
- * work->state already set to KSMBD_WORK_CLOSED by the caller) -- both
- * callers hold a spinlock across this call, so it must not sleep.
- * wake_up() only wakes the waiter in smb2_notify(); it does not touch
- * fp->blocked_works itself, matching smb2_remove_blocked_lock()'s same
- * non-mutating style for the equivalent byte-range-lock wait.
- */
-static void smb2_notify_cancel(void **argv)
-{
- struct ksmbd_notify_req *notify_req = argv[0];
-
- wake_up(¬ify_req->wait);
-}
-
/**
* smb2_notify() - handler for smb2 notify request
* @work: smb work containing notify command buffer
@@ -11850,98 +11829,17 @@ int smb2_notify(struct ksmbd_work *work)
{
struct smb2_change_notify_req *req;
struct smb2_change_notify_rsp *rsp;
- struct ksmbd_notify_req notify_req;
- struct ksmbd_file *fp = NULL;
- void **argv = NULL;
- bool async_work = false;
- int err = 0;
ksmbd_debug(SMB, "Received smb2 notify\n");
WORK_BUFFERS(work, req, rsp);
- if (smb2_compound_has_failed(work, &rsp->hdr))
+ if (smb2_compound_has_failed(work, &rsp->hdr)) {
+ pr_err("Failed compound notify request\n");
return -EACCES;
-
- if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) {
- rsp->hdr.Status = STATUS_INTERNAL_ERROR;
- err = -EIO;
- goto out;
- }
-
- fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId);
- if (!fp) {
- rsp->hdr.Status = STATUS_FILE_CLOSED;
- err = -ENOENT;
- goto out;
- }
-
- argv = kmalloc(sizeof(void *), KSMBD_DEFAULT_GFP);
- if (!argv) {
- rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
- err = -ENOMEM;
- goto out;
- }
- init_waitqueue_head(¬ify_req.wait);
- argv[0] = ¬ify_req;
-
- err = setup_async_work(work, smb2_notify_cancel, argv);
- if (err) {
- rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
- goto out;
- }
- async_work = true;
-
- /*
- * Handle close holds the file-table write lock while it marks the
- * handle closed and walks blocked_works. Hold the matching read lock
- * across the state check and registration so close cannot finish its
- * walk between the lookup above and this list insertion.
- */
- read_lock(&work->sess->file_table.lock);
- if (fp->f_state != FP_INITED) {
- read_unlock(&work->sess->file_table.lock);
- rsp->hdr.Status = STATUS_NOTIFY_CLEANUP;
- err = -ENOENT;
- goto out;
}
- spin_lock(&fp->f_lock);
- list_add_tail(&work->fp_entry, &fp->blocked_works);
- spin_unlock(&fp->f_lock);
- read_unlock(&work->sess->file_table.lock);
- smb2_send_interim_resp(work, STATUS_PENDING);
-
- err = wait_event_interruptible(notify_req.wait,
- READ_ONCE(work->state) != KSMBD_WORK_ACTIVE);
- if (err && READ_ONCE(work->state) == KSMBD_WORK_ACTIVE) {
- /*
- * Woken by a signal, not a real cancel/close. There is no
- * notification backend yet to report anything else against,
- * so treat this the same as a client-side cancel.
- */
- WRITE_ONCE(work->state, KSMBD_WORK_CANCELLED);
- }
-
- spin_lock(&fp->f_lock);
- list_del_init(&work->fp_entry);
- spin_unlock(&fp->f_lock);
-
- rsp->hdr.Status = work->state == KSMBD_WORK_CLOSED ?
- STATUS_NOTIFY_CLEANUP : STATUS_CANCELLED;
- smb2_send_interim_resp(work, rsp->hdr.Status);
- work->send_no_response = 1;
-
-out:
- if (rsp->hdr.Status != STATUS_SUCCESS && !work->send_no_response)
- smb2_set_err_rsp(work);
- if (async_work)
- release_async_work(work);
- else
- kfree(argv);
- if (fp)
- ksmbd_fd_put(work, fp);
- return err;
+ return ksmbd_handle_notify(work, req, rsp);
}
/**
--
2.55.0
next prev parent reply other threads:[~2026-09-26 9:06 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-26 9:05 [PATCH 00/12] smb/server: change notify support ChenXiaoSong
2026-09-26 9:05 ` ChenXiaoSong [this message]
2026-09-26 9:05 ` [PATCH 02/12] smb/server: add more validation for change notify requests ChenXiaoSong
2026-09-26 9:05 ` [PATCH 03/12] smb/server: add debug type for change notify ChenXiaoSong
2026-09-26 9:05 ` [PATCH 04/12] smb/server: support non-recursive directory change watches ChenXiaoSong
2026-09-27 10:51 ` Namjae Jeon
2026-09-28 0:38 ` ChenXiaoSong
2026-09-26 9:05 ` [PATCH 05/12] smb/server: support recursive " ChenXiaoSong
2026-09-26 9:05 ` [PATCH 06/12] smb/server: keep notify watches on file handles ChenXiaoSong
2026-09-26 9:05 ` [PATCH 07/12] smb/server: save simple notify events ChenXiaoSong
2026-09-27 10:31 ` Namjae Jeon
2026-09-28 1:27 ` ChenXiaoSong
2026-09-26 9:05 ` [PATCH 08/12] smb/server: save old names for rename " ChenXiaoSong
2026-09-26 9:05 ` [PATCH 09/12] smb/server: match " ChenXiaoSong
2026-09-26 9:05 ` [PATCH 10/12] smb/server: encode " ChenXiaoSong
2026-09-26 9:05 ` [PATCH 11/12] smb/server: send notify events to the client ChenXiaoSong
2026-09-26 9:05 ` [PATCH 12/12] smb/server: break directory leases before sending notify events ChenXiaoSong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260926090518.78547-2-chenxiaosong@chenxiaosong.com \
--to=chenxiaosong@chenxiaosong.com \
--cc=chenxiaosong@kylinos.cn \
--cc=linkinjeon@kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=senozhatsky@chromium.org \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox