Path: blob/main/sys/compat/linuxkpi/common/src/linux_schedule.c
39586 views
/*-1* Copyright (c) 2017 Mark Johnston <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conds6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice unmodified, this list of conds, and the following9* disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conds and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/systm.h>28#include <sys/proc.h>29#include <sys/signalvar.h>30#include <sys/sleepqueue.h>3132#include <linux/delay.h>33#include <linux/errno.h>34#include <linux/kernel.h>35#include <linux/list.h>36#include <linux/sched.h>37#include <linux/spinlock.h>38#include <linux/wait.h>3940/*41* Convert a relative time in jiffies to a tick count, suitable for use with42* native FreeBSD interfaces (callouts, sleepqueues, etc.).43*/44static int45linux_jiffies_timeout_to_ticks(long timeout)46{47if (timeout < 1)48return (1);49else if (timeout == MAX_SCHEDULE_TIMEOUT)50return (0);51else if (timeout > INT_MAX)52return (INT_MAX);53else54return (timeout);55}5657static int58linux_add_to_sleepqueue(void *wchan, struct task_struct *task,59const char *wmesg, long timeout, int state)60{61int flags, ret, stimeout;6263MPASS((state & ~(TASK_PARKED | TASK_NORMAL)) == 0);6465flags = SLEEPQ_SLEEP | ((state & TASK_INTERRUPTIBLE) != 0 ?66SLEEPQ_INTERRUPTIBLE : 0);67stimeout = linux_jiffies_timeout_to_ticks(timeout);6869sleepq_add(wchan, NULL, wmesg, flags, 0);70if (stimeout != 0)71sleepq_set_timeout(wchan, stimeout);7273DROP_GIANT();74if ((state & TASK_INTERRUPTIBLE) != 0) {75if (stimeout == 0)76ret = -sleepq_wait_sig(wchan, 0);77else78ret = -sleepq_timedwait_sig(wchan, 0);79} else {80if (stimeout == 0) {81sleepq_wait(wchan, 0);82ret = 0;83} else84ret = -sleepq_timedwait(wchan, 0);85}86PICKUP_GIANT();8788/* filter return value */89if (ret != 0 && ret != -EWOULDBLOCK) {90linux_schedule_save_interrupt_value(task, ret);91ret = -ERESTARTSYS;92}93return (ret);94}9596unsigned int97linux_msleep_interruptible(unsigned int ms)98{99int ret;100101/* guard against invalid values */102if (ms == 0)103ms = 1;104ret = -pause_sbt("lnxsleep", mstosbt(ms), 0, C_HARDCLOCK | C_CATCH);105106switch (ret) {107case -EWOULDBLOCK:108return (0);109default:110linux_schedule_save_interrupt_value(current, ret);111return (ms);112}113}114115static int116wake_up_task(struct task_struct *task, unsigned int state)117{118int ret;119120ret = 0;121sleepq_lock(task);122if ((atomic_read(&task->state) & state) != 0) {123set_task_state(task, TASK_WAKING);124sleepq_signal(task, SLEEPQ_SLEEP, 0, 0);125ret = 1;126}127sleepq_release(task);128return (ret);129}130131bool132linux_signal_pending(struct task_struct *task)133{134struct thread *td;135sigset_t pending;136137td = task->task_thread;138PROC_LOCK(td->td_proc);139pending = td->td_siglist;140SIGSETOR(pending, td->td_proc->p_siglist);141SIGSETNAND(pending, td->td_sigmask);142PROC_UNLOCK(td->td_proc);143return (!SIGISEMPTY(pending));144}145146bool147linux_fatal_signal_pending(struct task_struct *task)148{149struct thread *td;150bool ret;151152td = task->task_thread;153PROC_LOCK(td->td_proc);154ret = SIGISMEMBER(td->td_siglist, SIGKILL) ||155SIGISMEMBER(td->td_proc->p_siglist, SIGKILL);156PROC_UNLOCK(td->td_proc);157return (ret);158}159160bool161linux_signal_pending_state(long state, struct task_struct *task)162{163164MPASS((state & ~TASK_NORMAL) == 0);165166if ((state & TASK_INTERRUPTIBLE) == 0)167return (false);168return (linux_signal_pending(task));169}170171void172linux_send_sig(int signo, struct task_struct *task)173{174struct thread *td;175176td = task->task_thread;177PROC_LOCK(td->td_proc);178tdsignal(td, signo);179PROC_UNLOCK(td->td_proc);180}181182int183autoremove_wake_function(wait_queue_t *wq, unsigned int state, int flags,184void *key __unused)185{186struct task_struct *task;187int ret;188189task = wq->private;190if ((ret = wake_up_task(task, state)) != 0)191list_del_init(&wq->task_list);192return (ret);193}194195int196default_wake_function(wait_queue_t *wq, unsigned int state, int flags,197void *key __unused)198{199return (wake_up_task(wq->private, state));200}201202long203linux_wait_woken(wait_queue_t *wq, unsigned state, long timeout)204{205void *wchan;206struct task_struct *task;207int ret;208int remainder;209210task = current;211wchan = wq->private;212213remainder = jiffies + timeout;214215set_task_state(task, state);216217sleepq_lock(wchan);218if (!(wq->flags & WQ_FLAG_WOKEN)) {219ret = linux_add_to_sleepqueue(wchan, task, "woken",220timeout, state);221} else {222sleepq_release(wchan);223ret = 0;224}225226set_task_state(task, TASK_RUNNING);227wq->flags &= ~WQ_FLAG_WOKEN;228229if (timeout == MAX_SCHEDULE_TIMEOUT)230return (MAX_SCHEDULE_TIMEOUT);231232/* range check return value */233remainder -= jiffies;234235/* range check return value */236if (ret == -ERESTARTSYS && remainder < 1)237remainder = 1;238else if (remainder < 0)239remainder = 0;240else if (remainder > timeout)241remainder = timeout;242return (remainder);243}244245int246woken_wake_function(wait_queue_t *wq, unsigned int state,247int flags __unused, void *key __unused)248{249void *wchan;250251wchan = wq->private;252253sleepq_lock(wchan);254wq->flags |= WQ_FLAG_WOKEN;255sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);256sleepq_release(wchan);257258return (1);259}260261void262linux_init_wait_entry(wait_queue_t *wq, int flags)263{264265memset(wq, 0, sizeof(*wq));266wq->flags = flags;267wq->private = current;268wq->func = autoremove_wake_function;269INIT_LIST_HEAD(&wq->task_list);270}271272void273linux_wake_up(wait_queue_head_t *wqh, unsigned int state, int nr, bool locked)274{275wait_queue_t *pos, *next;276277if (!locked)278spin_lock(&wqh->lock);279list_for_each_entry_safe(pos, next, &wqh->task_list, task_list) {280if (pos->func == NULL) {281if (wake_up_task(pos->private, state) != 0 && --nr == 0)282break;283} else {284if (pos->func(pos, state, 0, NULL) != 0 && --nr == 0)285break;286}287}288if (!locked)289spin_unlock(&wqh->lock);290}291292void293linux_prepare_to_wait(wait_queue_head_t *wqh, wait_queue_t *wq, int state)294{295296spin_lock(&wqh->lock);297if (list_empty(&wq->task_list))298__add_wait_queue(wqh, wq);299set_task_state(current, state);300spin_unlock(&wqh->lock);301}302303void304linux_finish_wait(wait_queue_head_t *wqh, wait_queue_t *wq)305{306307spin_lock(&wqh->lock);308set_task_state(current, TASK_RUNNING);309if (!list_empty(&wq->task_list)) {310__remove_wait_queue(wqh, wq);311INIT_LIST_HEAD(&wq->task_list);312}313spin_unlock(&wqh->lock);314}315316bool317linux_waitqueue_active(wait_queue_head_t *wqh)318{319bool ret;320321spin_lock(&wqh->lock);322ret = !list_empty(&wqh->task_list);323spin_unlock(&wqh->lock);324return (ret);325}326327int328linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, long timeout,329unsigned int state, spinlock_t *lock)330{331struct task_struct *task;332int ret;333334if (lock != NULL)335spin_unlock_irq(lock);336337task = current;338339sleepq_lock(task);340if (atomic_read(&task->state) != TASK_WAKING) {341ret = linux_add_to_sleepqueue(task, task, "wevent", timeout,342state);343} else {344sleepq_release(task);345ret = 0;346}347348if (lock != NULL)349spin_lock_irq(lock);350return (ret);351}352353long354linux_schedule_timeout(long timeout)355{356struct task_struct *task;357long remainder;358int ret, state;359360task = current;361362remainder = jiffies + timeout;363364sleepq_lock(task);365state = atomic_read(&task->state);366if (state != TASK_WAKING) {367ret = linux_add_to_sleepqueue(task, task, "sched", timeout,368state);369} else {370sleepq_release(task);371ret = 0;372}373set_task_state(task, TASK_RUNNING);374375if (timeout == MAX_SCHEDULE_TIMEOUT)376return (MAX_SCHEDULE_TIMEOUT);377378/* range check return value */379remainder -= jiffies;380381/* range check return value */382if (ret == -ERESTARTSYS && remainder < 1)383remainder = 1;384else if (remainder < 0)385remainder = 0;386else if (remainder > timeout)387remainder = timeout;388return (remainder);389}390391static void392wake_up_sleepers(void *wchan)393{394sleepq_lock(wchan);395sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);396sleepq_release(wchan);397}398399#define bit_to_wchan(word, bit) ((void *)(((uintptr_t)(word) << 6) | (bit)))400401void402linux_wake_up_bit(void *word, int bit)403{404405wake_up_sleepers(bit_to_wchan(word, bit));406}407408int409linux_wait_on_bit_timeout(unsigned long *word, int bit, unsigned int state,410long timeout)411{412struct task_struct *task;413void *wchan;414int ret;415416task = current;417wchan = bit_to_wchan(word, bit);418for (;;) {419sleepq_lock(wchan);420if ((*word & (1 << bit)) == 0) {421sleepq_release(wchan);422ret = 0;423break;424}425set_task_state(task, state);426ret = linux_add_to_sleepqueue(wchan, task, "wbit", timeout,427state);428if (ret != 0)429break;430}431set_task_state(task, TASK_RUNNING);432433return (ret);434}435436void437linux_wake_up_atomic_t(atomic_t *a)438{439440wake_up_sleepers(a);441}442443int444linux_wait_on_atomic_t(atomic_t *a, unsigned int state)445{446struct task_struct *task;447void *wchan;448int ret;449450task = current;451wchan = a;452for (;;) {453sleepq_lock(wchan);454if (atomic_read(a) == 0) {455sleepq_release(wchan);456ret = 0;457break;458}459set_task_state(task, state);460ret = linux_add_to_sleepqueue(wchan, task, "watomic", 0, state);461if (ret != 0)462break;463}464set_task_state(task, TASK_RUNNING);465466return (ret);467}468469bool470linux_wake_up_state(struct task_struct *task, unsigned int state)471{472473return (wake_up_task(task, state) != 0);474}475476477