[RFC PATCH 03/11] smb/server: support canceling notify requests
ChenXiaoSong <[email protected]> Thu, 23 Jul 2026 03:16:31 +0000
| Newsgroups | org.kernel.vger.linux-cifs |
|---|---|
| Message-ID | <[email protected]> |
From: ChenXiaoSong <[email protected]> Register CHANGE_NOTIFY requests as asynchronous work so SMB2 CANCEL requests can wake and complete them with STATUS_CANCELLED. Example: 1. smbinfo notify /mnt 2. Then press `Ctrl+C` to exit `smbinfo notify` command. 3. The following packets can be captured using tcpdump: - Notify Request, File: <share> - Notify Response, Error: STATUS_PENDING - Cancel Request - Notify Response, Error: STATUS_CANCELLED Signed-off-by: ChenXiaoSong <[email protected]> --- fs/smb/server/notify.c | 103 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/notify.c b/fs/smb/server/notify.c index ad812fde5879..d1b971a75037 100644 --- a/fs/smb/server/notify.c +++ b/fs/smb/server/notify.c @@ -8,6 +8,9 @@ * */ +#include <linux/slab.h> +#include <linux/wait.h> + #include "glob.h" #include "../common/smb2status.h" #include "connection.h" @@ -17,6 +20,18 @@ #include "smb2pdu.h" #include "vfs_cache.h" +struct ksmbd_notify_req { + wait_queue_head_t wait; +}; + +static void smb2_notify_cancel(void **argv) +{ + struct ksmbd_notify_req *notify_req = argv[0]; + + ksmbd_debug(NOTIFY, "Wake pending notify request\n"); + wake_up(¬ify_req->wait); +} + static struct ksmbd_file * ksmbd_notify_validate_req(struct ksmbd_work *work, struct smb2_change_notify_req *req, @@ -73,6 +88,36 @@ ksmbd_notify_validate_req(struct ksmbd_work *work, return ERR_PTR(err); } +static int ksmbd_notify_wait(struct ksmbd_work *work, + struct ksmbd_file *fp, + struct ksmbd_notify_req *notify_req) +{ + int err; + + spin_lock(&fp->f_lock); + list_add_tail(&work->fp_entry, &fp->blocked_works); + spin_unlock(&fp->f_lock); + + ksmbd_debug(NOTIFY, "Notify request pending, async id %d\n", + work->async_id); + 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); + 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 @@ -85,24 +130,74 @@ 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 = NULL; 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; } - ksmbd_fd_put(work, fp); - rsp->hdr.Status = STATUS_NOT_IMPLEMENTED; - err = -EOPNOTSUPP; + notify_req = kzalloc_obj(*notify_req, KSMBD_DEFAULT_GFP); + if (!notify_req) { + pr_err("Failed to allocate notify request\n"); + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + err = -ENOMEM; + 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] = notify_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, notify_req); + + if (work->state == KSMBD_WORK_CANCELLED) { + ksmbd_debug(NOTIFY, "Notify request cancelled, async id %d\n", + work->async_id); + rsp->hdr.Status = STATUS_CANCELLED; + smb2_send_interim_resp(work, STATUS_CANCELLED); + work->send_no_response = 1; + } else if (work->state == KSMBD_WORK_CLOSED) { + ksmbd_debug(NOTIFY, "Notify handle closed, async id %d\n", + work->async_id); + rsp->hdr.Status = STATUS_NOTIFY_CLEANUP; + smb2_send_interim_resp(work, STATUS_NOTIFY_CLEANUP); + work->send_no_response = 1; + } out: if (err) pr_err("Failed to handle notify request: %d, status: 0x%x\n", err, le32_to_cpu(rsp->hdr.Status)); - if (rsp->hdr.Status != STATUS_SUCCESS) + 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 (notify_req) + kfree(notify_req); + if (fp) + ksmbd_fd_put(work, fp); return err; } -- 2.54.0