xref: /freebsd/sys/compat/linuxkpi/common/src/linux_schedule.c (revision 01518f5eede79cf65319d455eb50e78c9efa2b51)
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