git: 6d720cdfe735 - main - linuxkpi: Add `woken_wake_function()` and `wait_woken()`

From: Jean-Sébastien Pédron <dumbbell_at_FreeBSD.org>
Date: Mon, 12 May 2025 17:46:47 UTC
The branch main has been updated by dumbbell:

URL: https://cgit.FreeBSD.org/src/commit/?id=6d720cdfe7350d1eed620f1456bbe76ead708c30

commit 6d720cdfe7350d1eed620f1456bbe76ead708c30
Author:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
AuthorDate: 2024-12-27 21:43:59 +0000
Commit:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
CommitDate: 2025-05-12 17:08:02 +0000

    linuxkpi: Add `woken_wake_function()` and `wait_woken()`
    
    They are used by the i915 DRM driver starting with Linux 6.7.
    
    `(struct wait_queue)->flags` is no longer always zero. I wonder if some
    code relied on this...
    
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D48755
---
 sys/compat/linuxkpi/common/include/linux/wait.h | 14 ++++--
 sys/compat/linuxkpi/common/src/linux_schedule.c | 59 +++++++++++++++++++++++++
 2 files changed, 70 insertions(+), 3 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h
index bd496793e27e..03ddce2c06f5 100644
--- a/sys/compat/linuxkpi/common/include/linux/wait.h
+++ b/sys/compat/linuxkpi/common/include/linux/wait.h
@@ -61,12 +61,14 @@ typedef struct wait_queue_head wait_queue_head_t;
 
 typedef int wait_queue_func_t(wait_queue_t *, unsigned int, int, void *);
 
+#define WQ_FLAG_WOKEN		0x02
+
 /*
  * Many API consumers directly reference these fields and those of
  * wait_queue_head.
  */
 struct wait_queue {
-	unsigned int flags;	/* always 0 */
+	unsigned int flags;
 	void *private;
 	wait_queue_func_t *func;
 	union {
@@ -87,8 +89,14 @@ struct wait_queue_head {
  * This function is referenced by at least one DRM driver, so it may not be
  * renamed and furthermore must be the default wait queue callback.
  */
-extern wait_queue_func_t autoremove_wake_function;
-extern wait_queue_func_t default_wake_function;
+wait_queue_func_t autoremove_wake_function;
+wait_queue_func_t default_wake_function;
+wait_queue_func_t woken_wake_function;
+
+long linux_wait_woken(wait_queue_t *wq, unsigned state, long timeout);
+
+#define	wait_woken(wq, state, timeout) \
+	linux_wait_woken((wq), (state), (timeout))
 
 #define	DEFINE_WAIT_FUNC(name, function)				\
 	wait_queue_t name = {						\
diff --git a/sys/compat/linuxkpi/common/src/linux_schedule.c b/sys/compat/linuxkpi/common/src/linux_schedule.c
index 3f3605096d62..c6b7a2ebbd66 100644
--- a/sys/compat/linuxkpi/common/src/linux_schedule.c
+++ b/sys/compat/linuxkpi/common/src/linux_schedule.c
@@ -200,6 +200,65 @@ default_wake_function(wait_queue_t *wq, unsigned int state, int flags,
 	return (wake_up_task(wq->private, state));
 }
 
+long
+linux_wait_woken(wait_queue_t *wq, unsigned state, long timeout)
+{
+	void *wchan;
+	struct task_struct *task;
+	int ret;
+	int remainder;
+
+	task = current;
+	wchan = wq->private;
+
+	remainder = jiffies + timeout;
+
+	set_task_state(task, state);
+
+	sleepq_lock(wchan);
+	if (!(wq->flags & WQ_FLAG_WOKEN)) {
+		ret = linux_add_to_sleepqueue(wchan, task, "woken",
+		    timeout, state);
+	} else {
+		sleepq_release(wchan);
+		ret = 0;
+	}
+
+	set_task_state(task, TASK_RUNNING);
+	wq->flags &= ~WQ_FLAG_WOKEN;
+
+	if (timeout == MAX_SCHEDULE_TIMEOUT)
+		return (MAX_SCHEDULE_TIMEOUT);
+
+	/* range check return value */
+	remainder -= jiffies;
+
+	/* range check return value */
+	if (ret == -ERESTARTSYS && remainder < 1)
+		remainder = 1;
+	else if (remainder < 0)
+		remainder = 0;
+	else if (remainder > timeout)
+		remainder = timeout;
+	return (remainder);
+}
+
+int
+woken_wake_function(wait_queue_t *wq, unsigned int state,
+    int flags __unused, void *key __unused)
+{
+	void *wchan;
+
+	wchan = wq->private;
+
+	sleepq_lock(wchan);
+	wq->flags |= WQ_FLAG_WOKEN;
+	sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
+	sleepq_release(wchan);
+
+	return (1);
+}
+
 void
 linux_init_wait_entry(wait_queue_t *wq, int flags)
 {