146565964SMark Johnston /*- 246565964SMark Johnston * Copyright (c) 2017 Mark Johnston <markj@FreeBSD.org> 346565964SMark Johnston * All rights reserved. 446565964SMark Johnston * 546565964SMark Johnston * Redistribution and use in source and binary forms, with or without 646565964SMark Johnston * modification, are permitted provided that the following conds 746565964SMark Johnston * are met: 846565964SMark Johnston * 1. Redistributions of source code must retain the above copyright 946565964SMark Johnston * notice unmodified, this list of conds, and the following 1046565964SMark Johnston * disclaimer. 1146565964SMark Johnston * 2. Redistributions in binary form must reproduce the above copyright 1246565964SMark Johnston * notice, this list of conds and the following disclaimer in the 1346565964SMark Johnston * documentation and/or other materials provided with the distribution. 1446565964SMark Johnston * 1546565964SMark Johnston * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1646565964SMark Johnston * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1746565964SMark Johnston * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1846565964SMark Johnston * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1946565964SMark Johnston * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2046565964SMark Johnston * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2146565964SMark Johnston * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2246565964SMark Johnston * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2346565964SMark Johnston * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2446565964SMark Johnston * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2546565964SMark Johnston */ 2646565964SMark Johnston 2746565964SMark Johnston #include <sys/param.h> 2846565964SMark Johnston #include <sys/systm.h> 2946565964SMark Johnston #include <sys/proc.h> 3046565964SMark Johnston #include <sys/signalvar.h> 3146565964SMark Johnston #include <sys/sleepqueue.h> 3246565964SMark Johnston 337cf1c515SHans Petter Selasky #include <linux/delay.h> 3446565964SMark Johnston #include <linux/errno.h> 3546565964SMark Johnston #include <linux/kernel.h> 3646565964SMark Johnston #include <linux/list.h> 3746565964SMark Johnston #include <linux/sched.h> 3846565964SMark Johnston #include <linux/spinlock.h> 3946565964SMark Johnston #include <linux/wait.h> 4046565964SMark Johnston 4146565964SMark Johnston static int 4294944062SHans Petter Selasky linux_add_to_sleepqueue(void *wchan, struct task_struct *task, 4394944062SHans Petter Selasky const char *wmesg, int timeout, int state) 4446565964SMark Johnston { 4546565964SMark Johnston int flags, ret; 4646565964SMark Johnston 472a3ec128SHans Petter Selasky MPASS((state & ~(TASK_PARKED | TASK_NORMAL)) == 0); 4846565964SMark Johnston 4946565964SMark Johnston flags = SLEEPQ_SLEEP | ((state & TASK_INTERRUPTIBLE) != 0 ? 5046565964SMark Johnston SLEEPQ_INTERRUPTIBLE : 0); 5146565964SMark Johnston 5246565964SMark Johnston sleepq_add(wchan, NULL, wmesg, flags, 0); 5346565964SMark Johnston if (timeout != 0) 5446565964SMark Johnston sleepq_set_timeout(wchan, timeout); 554b706099SHans Petter Selasky 564b706099SHans Petter Selasky DROP_GIANT(); 5746565964SMark Johnston if ((state & TASK_INTERRUPTIBLE) != 0) { 5846565964SMark Johnston if (timeout == 0) 5946565964SMark Johnston ret = -sleepq_wait_sig(wchan, 0); 6046565964SMark Johnston else 6146565964SMark Johnston ret = -sleepq_timedwait_sig(wchan, 0); 6246565964SMark Johnston } else { 6346565964SMark Johnston if (timeout == 0) { 6446565964SMark Johnston sleepq_wait(wchan, 0); 6546565964SMark Johnston ret = 0; 6646565964SMark Johnston } else 6746565964SMark Johnston ret = -sleepq_timedwait(wchan, 0); 6846565964SMark Johnston } 694b706099SHans Petter Selasky PICKUP_GIANT(); 704b706099SHans Petter Selasky 7146565964SMark Johnston /* filter return value */ 7294944062SHans Petter Selasky if (ret != 0 && ret != -EWOULDBLOCK) { 7394944062SHans Petter Selasky linux_schedule_save_interrupt_value(task, ret); 7446565964SMark Johnston ret = -ERESTARTSYS; 7594944062SHans Petter Selasky } 7646565964SMark Johnston return (ret); 7746565964SMark Johnston } 7846565964SMark Johnston 797cf1c515SHans Petter Selasky unsigned int 807cf1c515SHans Petter Selasky linux_msleep_interruptible(unsigned int ms) 817cf1c515SHans Petter Selasky { 827cf1c515SHans Petter Selasky int ret; 837cf1c515SHans Petter Selasky 847cf1c515SHans Petter Selasky /* guard against invalid values */ 857cf1c515SHans Petter Selasky if (ms == 0) 867cf1c515SHans Petter Selasky ms = 1; 87ccae7bb8SHans Petter Selasky ret = -pause_sbt("lnxsleep", mstosbt(ms), 0, C_HARDCLOCK | C_CATCH); 887cf1c515SHans Petter Selasky 897cf1c515SHans Petter Selasky switch (ret) { 907cf1c515SHans Petter Selasky case -EWOULDBLOCK: 917cf1c515SHans Petter Selasky return (0); 927cf1c515SHans Petter Selasky default: 937cf1c515SHans Petter Selasky linux_schedule_save_interrupt_value(current, ret); 947cf1c515SHans Petter Selasky return (ms); 957cf1c515SHans Petter Selasky } 967cf1c515SHans Petter Selasky } 977cf1c515SHans Petter Selasky 9846565964SMark Johnston static int 9946565964SMark Johnston wake_up_task(struct task_struct *task, unsigned int state) 10046565964SMark Johnston { 101*01518f5eSMark Johnston int ret; 10246565964SMark Johnston 103*01518f5eSMark Johnston ret = 0; 10446565964SMark Johnston sleepq_lock(task); 105ef925749SHans Petter Selasky if ((atomic_read(&task->state) & state) != 0) { 10646565964SMark Johnston set_task_state(task, TASK_WAKING); 107*01518f5eSMark Johnston sleepq_signal(task, SLEEPQ_SLEEP, 0, 0); 10846565964SMark Johnston ret = 1; 10946565964SMark Johnston } 11046565964SMark Johnston sleepq_release(task); 11146565964SMark Johnston return (ret); 11246565964SMark Johnston } 11346565964SMark Johnston 11446565964SMark Johnston bool 11546565964SMark Johnston linux_signal_pending(struct task_struct *task) 11646565964SMark Johnston { 11746565964SMark Johnston struct thread *td; 11846565964SMark Johnston sigset_t pending; 11946565964SMark Johnston 12046565964SMark Johnston td = task->task_thread; 12146565964SMark Johnston PROC_LOCK(td->td_proc); 12246565964SMark Johnston pending = td->td_siglist; 12346565964SMark Johnston SIGSETOR(pending, td->td_proc->p_siglist); 12446565964SMark Johnston SIGSETNAND(pending, td->td_sigmask); 12546565964SMark Johnston PROC_UNLOCK(td->td_proc); 12646565964SMark Johnston return (!SIGISEMPTY(pending)); 12746565964SMark Johnston } 12846565964SMark Johnston 12946565964SMark Johnston bool 13046565964SMark Johnston linux_fatal_signal_pending(struct task_struct *task) 13146565964SMark Johnston { 13246565964SMark Johnston struct thread *td; 13346565964SMark Johnston bool ret; 13446565964SMark Johnston 13546565964SMark Johnston td = task->task_thread; 13646565964SMark Johnston PROC_LOCK(td->td_proc); 13746565964SMark Johnston ret = SIGISMEMBER(td->td_siglist, SIGKILL) || 13846565964SMark Johnston SIGISMEMBER(td->td_proc->p_siglist, SIGKILL); 13946565964SMark Johnston PROC_UNLOCK(td->td_proc); 14046565964SMark Johnston return (ret); 14146565964SMark Johnston } 14246565964SMark Johnston 14346565964SMark Johnston bool 14446565964SMark Johnston linux_signal_pending_state(long state, struct task_struct *task) 14546565964SMark Johnston { 14646565964SMark Johnston 14746565964SMark Johnston MPASS((state & ~TASK_NORMAL) == 0); 14846565964SMark Johnston 14946565964SMark Johnston if ((state & TASK_INTERRUPTIBLE) == 0) 15046565964SMark Johnston return (false); 15146565964SMark Johnston return (linux_signal_pending(task)); 15246565964SMark Johnston } 15346565964SMark Johnston 15446565964SMark Johnston void 15546565964SMark Johnston linux_send_sig(int signo, struct task_struct *task) 15646565964SMark Johnston { 15746565964SMark Johnston struct thread *td; 15846565964SMark Johnston 15946565964SMark Johnston td = task->task_thread; 16046565964SMark Johnston PROC_LOCK(td->td_proc); 16146565964SMark Johnston tdsignal(td, signo); 16246565964SMark Johnston PROC_UNLOCK(td->td_proc); 16346565964SMark Johnston } 16446565964SMark Johnston 16546565964SMark Johnston int 16646565964SMark Johnston autoremove_wake_function(wait_queue_t *wq, unsigned int state, int flags, 16746565964SMark Johnston void *key __unused) 16846565964SMark Johnston { 16946565964SMark Johnston struct task_struct *task; 17046565964SMark Johnston int ret; 17146565964SMark Johnston 17246565964SMark Johnston task = wq->private; 17346565964SMark Johnston if ((ret = wake_up_task(task, state)) != 0) 17446565964SMark Johnston list_del_init(&wq->task_list); 17546565964SMark Johnston return (ret); 17646565964SMark Johnston } 17746565964SMark Johnston 1788f368d48SHans Petter Selasky int 1798f368d48SHans Petter Selasky default_wake_function(wait_queue_t *wq, unsigned int state, int flags, 1808f368d48SHans Petter Selasky void *key __unused) 1818f368d48SHans Petter Selasky { 1828f368d48SHans Petter Selasky return (wake_up_task(wq->private, state)); 1838f368d48SHans Petter Selasky } 1848f368d48SHans Petter Selasky 18546565964SMark Johnston void 1861b092623SHans Petter Selasky linux_init_wait_entry(wait_queue_t *wq, int flags) 1871b092623SHans Petter Selasky { 1881b092623SHans Petter Selasky 1891b092623SHans Petter Selasky memset(wq, 0, sizeof(*wq)); 1901b092623SHans Petter Selasky wq->flags = flags; 1911b092623SHans Petter Selasky wq->private = current; 1921b092623SHans Petter Selasky wq->func = autoremove_wake_function; 1931b092623SHans Petter Selasky INIT_LIST_HEAD(&wq->task_list); 1941b092623SHans Petter Selasky } 1951b092623SHans Petter Selasky 1961b092623SHans Petter Selasky void 19746565964SMark Johnston linux_wake_up(wait_queue_head_t *wqh, unsigned int state, int nr, bool locked) 19846565964SMark Johnston { 19946565964SMark Johnston wait_queue_t *pos, *next; 20046565964SMark Johnston 20146565964SMark Johnston if (!locked) 202c974c22aSVladimir Kondratyev spin_lock(&wqh->lock); 20346565964SMark Johnston list_for_each_entry_safe(pos, next, &wqh->task_list, task_list) { 20446565964SMark Johnston if (pos->func == NULL) { 20546565964SMark Johnston if (wake_up_task(pos->private, state) != 0 && --nr == 0) 20646565964SMark Johnston break; 20746565964SMark Johnston } else { 20846565964SMark Johnston if (pos->func(pos, state, 0, NULL) != 0 && --nr == 0) 20946565964SMark Johnston break; 21046565964SMark Johnston } 21146565964SMark Johnston } 21246565964SMark Johnston if (!locked) 213c974c22aSVladimir Kondratyev spin_unlock(&wqh->lock); 21446565964SMark Johnston } 21546565964SMark Johnston 21646565964SMark Johnston void 21746565964SMark Johnston linux_prepare_to_wait(wait_queue_head_t *wqh, wait_queue_t *wq, int state) 21846565964SMark Johnston { 21946565964SMark Johnston 22046565964SMark Johnston spin_lock(&wqh->lock); 22146565964SMark Johnston if (list_empty(&wq->task_list)) 22246565964SMark Johnston __add_wait_queue(wqh, wq); 22346565964SMark Johnston set_task_state(current, state); 22446565964SMark Johnston spin_unlock(&wqh->lock); 22546565964SMark Johnston } 22646565964SMark Johnston 22746565964SMark Johnston void 22846565964SMark Johnston linux_finish_wait(wait_queue_head_t *wqh, wait_queue_t *wq) 22946565964SMark Johnston { 23046565964SMark Johnston 23146565964SMark Johnston spin_lock(&wqh->lock); 23246565964SMark Johnston set_task_state(current, TASK_RUNNING); 23346565964SMark Johnston if (!list_empty(&wq->task_list)) { 23446565964SMark Johnston __remove_wait_queue(wqh, wq); 23546565964SMark Johnston INIT_LIST_HEAD(&wq->task_list); 23646565964SMark Johnston } 23746565964SMark Johnston spin_unlock(&wqh->lock); 23846565964SMark Johnston } 23946565964SMark Johnston 24046565964SMark Johnston bool 24146565964SMark Johnston linux_waitqueue_active(wait_queue_head_t *wqh) 24246565964SMark Johnston { 24346565964SMark Johnston bool ret; 24446565964SMark Johnston 24546565964SMark Johnston spin_lock(&wqh->lock); 24646565964SMark Johnston ret = !list_empty(&wqh->task_list); 24746565964SMark Johnston spin_unlock(&wqh->lock); 24846565964SMark Johnston return (ret); 24946565964SMark Johnston } 25046565964SMark Johnston 25146565964SMark Johnston int 25246565964SMark Johnston linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout, 25346565964SMark Johnston unsigned int state, spinlock_t *lock) 25446565964SMark Johnston { 25546565964SMark Johnston struct task_struct *task; 2568ea44415SHans Petter Selasky int ret; 25746565964SMark Johnston 25846565964SMark Johnston if (lock != NULL) 25946565964SMark Johnston spin_unlock_irq(lock); 26046565964SMark Johnston 2618ea44415SHans Petter Selasky /* range check timeout */ 2628ea44415SHans Petter Selasky if (timeout < 1) 2638ea44415SHans Petter Selasky timeout = 1; 2648ea44415SHans Petter Selasky else if (timeout == MAX_SCHEDULE_TIMEOUT) 2658ea44415SHans Petter Selasky timeout = 0; 2668ea44415SHans Petter Selasky 26746565964SMark Johnston task = current; 26846565964SMark Johnston 26946565964SMark Johnston sleepq_lock(task); 270ef925749SHans Petter Selasky if (atomic_read(&task->state) != TASK_WAKING) { 2712a1067a9SMark Johnston ret = linux_add_to_sleepqueue(task, task, "wevent", timeout, 2722a1067a9SMark Johnston state); 27346565964SMark Johnston } else { 27446565964SMark Johnston sleepq_release(task); 27594944062SHans Petter Selasky ret = 0; 27646565964SMark Johnston } 27746565964SMark Johnston 27846565964SMark Johnston if (lock != NULL) 27946565964SMark Johnston spin_lock_irq(lock); 28046565964SMark Johnston return (ret); 28146565964SMark Johnston } 28246565964SMark Johnston 28346565964SMark Johnston int 28446565964SMark Johnston linux_schedule_timeout(int timeout) 28546565964SMark Johnston { 28646565964SMark Johnston struct task_struct *task; 28794944062SHans Petter Selasky int ret; 28846565964SMark Johnston int state; 28946565964SMark Johnston int remainder; 29046565964SMark Johnston 29146565964SMark Johnston task = current; 29246565964SMark Johnston 29346565964SMark Johnston /* range check timeout */ 29446565964SMark Johnston if (timeout < 1) 29546565964SMark Johnston timeout = 1; 29646565964SMark Johnston else if (timeout == MAX_SCHEDULE_TIMEOUT) 29746565964SMark Johnston timeout = 0; 29846565964SMark Johnston 29946565964SMark Johnston remainder = ticks + timeout; 30046565964SMark Johnston 30146565964SMark Johnston sleepq_lock(task); 302ef925749SHans Petter Selasky state = atomic_read(&task->state); 30394944062SHans Petter Selasky if (state != TASK_WAKING) { 3042a1067a9SMark Johnston ret = linux_add_to_sleepqueue(task, task, "sched", timeout, 3052a1067a9SMark Johnston state); 30694944062SHans Petter Selasky } else { 30746565964SMark Johnston sleepq_release(task); 30894944062SHans Petter Selasky ret = 0; 30994944062SHans Petter Selasky } 31046565964SMark Johnston set_task_state(task, TASK_RUNNING); 31146565964SMark Johnston 31246565964SMark Johnston if (timeout == 0) 31346565964SMark Johnston return (MAX_SCHEDULE_TIMEOUT); 31446565964SMark Johnston 31546565964SMark Johnston /* range check return value */ 31646565964SMark Johnston remainder -= ticks; 31794944062SHans Petter Selasky 31894944062SHans Petter Selasky /* range check return value */ 31994944062SHans Petter Selasky if (ret == -ERESTARTSYS && remainder < 1) 32094944062SHans Petter Selasky remainder = 1; 32194944062SHans Petter Selasky else if (remainder < 0) 32246565964SMark Johnston remainder = 0; 32346565964SMark Johnston else if (remainder > timeout) 32446565964SMark Johnston remainder = timeout; 32546565964SMark Johnston return (remainder); 32646565964SMark Johnston } 32746565964SMark Johnston 32846565964SMark Johnston static void 32946565964SMark Johnston wake_up_sleepers(void *wchan) 33046565964SMark Johnston { 33146565964SMark Johnston sleepq_lock(wchan); 332*01518f5eSMark Johnston sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0); 33346565964SMark Johnston sleepq_release(wchan); 33446565964SMark Johnston } 33546565964SMark Johnston 33646565964SMark Johnston #define bit_to_wchan(word, bit) ((void *)(((uintptr_t)(word) << 6) | (bit))) 33746565964SMark Johnston 33846565964SMark Johnston void 33946565964SMark Johnston linux_wake_up_bit(void *word, int bit) 34046565964SMark Johnston { 34146565964SMark Johnston 34246565964SMark Johnston wake_up_sleepers(bit_to_wchan(word, bit)); 34346565964SMark Johnston } 34446565964SMark Johnston 34546565964SMark Johnston int 34646565964SMark Johnston linux_wait_on_bit_timeout(unsigned long *word, int bit, unsigned int state, 34746565964SMark Johnston int timeout) 34846565964SMark Johnston { 34946565964SMark Johnston struct task_struct *task; 35046565964SMark Johnston void *wchan; 35146565964SMark Johnston int ret; 35246565964SMark Johnston 35346565964SMark Johnston /* range check timeout */ 35446565964SMark Johnston if (timeout < 1) 35546565964SMark Johnston timeout = 1; 35646565964SMark Johnston else if (timeout == MAX_SCHEDULE_TIMEOUT) 35746565964SMark Johnston timeout = 0; 35846565964SMark Johnston 35946565964SMark Johnston task = current; 36046565964SMark Johnston wchan = bit_to_wchan(word, bit); 36146565964SMark Johnston for (;;) { 36246565964SMark Johnston sleepq_lock(wchan); 36346565964SMark Johnston if ((*word & (1 << bit)) == 0) { 36446565964SMark Johnston sleepq_release(wchan); 36546565964SMark Johnston ret = 0; 36646565964SMark Johnston break; 36746565964SMark Johnston } 36846565964SMark Johnston set_task_state(task, state); 3692a1067a9SMark Johnston ret = linux_add_to_sleepqueue(wchan, task, "wbit", timeout, 3702a1067a9SMark Johnston state); 37146565964SMark Johnston if (ret != 0) 37246565964SMark Johnston break; 37346565964SMark Johnston } 37446565964SMark Johnston set_task_state(task, TASK_RUNNING); 37546565964SMark Johnston 37646565964SMark Johnston return (ret); 37746565964SMark Johnston } 37846565964SMark Johnston 37946565964SMark Johnston void 38046565964SMark Johnston linux_wake_up_atomic_t(atomic_t *a) 38146565964SMark Johnston { 38246565964SMark Johnston 38346565964SMark Johnston wake_up_sleepers(a); 38446565964SMark Johnston } 38546565964SMark Johnston 38646565964SMark Johnston int 38746565964SMark Johnston linux_wait_on_atomic_t(atomic_t *a, unsigned int state) 38846565964SMark Johnston { 38946565964SMark Johnston struct task_struct *task; 39046565964SMark Johnston void *wchan; 39146565964SMark Johnston int ret; 39246565964SMark Johnston 39346565964SMark Johnston task = current; 39446565964SMark Johnston wchan = a; 39546565964SMark Johnston for (;;) { 39646565964SMark Johnston sleepq_lock(wchan); 39746565964SMark Johnston if (atomic_read(a) == 0) { 39846565964SMark Johnston sleepq_release(wchan); 39946565964SMark Johnston ret = 0; 40046565964SMark Johnston break; 40146565964SMark Johnston } 40246565964SMark Johnston set_task_state(task, state); 40394944062SHans Petter Selasky ret = linux_add_to_sleepqueue(wchan, task, "watomic", 0, state); 40446565964SMark Johnston if (ret != 0) 40546565964SMark Johnston break; 40646565964SMark Johnston } 40746565964SMark Johnston set_task_state(task, TASK_RUNNING); 40846565964SMark Johnston 40946565964SMark Johnston return (ret); 41046565964SMark Johnston } 41146565964SMark Johnston 41246565964SMark Johnston bool 41346565964SMark Johnston linux_wake_up_state(struct task_struct *task, unsigned int state) 41446565964SMark Johnston { 41546565964SMark Johnston 41646565964SMark Johnston return (wake_up_task(task, state) != 0); 41746565964SMark Johnston } 418