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