]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/linuxkpi/common/src/linux_schedule.c
MFC r310014-r327788:
[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/errno.h>
37 #include <linux/kernel.h>
38 #include <linux/list.h>
39 #include <linux/sched.h>
40 #include <linux/spinlock.h>
41 #include <linux/wait.h>
42
43 static int
44 linux_add_to_sleepqueue(void *wchan, const char *wmesg, int timeout, int state)
45 {
46         int flags, ret;
47
48         MPASS((state & ~TASK_NORMAL) == 0);
49
50         flags = SLEEPQ_SLEEP | ((state & TASK_INTERRUPTIBLE) != 0 ?
51             SLEEPQ_INTERRUPTIBLE : 0);
52
53         sleepq_add(wchan, NULL, wmesg, flags, 0);
54         if (timeout != 0)
55                 sleepq_set_timeout(wchan, timeout);
56         if ((state & TASK_INTERRUPTIBLE) != 0) {
57                 if (timeout == 0)
58                         ret = -sleepq_wait_sig(wchan, 0);
59                 else
60                         ret = -sleepq_timedwait_sig(wchan, 0);
61         } else {
62                 if (timeout == 0) {
63                         sleepq_wait(wchan, 0);
64                         ret = 0;
65                 } else
66                         ret = -sleepq_timedwait(wchan, 0);
67         }
68         /* filter return value */
69         if (ret != 0 && ret != -EWOULDBLOCK)
70                 ret = -ERESTARTSYS;
71         return (ret);
72 }
73
74 static int
75 wake_up_task(struct task_struct *task, unsigned int state)
76 {
77         int ret, wakeup_swapper;
78
79         ret = wakeup_swapper = 0;
80         sleepq_lock(task);
81         if ((atomic_read(&task->state) & state) != 0) {
82                 set_task_state(task, TASK_WAKING);
83                 wakeup_swapper = sleepq_signal(task, SLEEPQ_SLEEP, 0, 0);
84                 ret = 1;
85         }
86         sleepq_release(task);
87         if (wakeup_swapper)
88                 kick_proc0();
89         return (ret);
90 }
91
92 bool
93 linux_signal_pending(struct task_struct *task)
94 {
95         struct thread *td;
96         sigset_t pending;
97
98         td = task->task_thread;
99         PROC_LOCK(td->td_proc);
100         pending = td->td_siglist;
101         SIGSETOR(pending, td->td_proc->p_siglist);
102         SIGSETNAND(pending, td->td_sigmask);
103         PROC_UNLOCK(td->td_proc);
104         return (!SIGISEMPTY(pending));
105 }
106
107 bool
108 linux_fatal_signal_pending(struct task_struct *task)
109 {
110         struct thread *td;
111         bool ret;
112
113         td = task->task_thread;
114         PROC_LOCK(td->td_proc);
115         ret = SIGISMEMBER(td->td_siglist, SIGKILL) ||
116             SIGISMEMBER(td->td_proc->p_siglist, SIGKILL);
117         PROC_UNLOCK(td->td_proc);
118         return (ret);
119 }
120
121 bool
122 linux_signal_pending_state(long state, struct task_struct *task)
123 {
124
125         MPASS((state & ~TASK_NORMAL) == 0);
126
127         if ((state & TASK_INTERRUPTIBLE) == 0)
128                 return (false);
129         return (linux_signal_pending(task));
130 }
131
132 void
133 linux_send_sig(int signo, struct task_struct *task)
134 {
135         struct thread *td;
136
137         td = task->task_thread;
138         PROC_LOCK(td->td_proc);
139         tdsignal(td, signo);
140         PROC_UNLOCK(td->td_proc);
141 }
142
143 int
144 autoremove_wake_function(wait_queue_t *wq, unsigned int state, int flags,
145     void *key __unused)
146 {
147         struct task_struct *task;
148         int ret;
149
150         task = wq->private;
151         if ((ret = wake_up_task(task, state)) != 0)
152                 list_del_init(&wq->task_list);
153         return (ret);
154 }
155
156 void
157 linux_wake_up(wait_queue_head_t *wqh, unsigned int state, int nr, bool locked)
158 {
159         wait_queue_t *pos, *next;
160
161         if (!locked)
162                 spin_lock(&wqh->lock);
163         list_for_each_entry_safe(pos, next, &wqh->task_list, task_list) {
164                 if (pos->func == NULL) {
165                         if (wake_up_task(pos->private, state) != 0 && --nr == 0)
166                                 break;
167                 } else {
168                         if (pos->func(pos, state, 0, NULL) != 0 && --nr == 0)
169                                 break;
170                 }
171         }
172         if (!locked)
173                 spin_unlock(&wqh->lock);
174 }
175
176 void
177 linux_prepare_to_wait(wait_queue_head_t *wqh, wait_queue_t *wq, int state)
178 {
179
180         spin_lock(&wqh->lock);
181         if (list_empty(&wq->task_list))
182                 __add_wait_queue(wqh, wq);
183         set_task_state(current, state);
184         spin_unlock(&wqh->lock);
185 }
186
187 void
188 linux_finish_wait(wait_queue_head_t *wqh, wait_queue_t *wq)
189 {
190
191         spin_lock(&wqh->lock);
192         set_task_state(current, TASK_RUNNING);
193         if (!list_empty(&wq->task_list)) {
194                 __remove_wait_queue(wqh, wq);
195                 INIT_LIST_HEAD(&wq->task_list);
196         }
197         spin_unlock(&wqh->lock);
198 }
199
200 bool
201 linux_waitqueue_active(wait_queue_head_t *wqh)
202 {
203         bool ret;
204
205         spin_lock(&wqh->lock);
206         ret = !list_empty(&wqh->task_list);
207         spin_unlock(&wqh->lock);
208         return (ret);
209 }
210
211 int
212 linux_wait_event_common(wait_queue_head_t *wqh, wait_queue_t *wq, int timeout,
213     unsigned int state, spinlock_t *lock)
214 {
215         struct task_struct *task;
216         int ret;
217
218         if (lock != NULL)
219                 spin_unlock_irq(lock);
220
221         DROP_GIANT();
222
223         /* range check timeout */
224         if (timeout < 1)
225                 timeout = 1;
226         else if (timeout == MAX_SCHEDULE_TIMEOUT)
227                 timeout = 0;
228
229         task = current;
230
231         /*
232          * Our wait queue entry is on the stack - make sure it doesn't
233          * get swapped out while we sleep.
234          */
235         PHOLD(task->task_thread->td_proc);
236         sleepq_lock(task);
237         if (atomic_read(&task->state) != TASK_WAKING) {
238                 ret = linux_add_to_sleepqueue(task, "wevent", timeout, state);
239         } else {
240                 sleepq_release(task);
241                 ret = linux_signal_pending_state(state, task) ? -ERESTARTSYS : 0;
242         }
243         PRELE(task->task_thread->td_proc);
244
245         PICKUP_GIANT();
246
247         if (lock != NULL)
248                 spin_lock_irq(lock);
249         return (ret);
250 }
251
252 int
253 linux_schedule_timeout(int timeout)
254 {
255         struct task_struct *task;
256         int state;
257         int remainder;
258
259         task = current;
260
261         /* range check timeout */
262         if (timeout < 1)
263                 timeout = 1;
264         else if (timeout == MAX_SCHEDULE_TIMEOUT)
265                 timeout = 0;
266
267         remainder = ticks + timeout;
268
269         DROP_GIANT();
270
271         sleepq_lock(task);
272         state = atomic_read(&task->state);
273         if (state != TASK_WAKING)
274                 (void)linux_add_to_sleepqueue(task, "sched", timeout, state);
275         else
276                 sleepq_release(task);
277         set_task_state(task, TASK_RUNNING);
278
279         PICKUP_GIANT();
280
281         if (timeout == 0)
282                 return (MAX_SCHEDULE_TIMEOUT);
283
284         /* range check return value */
285         remainder -= ticks;
286         if (remainder < 0)
287                 remainder = 0;
288         else if (remainder > timeout)
289                 remainder = timeout;
290         return (remainder);
291 }
292
293 static void
294 wake_up_sleepers(void *wchan)
295 {
296         int wakeup_swapper;
297
298         sleepq_lock(wchan);
299         wakeup_swapper = sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
300         sleepq_release(wchan);
301         if (wakeup_swapper)
302                 kick_proc0();
303 }
304
305 #define bit_to_wchan(word, bit) ((void *)(((uintptr_t)(word) << 6) | (bit)))
306
307 void
308 linux_wake_up_bit(void *word, int bit)
309 {
310
311         wake_up_sleepers(bit_to_wchan(word, bit));
312 }
313
314 int
315 linux_wait_on_bit_timeout(unsigned long *word, int bit, unsigned int state,
316     int timeout)
317 {
318         struct task_struct *task;
319         void *wchan;
320         int ret;
321
322         DROP_GIANT();
323
324         /* range check timeout */
325         if (timeout < 1)
326                 timeout = 1;
327         else if (timeout == MAX_SCHEDULE_TIMEOUT)
328                 timeout = 0;
329
330         task = current;
331         wchan = bit_to_wchan(word, bit);
332         for (;;) {
333                 sleepq_lock(wchan);
334                 if ((*word & (1 << bit)) == 0) {
335                         sleepq_release(wchan);
336                         ret = 0;
337                         break;
338                 }
339                 set_task_state(task, state);
340                 ret = linux_add_to_sleepqueue(wchan, "wbit", timeout, state);
341                 if (ret != 0)
342                         break;
343         }
344         set_task_state(task, TASK_RUNNING);
345
346         PICKUP_GIANT();
347
348         return (ret);
349 }
350
351 void
352 linux_wake_up_atomic_t(atomic_t *a)
353 {
354
355         wake_up_sleepers(a);
356 }
357
358 int
359 linux_wait_on_atomic_t(atomic_t *a, unsigned int state)
360 {
361         struct task_struct *task;
362         void *wchan;
363         int ret;
364
365         DROP_GIANT();
366
367         task = current;
368         wchan = a;
369         for (;;) {
370                 sleepq_lock(wchan);
371                 if (atomic_read(a) == 0) {
372                         sleepq_release(wchan);
373                         ret = 0;
374                         break;
375                 }
376                 set_task_state(task, state);
377                 ret = linux_add_to_sleepqueue(wchan, "watomic", 0, state);
378                 if (ret != 0)
379                         break;
380         }
381         set_task_state(task, TASK_RUNNING);
382
383         PICKUP_GIANT();
384
385         return (ret);
386 }
387
388 bool
389 linux_wake_up_state(struct task_struct *task, unsigned int state)
390 {
391
392         return (wake_up_task(task, state) != 0);
393 }