git: 1b2f43a7427e - main - LinuxKPI: Add cancel_work() function.

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Sun, 24 Dec 2023 08:23:12 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=1b2f43a7427ebf51561867f6c497833268014512

commit 1b2f43a7427ebf51561867f6c497833268014512
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2023-12-24 08:19:59 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2023-12-24 08:19:59 +0000

    LinuxKPI: Add cancel_work() function.
    
    Cancel a work not waiting for it to finish.
    
    Sponsored by:   Serenity Cyber Security, LLC
    Reviewed by:    manu, kib
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D42811
---
 .../linuxkpi/common/include/linux/workqueue.h      |  4 +++
 sys/compat/linuxkpi/common/src/linux_work.c        | 32 ++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/sys/compat/linuxkpi/common/include/linux/workqueue.h b/sys/compat/linuxkpi/common/include/linux/workqueue.h
index 226c2d1f4f0c..1c9df9fcb74d 100644
--- a/sys/compat/linuxkpi/common/include/linux/workqueue.h
+++ b/sys/compat/linuxkpi/common/include/linux/workqueue.h
@@ -188,6 +188,9 @@ do {									\
 #define	delayed_work_pending(dwork) \
 	linux_work_pending(&(dwork)->work)
 
+#define	cancel_work(work) \
+	linux_cancel_work(work)
+
 #define	cancel_delayed_work(dwork) \
 	linux_cancel_delayed_work(dwork)
 
@@ -243,6 +246,7 @@ extern void linux_destroy_workqueue(struct workqueue_struct *);
 extern bool linux_queue_work_on(int cpu, struct workqueue_struct *, struct work_struct *);
 extern bool linux_queue_delayed_work_on(int cpu, struct workqueue_struct *,
     struct delayed_work *, unsigned delay);
+extern bool linux_cancel_work(struct work_struct *);
 extern bool linux_cancel_delayed_work(struct delayed_work *);
 extern bool linux_cancel_work_sync(struct work_struct *);
 extern bool linux_cancel_delayed_work_sync(struct delayed_work *);
diff --git a/sys/compat/linuxkpi/common/src/linux_work.c b/sys/compat/linuxkpi/common/src/linux_work.c
index c62302ad2346..939bdbbc1434 100644
--- a/sys/compat/linuxkpi/common/src/linux_work.c
+++ b/sys/compat/linuxkpi/common/src/linux_work.c
@@ -360,6 +360,38 @@ linux_delayed_work_timer_fn(void *arg)
 	}
 }
 
+/*
+ * This function cancels the given work structure in a
+ * non-blocking fashion. It returns non-zero if the work was
+ * successfully cancelled. Else the work may still be busy or already
+ * cancelled.
+ */
+bool
+linux_cancel_work(struct work_struct *work)
+{
+	static const uint8_t states[WORK_ST_MAX] __aligned(8) = {
+		[WORK_ST_IDLE] = WORK_ST_IDLE,		/* NOP */
+		[WORK_ST_TIMER] = WORK_ST_TIMER,	/* can't happen */
+		[WORK_ST_TASK] = WORK_ST_IDLE,		/* cancel */
+		[WORK_ST_EXEC] = WORK_ST_EXEC,		/* NOP */
+		[WORK_ST_CANCEL] = WORK_ST_IDLE,	/* can't happen */
+	};
+	struct taskqueue *tq;
+
+	MPASS(atomic_read(&work->state) != WORK_ST_TIMER);
+	MPASS(atomic_read(&work->state) != WORK_ST_CANCEL);
+
+	switch (linux_update_state(&work->state, states)) {
+	case WORK_ST_TASK:
+		tq = work->work_queue->taskqueue;
+		if (taskqueue_cancel(tq, &work->work_task, NULL) == 0)
+			return (true);
+		/* FALLTHROUGH */
+	default:
+		return (false);
+	}
+}
+
 /*
  * This function cancels the given work structure in a synchronous
  * fashion. It returns non-zero if the work was successfully