]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/compat/linuxkpi/common/src/linux_schedule.c
Don't call selrecord() outside the select system call in the LinuxKPI, because
[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_load_acq_int(&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 #ifndef NO_SWAPPING
236         PHOLD(task->task_thread->td_proc);
237 #endif
238         sleepq_lock(task);
239         if (atomic_load_acq_int(&task->state) != TASK_WAKING) {
240                 ret = linux_add_to_sleepqueue(task, "wevent", timeout, state);
241         } else {
242                 sleepq_release(task);
243                 ret = linux_signal_pending_state(state, task) ? -ERESTARTSYS : 0;
244         }
245 #ifndef NO_SWAPPING
246         PRELE(task->task_thread->td_proc);
247 #endif
248
249         PICKUP_GIANT();
250
251         if (lock != NULL)
252                 spin_lock_irq(lock);
253         return (ret);
254 }
255
256 int
257 linux_schedule_timeout(int timeout)
258 {
259         struct task_struct *task;
260         int state;
261         int remainder;
262
263         task = current;
264
265         /* range check timeout */
266         if (timeout < 1)
267                 timeout = 1;
268         else if (timeout == MAX_SCHEDULE_TIMEOUT)
269                 timeout = 0;
270
271         remainder = ticks + timeout;
272
273         DROP_GIANT();
274
275         sleepq_lock(task);
276         state = atomic_load_acq_int(&task->state);
277         if (state != TASK_WAKING)
278                 (void)linux_add_to_sleepqueue(task, "sched", timeout, state);
279         else
280                 sleepq_release(task);
281         set_task_state(task, TASK_RUNNING);
282
283         PICKUP_GIANT();
284
285         if (timeout == 0)
286                 return (MAX_SCHEDULE_TIMEOUT);
287
288         /* range check return value */
289         remainder -= ticks;
290         if (remainder < 0)
291                 remainder = 0;
292         else if (remainder > timeout)
293                 remainder = timeout;
294         return (remainder);
295 }
296
297 static void
298 wake_up_sleepers(void *wchan)
299 {
300         int wakeup_swapper;
301
302         sleepq_lock(wchan);
303         wakeup_swapper = sleepq_signal(wchan, SLEEPQ_SLEEP, 0, 0);
304         sleepq_release(wchan);
305         if (wakeup_swapper)
306                 kick_proc0();
307 }
308
309 #define bit_to_wchan(word, bit) ((void *)(((uintptr_t)(word) << 6) | (bit)))
310
311 void
312 linux_wake_up_bit(void *word, int bit)
313 {
314
315         wake_up_sleepers(bit_to_wchan(word, bit));
316 }
317
318 int
319 linux_wait_on_bit_timeout(unsigned long *word, int bit, unsigned int state,
320     int timeout)
321 {
322         struct task_struct *task;
323         void *wchan;
324         int ret;
325
326         DROP_GIANT();
327
328         /* range check timeout */
329         if (timeout < 1)
330                 timeout = 1;
331         else if (timeout == MAX_SCHEDULE_TIMEOUT)
332                 timeout = 0;
333
334         task = current;
335         wchan = bit_to_wchan(word, bit);
336         for (;;) {
337                 sleepq_lock(wchan);
338                 if ((*word & (1 << bit)) == 0) {
339                         sleepq_release(wchan);
340                         ret = 0;
341                         break;
342                 }
343                 set_task_state(task, state);
344                 ret = linux_add_to_sleepqueue(wchan, "wbit", timeout, state);
345                 if (ret != 0)
346                         break;
347         }
348         set_task_state(task, TASK_RUNNING);
349
350         PICKUP_GIANT();
351
352         return (ret);
353 }
354
355 void
356 linux_wake_up_atomic_t(atomic_t *a)
357 {
358
359         wake_up_sleepers(a);
360 }
361
362 int
363 linux_wait_on_atomic_t(atomic_t *a, unsigned int state)
364 {
365         struct task_struct *task;
366         void *wchan;
367         int ret;
368
369         DROP_GIANT();
370
371         task = current;
372         wchan = a;
373         for (;;) {
374                 sleepq_lock(wchan);
375                 if (atomic_read(a) == 0) {
376                         sleepq_release(wchan);
377                         ret = 0;
378                         break;
379                 }
380                 set_task_state(task, state);
381                 ret = linux_add_to_sleepqueue(wchan, "watomic", 0, state);
382                 if (ret != 0)
383                         break;
384         }
385         set_task_state(task, TASK_RUNNING);
386
387         PICKUP_GIANT();
388
389         return (ret);
390 }
391
392 bool
393 linux_wake_up_state(struct task_struct *task, unsigned int state)
394 {
395
396         return (wake_up_task(task, state) != 0);
397 }