]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/linuxkpi/common/src/linux_schedule.c
MFC r339924:
[FreeBSD/FreeBSD.git] / sys / compat / linuxkpi / common / src / linux_schedule.c
1 /*-
2  * Copyright (c) 2017 Mark Johnston <markj@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conds
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conds, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conds and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/proc.h>
33 #include <sys/signalvar.h>
34 #include <sys/sleepqueue.h>
35
36 #include <linux/delay.h>
37 #include <linux/errno.h>
38 #include <linux/kernel.h>
39 #include <linux/list.h>
40 #include <linux/sched.h>
41 #include <linux/spinlock.h>
42 #include <linux/wait.h>
43
44 static int
45 linux_add_to_sleepqueue(void *wchan, struct task_struct *task,
46     const char *wmesg, int timeout, int state)
47 {
48         int flags, ret;
49
50         MPASS((state & ~(TASK_PARKED | TASK_NORMAL)) == 0);
51
52         flags = SLEEPQ_SLEEP | ((state & TASK_INTERRUPTIBLE) != 0 ?
53             SLEEPQ_INTERRUPTIBLE : 0);
54
55         sleepq_add(wchan, NULL, wmesg, flags, 0);
56         if (timeout != 0)
57                 sleepq_set_timeout(wchan, timeout);
58
59         DROP_GIANT();
60         if ((state & TASK_INTERRUPTIBLE) != 0) {
61                 if (timeout == 0)
62                         ret = -sleepq_wait_sig(wchan, 0);
63                 else
64                         ret = -sleepq_timedwait_sig(wchan, 0);
65         } else {
66                 if (timeout == 0) {
67                         sleepq_wait(wchan, 0);
68                         ret = 0;
69                 } else
70                         ret = -sleepq_timedwait(wchan, 0);
71         }
72         PICKUP_GIANT();
73
74         /* filter return value */
75         if (ret != 0 && ret != -EWOULDBLOCK) {
76                 linux_schedule_save_interrupt_value(task, ret);
77                 ret = -ERESTARTSYS;
78         }
79         return (ret);
80 }
81
82 unsigned int
83 linux_msleep_interruptible(unsigned int ms)
84 {
85         int ret;
86
87         /* guard against invalid values */
88         if (ms == 0)
89                 ms = 1;
90         ret = -pause_sbt("lnxsleep", mstosbt(ms), 0, C_HARDCLOCK | C_CATCH);
91
92         switch (ret) {
93         case -EWOULDBLOCK:
94                 return (0);
95         default:
96                 linux_schedule_save_interrupt_value(current, ret);
97                 return (ms);
98         }
99 }
100
101 static int
102 wake_up_task(struct task_struct *task, unsigned int state)
103 {
104         int ret, wakeup_swapper;
105
106         ret = wakeup_swapper = 0;
107         sleepq_lock(task);
108         if ((atomic_read(&task->state) & state) != 0) {
109                 set_task_state(task, TASK_WAKING);
110                 wakeup_swapper = sleepq_signal(task, SLEEPQ_SLEEP, 0, 0);
111                 ret = 1;
112         }
113         sleepq_release(task);
114         if (wakeup_swapper)
115                 kick_proc0();
116         return (ret);
117 }
118
119 bool
120 linux_signal_pending(struct task_struct *task)
121 {
122         struct thread *td;
123         sigset_t pending;
124
125         td = task->task_thread;
126         PROC_LOCK(td->td_proc);
127         pending = td->td_siglist;
128         SIGSETOR(pending, td->td_proc->p_siglist);
129         SIGSETNAND(pending, td->td_sigmask);
130         PROC_UNLOCK(td->td_proc);
131         return (!SIGISEMPTY(pending));
132 }
133
134 bool
135 linux_fatal_signal_pending(struct task_struct *task)
136 {
137         struct thread *td;
138         bool ret;
139
140         td = task->task_thread;
141         PROC_LOCK(td->td_proc);
142         ret = SIGISMEMBER(td->td_siglist, SIGKILL) ||
143             SIGISMEMBER(td->td_proc->p_siglist, SIGKILL);
144         PROC_UNLOCK(td->td_proc);
145         return (ret);
146 }
147
148 bool
149 linux_signal_pending_state(long state, struct task_struct *task)
150 {
151
152         MPASS((state & ~TASK_NORMAL) == 0);
153
154         if ((state & TASK_INTERRUPTIBLE) == 0)
155                 return (false);
156         return (linux_signal_pending(task));
157 }
158
159 void
160 linux_send_sig(int signo, struct task_struct *task)
161 {
162         struct thread *td;
163
164         td = task->task_thread;
165         PROC_LOCK(td->td_proc);
166         tdsignal(td, signo);
167         PROC_UNLOCK(td->td_proc);
168 }
169
170 int
171 autoremove_wake_function(wait_queue_t *wq, unsigned int state, int flags,
172     void *key __unused)
173 {
174         struct task_struct *task;
175         int ret;
176
177         task = wq->private;
178         if ((ret = wake_up_task(task, state)) != 0)
179                 list_del_init(&wq->task_list);
180         return (ret);
181 }
182
183 int
184 default_wake_function(wait_queue_t *wq, unsigned int state, int flags,
185     void *key __unused)
186 {
187         return (wake_up_task(wq->private, state));
188 }
189
190 void
191 linux_init_wait_entry(wait_queue_t *wq, int flags)
192 {
193
194         memset(wq, 0, sizeof(*wq));
195         wq->flags = flags;
196         wq->private = current;
197         wq->func = autoremove_wake_function;
198         INIT_LIST_HEAD(&wq->task_list);
199 }
200
201 void
202 linux_wake_up(wait_queue_head_t *wqh, unsigned int state, int nr, bool locked)
203 {
204         wait_queue_t *pos, *next;
205
206         if (!locked)
207                 spin_lock(&wqh->lock);
208         list_for_each_entry_safe(pos, next, &wqh->task_list, task_list) {
209                 if (pos->func == NULL) {
210                         if (wake_up_task(pos->private, state) != 0 && --nr == 0)
211                                 break;
212                 } else {
213                         if (pos->func(pos, state, 0, NULL) != 0 && --nr == 0)
214                                 break;
215                 }
216         }
217         if (!locked)
218                 spin_unlock(&wqh->lock);
219 }
220
221 void
222 linux_prepare_to_wait(wait_queue_head_t *wqh, wait_queue_t *wq, int state)
223 {
224
225         spin_lock(&wqh->lock);
226         if (list_empty(&wq->task_list))
227                 __add_wait_queue(wqh, wq);
228         set_task_state(current, state);
229         spin_unlock(&wqh->lock);
230 }
231
232 void
233 linux_finish_wait(wait_queue_head_t *wqh, wait_queue_t *wq)
234 {
235
236         spin_lock(&wqh->lock);
237         set_task_state(current, TASK_RUNNING);
238         if (!list_empty(&wq->task_list)) {
239                 __remove_wait_queue(wqh, wq);
240                 INIT_LIST_HEAD(&wq->task_list);
241         }
242         spin_unlock(&wqh->lock);
243 }
244
245 bool
246 linux_waitqueue_active(wait_queue_head_t *wqh)
247 {
248         bool ret;
249
250         spin_lock(&wqh->lock);
251         ret = !list_empty(&wqh->task_list);
252         spin_unlock(&wqh->lock);
253         return (ret);
254 }
255
256 int
257 linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
258     unsigned int state, spinlock_t *lock)
259 {
260         struct task_struct *task;
261         int ret;
262
263         if (lock != NULL)
264                 spin_unlock_irq(lock);
265
266         /* range check timeout */
267         if (timeout < 1)
268                 timeout = 1;
269         else if (timeout == MAX_SCHEDULE_TIMEOUT)
270                 timeout = 0;
271
272         task = current;
273
274         /*
275          * Our wait queue entry is on the stack - make sure it doesn't
276          * get swapped out while we sleep.
277          */
278         PHOLD(task->task_thread->td_proc);
279         sleepq_lock(task);
280         if (atomic_read(&task->state) != TASK_WAKING) {
281                 ret = linux_add_to_sleepqueue(task, task, "wevent", timeout,
282                     state);
283         } else {
284                 sleepq_release(task);
285                 ret = 0;
286         }
287         PRELE(task->task_thread->td_proc);
288
289         if (lock != NULL)
290                 spin_lock_irq(lock);
291         return (ret);
292 }
293
294 int
295 linux_schedule_timeout(int timeout)
296 {
297         struct task_struct *task;
298         int ret;
299         int state;
300         int remainder;
301
302         task = current;
303
304         /* range check timeout */
305         if (timeout < 1)
306                 timeout = 1;
307         else if (timeout == MAX_SCHEDULE_TIMEOUT)
308                 timeout = 0;
309
310         remainder = ticks + timeout;
311
312         sleepq_lock(task);
313         state = atomic_read(&task->state);
314         if (state != TASK_WAKING) {
315                 ret = linux_add_to_sleepqueue(task, task, "sched", timeout,
316                     state);
317         } else {
318                 sleepq_release(task);
319                 ret = 0;
320         }
321         set_task_state(task, TASK_RUNNING);
322
323         if (timeout == 0)
324                 return (MAX_SCHEDULE_TIMEOUT);
325
326         /* range check return value */
327         remainder -= ticks;
328
329         /* range check return value */
330         if (ret == -ERESTARTSYS && remainder < 1)
331                 remainder = 1;
332         else if (remainder < 0)
333                 remainder = 0;
334         else if (remainder > timeout)
335                 remainder = timeout;
336         return (remainder);
337 }
338
339 static void
340 wake_up_sleepers(void *wchan)
341 {
342         int wakeup_swapper;
343
344         sleepq_lock(wchan);
345         wakeup_swapper = sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
346         sleepq_release(wchan);
347         if (wakeup_swapper)
348                 kick_proc0();
349 }
350
351 #define bit_to_wchan(word, bit) ((void *)(((uintptr_t)(word) << 6) | (bit)))
352
353 void
354 linux_wake_up_bit(void *word, int bit)
355 {
356
357         wake_up_sleepers(bit_to_wchan(word, bit));
358 }
359
360 int
361 linux_wait_on_bit_timeout(unsigned long *word, int bit, unsigned int state,
362     int timeout)
363 {
364         struct task_struct *task;
365         void *wchan;
366         int ret;
367
368         /* range check timeout */
369         if (timeout < 1)
370                 timeout = 1;
371         else if (timeout == MAX_SCHEDULE_TIMEOUT)
372                 timeout = 0;
373
374         task = current;
375         wchan = bit_to_wchan(word, bit);
376         for (;;) {
377                 sleepq_lock(wchan);
378                 if ((*word & (1 << bit)) == 0) {
379                         sleepq_release(wchan);
380                         ret = 0;
381                         break;
382                 }
383                 set_task_state(task, state);
384                 ret = linux_add_to_sleepqueue(wchan, task, "wbit", timeout,
385                     state);
386                 if (ret != 0)
387                         break;
388         }
389         set_task_state(task, TASK_RUNNING);
390
391         return (ret);
392 }
393
394 void
395 linux_wake_up_atomic_t(atomic_t *a)
396 {
397
398         wake_up_sleepers(a);
399 }
400
401 int
402 linux_wait_on_atomic_t(atomic_t *a, unsigned int state)
403 {
404         struct task_struct *task;
405         void *wchan;
406         int ret;
407
408         task = current;
409         wchan = a;
410         for (;;) {
411                 sleepq_lock(wchan);
412                 if (atomic_read(a) == 0) {
413                         sleepq_release(wchan);
414                         ret = 0;
415                         break;
416                 }
417                 set_task_state(task, state);
418                 ret = linux_add_to_sleepqueue(wchan, task, "watomic", 0, state);
419                 if (ret != 0)
420                         break;
421         }
422         set_task_state(task, TASK_RUNNING);
423
424         return (ret);
425 }
426
427 bool
428 linux_wake_up_state(struct task_struct *task, unsigned int state)
429 {
430
431         return (wake_up_task(task, state) != 0);
432 }