diff --git a/sys/compat/linuxkpi/common/include/linux/wait.h b/sys/compat/linuxkpi/common/include/linux/wait.h --- a/sys/compat/linuxkpi/common/include/linux/wait.h +++ b/sys/compat/linuxkpi/common/include/linux/wait.h @@ -61,12 +61,14 @@ 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 @@ * 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 --- a/sys/compat/linuxkpi/common/src/linux_schedule.c +++ b/sys/compat/linuxkpi/common/src/linux_schedule.c @@ -200,6 +200,65 @@ 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) {