rumpfiber.c revision 1.1 1 1.1 justin /*
2 1.1 justin * Copyright (c) 2007-2013 Antti Kantee. All Rights Reserved.
3 1.1 justin * Copyright (c) 2014 Justin Cormack. All Rights Reserved.
4 1.1 justin *
5 1.1 justin * Redistribution and use in source and binary forms, with or without
6 1.1 justin * modification, are permitted provided that the following conditions
7 1.1 justin * are met:
8 1.1 justin * 1. Redistributions of source code must retain the above copyright
9 1.1 justin * notice, this list of conditions and the following disclaimer.
10 1.1 justin * 2. Redistributions in binary form must reproduce the above copyright
11 1.1 justin * notice, this list of conditions and the following disclaimer in the
12 1.1 justin * documentation and/or other materials provided with the distribution.
13 1.1 justin *
14 1.1 justin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
15 1.1 justin * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 1.1 justin * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 1.1 justin * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 1.1 justin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 1.1 justin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 1.1 justin * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 1.1 justin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 1.1 justin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 1.1 justin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 1.1 justin * SUCH DAMAGE.
25 1.1 justin */
26 1.1 justin
27 1.1 justin /* Based partly on code from Xen Minios with the following license */
28 1.1 justin
29 1.1 justin /*
30 1.1 justin ****************************************************************************
31 1.1 justin * (C) 2005 - Grzegorz Milos - Intel Research Cambridge
32 1.1 justin ****************************************************************************
33 1.1 justin *
34 1.1 justin * File: sched.c
35 1.1 justin * Author: Grzegorz Milos
36 1.1 justin * Changes: Robert Kaiser
37 1.1 justin *
38 1.1 justin * Date: Aug 2005
39 1.1 justin *
40 1.1 justin * Environment: Xen Minimal OS
41 1.1 justin * Description: simple scheduler for Mini-Os
42 1.1 justin *
43 1.1 justin * The scheduler is non-preemptive (cooperative), and schedules according
44 1.1 justin * to Round Robin algorithm.
45 1.1 justin *
46 1.1 justin ****************************************************************************
47 1.1 justin * Permission is hereby granted, free of charge, to any person obtaining a copy
48 1.1 justin * of this software and associated documentation files (the "Software"), to
49 1.1 justin * deal in the Software without restriction, including without limitation the
50 1.1 justin * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
51 1.1 justin * sell copies of the Software, and to permit persons to whom the Software is
52 1.1 justin * furnished to do so, subject to the following conditions:
53 1.1 justin *
54 1.1 justin * The above copyright notice and this permission notice shall be included in
55 1.1 justin * all copies or substantial portions of the Software.
56 1.1 justin *
57 1.1 justin * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58 1.1 justin * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59 1.1 justin * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60 1.1 justin * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
61 1.1 justin * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
62 1.1 justin * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
63 1.1 justin * DEALINGS IN THE SOFTWARE.
64 1.1 justin */
65 1.1 justin
66 1.1 justin #include "rumpuser_port.h"
67 1.1 justin
68 1.1 justin #if !defined(lint)
69 1.1 justin __RCSID("$NetBSD: rumpfiber.c,v 1.1 2014/07/11 20:26:31 justin Exp $");
70 1.1 justin #endif /* !lint */
71 1.1 justin
72 1.1 justin #include <sys/ioctl.h>
73 1.1 justin #include <sys/mman.h>
74 1.1 justin #include <sys/time.h>
75 1.1 justin
76 1.1 justin #include <assert.h>
77 1.1 justin #include <errno.h>
78 1.1 justin #include <fcntl.h>
79 1.1 justin #include <signal.h>
80 1.1 justin #include <stdarg.h>
81 1.1 justin #include <stdint.h>
82 1.1 justin #include <stdio.h>
83 1.1 justin #include <stdlib.h>
84 1.1 justin #include <string.h>
85 1.1 justin #include <time.h>
86 1.1 justin #include <unistd.h>
87 1.1 justin
88 1.1 justin #include <rump/rumpuser.h>
89 1.1 justin
90 1.1 justin #include "rumpuser_int.h"
91 1.1 justin #include "rumpfiber.h"
92 1.1 justin
93 1.1 justin static void init_sched(void);
94 1.1 justin static void join_thread(struct thread *);
95 1.1 justin static void switch_threads(struct thread *prev, struct thread *next);
96 1.1 justin static struct thread *get_current(void);
97 1.1 justin static int64_t now(void);
98 1.1 justin static void msleep(uint64_t millisecs);
99 1.1 justin static void abssleep(uint64_t millisecs);
100 1.1 justin
101 1.1 justin TAILQ_HEAD(thread_list, thread);
102 1.1 justin
103 1.1 justin static struct thread_list exited_threads = TAILQ_HEAD_INITIALIZER(exited_threads);
104 1.1 justin static struct thread_list thread_list = TAILQ_HEAD_INITIALIZER(thread_list);
105 1.1 justin static struct thread *current_thread = NULL;
106 1.1 justin
107 1.1 justin static void (*scheduler_hook)(void *, void *);
108 1.1 justin
109 1.1 justin static struct thread *
110 1.1 justin get_current(void)
111 1.1 justin {
112 1.1 justin
113 1.1 justin return current_thread;
114 1.1 justin }
115 1.1 justin
116 1.1 justin static int64_t
117 1.1 justin now(void)
118 1.1 justin {
119 1.1 justin struct timespec ts;
120 1.1 justin
121 1.1 justin clock_gettime(CLOCK_MONOTONIC, &ts);
122 1.1 justin return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL);
123 1.1 justin }
124 1.1 justin
125 1.1 justin void
126 1.1 justin schedule(void)
127 1.1 justin {
128 1.1 justin struct thread *prev, *next, *thread, *tmp;
129 1.1 justin int64_t tm, wakeup;
130 1.1 justin struct timespec sl;
131 1.1 justin
132 1.1 justin prev = get_current();
133 1.1 justin
134 1.1 justin do {
135 1.1 justin tm = now();
136 1.1 justin wakeup = tm + 1000; /* wake up in 1s max */
137 1.1 justin next = NULL;
138 1.1 justin TAILQ_FOREACH_SAFE(thread, &thread_list, thread_list, tmp) {
139 1.1 justin if (!is_runnable(thread) && thread->wakeup_time >= 0) {
140 1.1 justin if (thread->wakeup_time <= tm) {
141 1.1 justin thread->flags |= THREAD_TIMEDOUT;
142 1.1 justin wake(thread);
143 1.1 justin } else if (thread->wakeup_time < wakeup)
144 1.1 justin wakeup = thread->wakeup_time;
145 1.1 justin }
146 1.1 justin if (is_runnable(thread)) {
147 1.1 justin next = thread;
148 1.1 justin /* Put this thread on the end of the list */
149 1.1 justin TAILQ_REMOVE(&thread_list, thread, thread_list);
150 1.1 justin TAILQ_INSERT_TAIL(&thread_list, thread, thread_list);
151 1.1 justin break;
152 1.1 justin }
153 1.1 justin }
154 1.1 justin if (next)
155 1.1 justin break;
156 1.1 justin sl.tv_sec = (wakeup - tm) / 1000;
157 1.1 justin sl.tv_nsec = ((wakeup - tm) - 1000 * sl.tv_sec) * 1000000;
158 1.1 justin #ifdef HAVE_CLOCK_NANOSLEEP
159 1.1 justin clock_nanosleep(CLOCK_MONOTONIC, 0, &sl, NULL);
160 1.1 justin #else
161 1.1 justin nanosleep(&sl, NULL);
162 1.1 justin #endif
163 1.1 justin } while (1);
164 1.1 justin
165 1.1 justin if (prev != next)
166 1.1 justin switch_threads(prev, next);
167 1.1 justin
168 1.1 justin TAILQ_FOREACH_SAFE(thread, &exited_threads, thread_list, tmp) {
169 1.1 justin if (thread != prev) {
170 1.1 justin TAILQ_REMOVE(&exited_threads, thread, thread_list);
171 1.1 justin if ((thread->flags & THREAD_EXTSTACK) == 0)
172 1.1 justin munmap(thread->ctx.uc_stack.ss_sp, STACKSIZE);
173 1.1 justin free(thread->name);
174 1.1 justin free(thread);
175 1.1 justin }
176 1.1 justin }
177 1.1 justin }
178 1.1 justin
179 1.1 justin static void
180 1.1 justin create_ctx(ucontext_t *ctx, void *stack, size_t stack_size)
181 1.1 justin {
182 1.1 justin
183 1.1 justin getcontext(ctx);
184 1.1 justin ctx->uc_stack.ss_sp = stack;
185 1.1 justin ctx->uc_stack.ss_size = stack_size;
186 1.1 justin ctx->uc_stack.ss_flags = 0;
187 1.1 justin ctx->uc_link = NULL; /* TODO may link to main thread */
188 1.1 justin }
189 1.1 justin
190 1.1 justin /* may have to do bounce function to call, if args to makecontext are ints */
191 1.1 justin /* TODO see notes in rumpuser_thread_create, have flags here */
192 1.1 justin struct thread *
193 1.1 justin create_thread(const char *name, void *cookie, void (*f)(void *), void *data,
194 1.1 justin void *stack, size_t stack_size)
195 1.1 justin {
196 1.1 justin struct thread *thread = calloc(1, sizeof(struct thread));
197 1.1 justin
198 1.1 justin if (!stack) {
199 1.1 justin assert(stack_size == 0);
200 1.1 justin stack = mmap(NULL, STACKSIZE, PROT_READ | PROT_WRITE,
201 1.1 justin MAP_SHARED | MAP_ANON, -1, 0);
202 1.1 justin if (stack == MAP_FAILED) {
203 1.1 justin return NULL;
204 1.1 justin }
205 1.1 justin stack_size = STACKSIZE;
206 1.1 justin } else {
207 1.1 justin thread->flags = THREAD_EXTSTACK;
208 1.1 justin }
209 1.1 justin create_ctx(&thread->ctx, stack, stack_size);
210 1.1 justin makecontext(&thread->ctx, (void (*)(void))f, 1, data);
211 1.1 justin
212 1.1 justin thread->name = strdup(name);
213 1.1 justin thread->cookie = cookie;
214 1.1 justin
215 1.1 justin /* Not runnable, not exited, not sleeping */
216 1.1 justin thread->wakeup_time = -1;
217 1.1 justin thread->lwp = NULL;
218 1.1 justin set_runnable(thread);
219 1.1 justin TAILQ_INSERT_TAIL(&thread_list, thread, thread_list);
220 1.1 justin
221 1.1 justin return thread;
222 1.1 justin }
223 1.1 justin
224 1.1 justin static void
225 1.1 justin switch_threads(struct thread *prev, struct thread *next)
226 1.1 justin {
227 1.1 justin int ret;
228 1.1 justin
229 1.1 justin current_thread = next;
230 1.1 justin if (scheduler_hook)
231 1.1 justin scheduler_hook(prev->cookie, next->cookie);
232 1.1 justin ret = swapcontext(&prev->ctx, &next->ctx);
233 1.1 justin if (ret < 0) {
234 1.1 justin printk("swapcontext failed\n");
235 1.1 justin abort();
236 1.1 justin }
237 1.1 justin }
238 1.1 justin
239 1.1 justin struct join_waiter {
240 1.1 justin struct thread *jw_thread;
241 1.1 justin struct thread *jw_wanted;
242 1.1 justin TAILQ_ENTRY(join_waiter) jw_entries;
243 1.1 justin };
244 1.1 justin static TAILQ_HEAD(, join_waiter) joinwq = TAILQ_HEAD_INITIALIZER(joinwq);
245 1.1 justin
246 1.1 justin void
247 1.1 justin exit_thread(void)
248 1.1 justin {
249 1.1 justin struct thread *thread = get_current();
250 1.1 justin struct join_waiter *jw_iter;
251 1.1 justin
252 1.1 justin /* if joinable, gate until we are allowed to exit */
253 1.1 justin while (thread->flags & THREAD_MUSTJOIN) {
254 1.1 justin thread->flags |= THREAD_JOINED;
255 1.1 justin
256 1.1 justin /* see if the joiner is already there */
257 1.1 justin TAILQ_FOREACH(jw_iter, &joinwq, jw_entries) {
258 1.1 justin if (jw_iter->jw_wanted == thread) {
259 1.1 justin wake(jw_iter->jw_thread);
260 1.1 justin break;
261 1.1 justin }
262 1.1 justin }
263 1.1 justin block(thread);
264 1.1 justin schedule();
265 1.1 justin }
266 1.1 justin
267 1.1 justin /* Remove from the thread list */
268 1.1 justin TAILQ_REMOVE(&thread_list, thread, thread_list);
269 1.1 justin clear_runnable(thread);
270 1.1 justin /* Put onto exited list */
271 1.1 justin TAILQ_INSERT_HEAD(&exited_threads, thread, thread_list);
272 1.1 justin
273 1.1 justin /* Schedule will free the resources */
274 1.1 justin while (1) {
275 1.1 justin schedule();
276 1.1 justin printk("schedule() returned! Trying again\n");
277 1.1 justin }
278 1.1 justin }
279 1.1 justin
280 1.1 justin static void
281 1.1 justin join_thread(struct thread *joinable)
282 1.1 justin {
283 1.1 justin struct join_waiter jw;
284 1.1 justin struct thread *thread = get_current();
285 1.1 justin
286 1.1 justin assert(joinable->flags & THREAD_MUSTJOIN);
287 1.1 justin
288 1.1 justin /* wait for exiting thread to hit thread_exit() */
289 1.1 justin while (! (joinable->flags & THREAD_JOINED)) {
290 1.1 justin
291 1.1 justin jw.jw_thread = thread;
292 1.1 justin jw.jw_wanted = joinable;
293 1.1 justin TAILQ_INSERT_TAIL(&joinwq, &jw, jw_entries);
294 1.1 justin block(thread);
295 1.1 justin schedule();
296 1.1 justin TAILQ_REMOVE(&joinwq, &jw, jw_entries);
297 1.1 justin }
298 1.1 justin
299 1.1 justin /* signal exiting thread that we have seen it and it may now exit */
300 1.1 justin assert(joinable->flags & THREAD_JOINED);
301 1.1 justin joinable->flags &= ~THREAD_MUSTJOIN;
302 1.1 justin
303 1.1 justin wake(joinable);
304 1.1 justin }
305 1.1 justin
306 1.1 justin static void msleep(uint64_t millisecs)
307 1.1 justin {
308 1.1 justin struct thread *thread = get_current();
309 1.1 justin
310 1.1 justin thread->wakeup_time = now() + millisecs;
311 1.1 justin clear_runnable(thread);
312 1.1 justin schedule();
313 1.1 justin }
314 1.1 justin
315 1.1 justin static void abssleep(uint64_t millisecs)
316 1.1 justin {
317 1.1 justin struct thread *thread = get_current();
318 1.1 justin
319 1.1 justin thread->wakeup_time = millisecs;
320 1.1 justin clear_runnable(thread);
321 1.1 justin schedule();
322 1.1 justin }
323 1.1 justin
324 1.1 justin /* like abssleep, except against realtime clock instead of monotonic clock */
325 1.1 justin int abssleep_real(uint64_t millisecs)
326 1.1 justin {
327 1.1 justin struct thread *thread = get_current();
328 1.1 justin struct timespec ts;
329 1.1 justin uint64_t real_now;
330 1.1 justin int rv;
331 1.1 justin
332 1.1 justin clock_gettime(CLOCK_REALTIME, &ts);
333 1.1 justin real_now = 1000*ts.tv_sec + ts.tv_nsec/(1000*1000);
334 1.1 justin thread->wakeup_time = now() + (millisecs - real_now);
335 1.1 justin
336 1.1 justin clear_runnable(thread);
337 1.1 justin schedule();
338 1.1 justin
339 1.1 justin rv = !!(thread->flags & THREAD_TIMEDOUT);
340 1.1 justin thread->flags &= ~THREAD_TIMEDOUT;
341 1.1 justin return rv;
342 1.1 justin }
343 1.1 justin
344 1.1 justin void wake(struct thread *thread)
345 1.1 justin {
346 1.1 justin
347 1.1 justin thread->wakeup_time = -1;
348 1.1 justin set_runnable(thread);
349 1.1 justin }
350 1.1 justin
351 1.1 justin void block(struct thread *thread)
352 1.1 justin {
353 1.1 justin
354 1.1 justin thread->wakeup_time = -1;
355 1.1 justin clear_runnable(thread);
356 1.1 justin }
357 1.1 justin
358 1.1 justin int is_runnable(struct thread *thread)
359 1.1 justin {
360 1.1 justin
361 1.1 justin return thread->flags & RUNNABLE_FLAG;
362 1.1 justin }
363 1.1 justin
364 1.1 justin void set_runnable(struct thread *thread)
365 1.1 justin {
366 1.1 justin
367 1.1 justin thread->flags |= RUNNABLE_FLAG;
368 1.1 justin }
369 1.1 justin
370 1.1 justin void clear_runnable(struct thread *thread)
371 1.1 justin {
372 1.1 justin
373 1.1 justin thread->flags &= ~RUNNABLE_FLAG;
374 1.1 justin }
375 1.1 justin
376 1.1 justin static void
377 1.1 justin init_sched(void)
378 1.1 justin {
379 1.1 justin struct thread *thread = calloc(1, sizeof(struct thread));
380 1.1 justin
381 1.1 justin getcontext(&thread->ctx);
382 1.1 justin thread->name = strdup("init");
383 1.1 justin thread->flags = 0;
384 1.1 justin thread->wakeup_time = -1;
385 1.1 justin thread->lwp = NULL;
386 1.1 justin set_runnable(thread);
387 1.1 justin TAILQ_INSERT_TAIL(&thread_list, thread, thread_list);
388 1.1 justin current_thread = thread;
389 1.1 justin }
390 1.1 justin
391 1.1 justin void
392 1.1 justin set_sched_hook(void (*f)(void *, void *))
393 1.1 justin {
394 1.1 justin
395 1.1 justin scheduler_hook = f;
396 1.1 justin }
397 1.1 justin
398 1.1 justin struct thread *
399 1.1 justin init_mainthread(void *cookie)
400 1.1 justin {
401 1.1 justin
402 1.1 justin current_thread->cookie = cookie;
403 1.1 justin return current_thread;
404 1.1 justin }
405 1.1 justin
406 1.1 justin /* rump functions below */
407 1.1 justin
408 1.1 justin struct rumpuser_hyperup rumpuser__hyp;
409 1.1 justin
410 1.1 justin int
411 1.1 justin rumpuser_init(int version, const struct rumpuser_hyperup *hyp)
412 1.1 justin {
413 1.1 justin
414 1.1 justin if (version != RUMPUSER_VERSION) {
415 1.1 justin printk("rumpuser version mismatch\n");
416 1.1 justin return 1;
417 1.1 justin }
418 1.1 justin
419 1.1 justin #ifdef RUMPUSER_USE_DEVRANDOM
420 1.1 justin uint32_t rv;
421 1.1 justin int fd;
422 1.1 justin
423 1.1 justin if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
424 1.1 justin srandom(time(NULL));
425 1.1 justin } else {
426 1.1 justin if (read(fd, &rv, sizeof(rv)) != sizeof(rv))
427 1.1 justin srandom(time(NULL));
428 1.1 justin else
429 1.1 justin srandom(rv);
430 1.1 justin close(fd);
431 1.1 justin }
432 1.1 justin #endif
433 1.1 justin
434 1.1 justin rumpuser__hyp = *hyp;
435 1.1 justin
436 1.1 justin init_sched();
437 1.1 justin
438 1.1 justin return 0;
439 1.1 justin }
440 1.1 justin
441 1.1 justin int
442 1.1 justin rumpuser_clock_gettime(int enum_rumpclock, int64_t *sec, long *nsec)
443 1.1 justin {
444 1.1 justin enum rumpclock rclk = enum_rumpclock;
445 1.1 justin struct timespec ts;
446 1.1 justin clockid_t clk;
447 1.1 justin int rv;
448 1.1 justin
449 1.1 justin switch (rclk) {
450 1.1 justin case RUMPUSER_CLOCK_RELWALL:
451 1.1 justin clk = CLOCK_REALTIME;
452 1.1 justin break;
453 1.1 justin case RUMPUSER_CLOCK_ABSMONO:
454 1.1 justin clk = CLOCK_MONOTONIC;
455 1.1 justin break;
456 1.1 justin default:
457 1.1 justin abort();
458 1.1 justin }
459 1.1 justin
460 1.1 justin if (clock_gettime(clk, &ts) == -1) {
461 1.1 justin rv = errno;
462 1.1 justin } else {
463 1.1 justin *sec = ts.tv_sec;
464 1.1 justin *nsec = ts.tv_nsec;
465 1.1 justin rv = 0;
466 1.1 justin }
467 1.1 justin
468 1.1 justin ET(rv);
469 1.1 justin }
470 1.1 justin
471 1.1 justin int
472 1.1 justin rumpuser_clock_sleep(int enum_rumpclock, int64_t sec, long nsec)
473 1.1 justin {
474 1.1 justin enum rumpclock rclk = enum_rumpclock;
475 1.1 justin uint32_t msec;
476 1.1 justin int nlocks;
477 1.1 justin
478 1.1 justin rumpkern_unsched(&nlocks, NULL);
479 1.1 justin switch (rclk) {
480 1.1 justin case RUMPUSER_CLOCK_RELWALL:
481 1.1 justin msec = sec * 1000 + nsec / (1000*1000UL);
482 1.1 justin msleep(msec);
483 1.1 justin break;
484 1.1 justin case RUMPUSER_CLOCK_ABSMONO:
485 1.1 justin msec = sec * 1000 + nsec / (1000*1000UL);
486 1.1 justin abssleep(msec);
487 1.1 justin break;
488 1.1 justin }
489 1.1 justin rumpkern_sched(nlocks, NULL);
490 1.1 justin
491 1.1 justin return 0;
492 1.1 justin }
493 1.1 justin
494 1.1 justin int
495 1.1 justin rumpuser_getparam(const char *name, void *buf, size_t blen)
496 1.1 justin {
497 1.1 justin int rv;
498 1.1 justin const char *ncpu = "1";
499 1.1 justin
500 1.1 justin if (strcmp(name, RUMPUSER_PARAM_NCPU) == 0) {
501 1.1 justin strncpy(buf, ncpu, blen);
502 1.1 justin rv = 0;
503 1.1 justin } else if (strcmp(name, RUMPUSER_PARAM_HOSTNAME) == 0) {
504 1.1 justin char tmp[MAXHOSTNAMELEN];
505 1.1 justin
506 1.1 justin if (gethostname(tmp, sizeof(tmp)) == -1) {
507 1.1 justin snprintf(buf, blen, "rump-%05d", (int)getpid());
508 1.1 justin } else {
509 1.1 justin snprintf(buf, blen, "rump-%05d.%s",
510 1.1 justin (int)getpid(), tmp);
511 1.1 justin }
512 1.1 justin rv = 0;
513 1.1 justin } else if (*name == '_') {
514 1.1 justin rv = EINVAL;
515 1.1 justin } else {
516 1.1 justin if (getenv_r(name, buf, blen) == -1)
517 1.1 justin rv = errno;
518 1.1 justin else
519 1.1 justin rv = 0;
520 1.1 justin }
521 1.1 justin
522 1.1 justin ET(rv);
523 1.1 justin }
524 1.1 justin
525 1.1 justin void
526 1.1 justin rumpuser_putchar(int c)
527 1.1 justin {
528 1.1 justin
529 1.1 justin putchar(c);
530 1.1 justin }
531 1.1 justin
532 1.1 justin __dead void
533 1.1 justin rumpuser_exit(int rv)
534 1.1 justin {
535 1.1 justin
536 1.1 justin if (rv == RUMPUSER_PANIC)
537 1.1 justin abort();
538 1.1 justin else
539 1.1 justin exit(rv);
540 1.1 justin }
541 1.1 justin
542 1.1 justin void
543 1.1 justin rumpuser_seterrno(int error)
544 1.1 justin {
545 1.1 justin
546 1.1 justin errno = error;
547 1.1 justin }
548 1.1 justin
549 1.1 justin /*
550 1.1 justin * This is meant for safe debugging prints from the kernel.
551 1.1 justin */
552 1.1 justin void
553 1.1 justin rumpuser_dprintf(const char *format, ...)
554 1.1 justin {
555 1.1 justin va_list ap;
556 1.1 justin
557 1.1 justin va_start(ap, format);
558 1.1 justin vfprintf(stderr, format, ap);
559 1.1 justin va_end(ap);
560 1.1 justin }
561 1.1 justin
562 1.1 justin int
563 1.1 justin rumpuser_kill(int64_t pid, int rumpsig)
564 1.1 justin {
565 1.1 justin int sig;
566 1.1 justin
567 1.1 justin sig = rumpuser__sig_rump2host(rumpsig);
568 1.1 justin if (sig > 0)
569 1.1 justin raise(sig);
570 1.1 justin return 0;
571 1.1 justin }
572 1.1 justin
573 1.1 justin int
574 1.1 justin rumpuser_getrandom(void *buf, size_t buflen, int flags, size_t *retp)
575 1.1 justin {
576 1.1 justin size_t origlen = buflen;
577 1.1 justin uint32_t *p = buf;
578 1.1 justin uint32_t tmp;
579 1.1 justin int chunk;
580 1.1 justin
581 1.1 justin do {
582 1.1 justin chunk = buflen < 4 ? buflen : 4; /* portable MIN ... */
583 1.1 justin tmp = RUMPUSER_RANDOM();
584 1.1 justin memcpy(p, &tmp, chunk);
585 1.1 justin p++;
586 1.1 justin buflen -= chunk;
587 1.1 justin } while (chunk);
588 1.1 justin
589 1.1 justin *retp = origlen;
590 1.1 justin ET(0);
591 1.1 justin }
592 1.1 justin
593 1.1 justin /* thread functions */
594 1.1 justin
595 1.1 justin TAILQ_HEAD(waithead, waiter);
596 1.1 justin struct waiter {
597 1.1 justin struct thread *who;
598 1.1 justin TAILQ_ENTRY(waiter) entries;
599 1.1 justin int onlist;
600 1.1 justin };
601 1.1 justin
602 1.1 justin static int
603 1.1 justin wait(struct waithead *wh, uint64_t msec)
604 1.1 justin {
605 1.1 justin struct waiter w;
606 1.1 justin
607 1.1 justin w.who = get_current();
608 1.1 justin TAILQ_INSERT_TAIL(wh, &w, entries);
609 1.1 justin w.onlist = 1;
610 1.1 justin block(w.who);
611 1.1 justin if (msec)
612 1.1 justin w.who->wakeup_time = now() + msec;
613 1.1 justin schedule();
614 1.1 justin
615 1.1 justin /* woken up by timeout? */
616 1.1 justin if (w.onlist)
617 1.1 justin TAILQ_REMOVE(wh, &w, entries);
618 1.1 justin
619 1.1 justin return w.onlist ? ETIMEDOUT : 0;
620 1.1 justin }
621 1.1 justin
622 1.1 justin static void
623 1.1 justin wakeup_one(struct waithead *wh)
624 1.1 justin {
625 1.1 justin struct waiter *w;
626 1.1 justin
627 1.1 justin if ((w = TAILQ_FIRST(wh)) != NULL) {
628 1.1 justin TAILQ_REMOVE(wh, w, entries);
629 1.1 justin w->onlist = 0;
630 1.1 justin wake(w->who);
631 1.1 justin }
632 1.1 justin }
633 1.1 justin
634 1.1 justin static void
635 1.1 justin wakeup_all(struct waithead *wh)
636 1.1 justin {
637 1.1 justin struct waiter *w;
638 1.1 justin
639 1.1 justin while ((w = TAILQ_FIRST(wh)) != NULL) {
640 1.1 justin TAILQ_REMOVE(wh, w, entries);
641 1.1 justin w->onlist = 0;
642 1.1 justin wake(w->who);
643 1.1 justin }
644 1.1 justin }
645 1.1 justin
646 1.1 justin int
647 1.1 justin rumpuser_thread_create(void *(*f)(void *), void *arg, const char *thrname,
648 1.1 justin int joinable, int pri, int cpuidx, void **tptr)
649 1.1 justin {
650 1.1 justin struct thread *thr;
651 1.1 justin
652 1.1 justin thr = create_thread(thrname, NULL, (void (*)(void *))f, arg, NULL, 0);
653 1.1 justin /*
654 1.1 justin * XXX: should be supplied as a flag to create_thread() so as to
655 1.1 justin * _ensure_ it's set before the thread runs (and could exit).
656 1.1 justin * now we're trusting unclear semantics of create_thread()
657 1.1 justin */
658 1.1 justin if (thr && joinable)
659 1.1 justin thr->flags |= THREAD_MUSTJOIN;
660 1.1 justin
661 1.1 justin if (!thr)
662 1.1 justin return EINVAL;
663 1.1 justin
664 1.1 justin *tptr = thr;
665 1.1 justin return 0;
666 1.1 justin }
667 1.1 justin
668 1.1 justin void
669 1.1 justin rumpuser_thread_exit(void)
670 1.1 justin {
671 1.1 justin
672 1.1 justin exit_thread();
673 1.1 justin }
674 1.1 justin
675 1.1 justin int
676 1.1 justin rumpuser_thread_join(void *p)
677 1.1 justin {
678 1.1 justin
679 1.1 justin join_thread(p);
680 1.1 justin return 0;
681 1.1 justin }
682 1.1 justin
683 1.1 justin struct rumpuser_mtx {
684 1.1 justin struct waithead waiters;
685 1.1 justin int v;
686 1.1 justin int flags;
687 1.1 justin struct lwp *o;
688 1.1 justin };
689 1.1 justin
690 1.1 justin void
691 1.1 justin rumpuser_mutex_init(struct rumpuser_mtx **mtxp, int flags)
692 1.1 justin {
693 1.1 justin struct rumpuser_mtx *mtx;
694 1.1 justin
695 1.1 justin mtx = malloc(sizeof(*mtx));
696 1.1 justin memset(mtx, 0, sizeof(*mtx));
697 1.1 justin mtx->flags = flags;
698 1.1 justin TAILQ_INIT(&mtx->waiters);
699 1.1 justin *mtxp = mtx;
700 1.1 justin }
701 1.1 justin
702 1.1 justin void
703 1.1 justin rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
704 1.1 justin {
705 1.1 justin int nlocks;
706 1.1 justin
707 1.1 justin if (rumpuser_mutex_tryenter(mtx) != 0) {
708 1.1 justin rumpkern_unsched(&nlocks, NULL);
709 1.1 justin while (rumpuser_mutex_tryenter(mtx) != 0)
710 1.1 justin wait(&mtx->waiters, 0);
711 1.1 justin rumpkern_sched(nlocks, NULL);
712 1.1 justin }
713 1.1 justin }
714 1.1 justin
715 1.1 justin void
716 1.1 justin rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx)
717 1.1 justin {
718 1.1 justin int rv;
719 1.1 justin
720 1.1 justin rv = rumpuser_mutex_tryenter(mtx);
721 1.1 justin /* one VCPU supported, no preemption => must succeed */
722 1.1 justin if (rv != 0) {
723 1.1 justin printk("no voi ei\n");
724 1.1 justin }
725 1.1 justin }
726 1.1 justin
727 1.1 justin int
728 1.1 justin rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
729 1.1 justin {
730 1.1 justin struct lwp *l = get_current()->lwp;
731 1.1 justin
732 1.1 justin if (mtx->v && mtx->o != l)
733 1.1 justin return EBUSY;
734 1.1 justin
735 1.1 justin mtx->v++;
736 1.1 justin mtx->o = l;
737 1.1 justin
738 1.1 justin return 0;
739 1.1 justin }
740 1.1 justin
741 1.1 justin void
742 1.1 justin rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
743 1.1 justin {
744 1.1 justin
745 1.1 justin assert(mtx->v > 0);
746 1.1 justin if (--mtx->v == 0) {
747 1.1 justin mtx->o = NULL;
748 1.1 justin wakeup_one(&mtx->waiters);
749 1.1 justin }
750 1.1 justin }
751 1.1 justin
752 1.1 justin void
753 1.1 justin rumpuser_mutex_destroy(struct rumpuser_mtx *mtx)
754 1.1 justin {
755 1.1 justin
756 1.1 justin assert(TAILQ_EMPTY(&mtx->waiters) && mtx->o == NULL);
757 1.1 justin free(mtx);
758 1.1 justin }
759 1.1 justin
760 1.1 justin void
761 1.1 justin rumpuser_mutex_owner(struct rumpuser_mtx *mtx, struct lwp **lp)
762 1.1 justin {
763 1.1 justin
764 1.1 justin *lp = mtx->o;
765 1.1 justin }
766 1.1 justin
767 1.1 justin struct rumpuser_rw {
768 1.1 justin struct waithead rwait;
769 1.1 justin struct waithead wwait;
770 1.1 justin int v;
771 1.1 justin struct lwp *o;
772 1.1 justin };
773 1.1 justin
774 1.1 justin void
775 1.1 justin rumpuser_rw_init(struct rumpuser_rw **rwp)
776 1.1 justin {
777 1.1 justin struct rumpuser_rw *rw;
778 1.1 justin
779 1.1 justin rw = malloc(sizeof(*rw));
780 1.1 justin memset(rw, 0, sizeof(*rw));
781 1.1 justin TAILQ_INIT(&rw->rwait);
782 1.1 justin TAILQ_INIT(&rw->wwait);
783 1.1 justin
784 1.1 justin *rwp = rw;
785 1.1 justin }
786 1.1 justin
787 1.1 justin void
788 1.1 justin rumpuser_rw_enter(int enum_rumprwlock, struct rumpuser_rw *rw)
789 1.1 justin {
790 1.1 justin enum rumprwlock lk = enum_rumprwlock;
791 1.1 justin struct waithead *w = NULL;
792 1.1 justin int nlocks;
793 1.1 justin
794 1.1 justin switch (lk) {
795 1.1 justin case RUMPUSER_RW_WRITER:
796 1.1 justin w = &rw->wwait;
797 1.1 justin break;
798 1.1 justin case RUMPUSER_RW_READER:
799 1.1 justin w = &rw->rwait;
800 1.1 justin break;
801 1.1 justin }
802 1.1 justin
803 1.1 justin if (rumpuser_rw_tryenter(enum_rumprwlock, rw) != 0) {
804 1.1 justin rumpkern_unsched(&nlocks, NULL);
805 1.1 justin while (rumpuser_rw_tryenter(enum_rumprwlock, rw) != 0)
806 1.1 justin wait(w, 0);
807 1.1 justin rumpkern_sched(nlocks, NULL);
808 1.1 justin }
809 1.1 justin }
810 1.1 justin
811 1.1 justin int
812 1.1 justin rumpuser_rw_tryenter(int enum_rumprwlock, struct rumpuser_rw *rw)
813 1.1 justin {
814 1.1 justin enum rumprwlock lk = enum_rumprwlock;
815 1.1 justin int rv;
816 1.1 justin
817 1.1 justin switch (lk) {
818 1.1 justin case RUMPUSER_RW_WRITER:
819 1.1 justin if (rw->o == NULL) {
820 1.1 justin rw->o = rumpuser_curlwp();
821 1.1 justin rv = 0;
822 1.1 justin } else {
823 1.1 justin rv = EBUSY;
824 1.1 justin }
825 1.1 justin break;
826 1.1 justin case RUMPUSER_RW_READER:
827 1.1 justin if (rw->o == NULL && TAILQ_EMPTY(&rw->wwait)) {
828 1.1 justin rw->v++;
829 1.1 justin rv = 0;
830 1.1 justin } else {
831 1.1 justin rv = EBUSY;
832 1.1 justin }
833 1.1 justin break;
834 1.1 justin default:
835 1.1 justin rv = EINVAL;
836 1.1 justin }
837 1.1 justin
838 1.1 justin return rv;
839 1.1 justin }
840 1.1 justin
841 1.1 justin void
842 1.1 justin rumpuser_rw_exit(struct rumpuser_rw *rw)
843 1.1 justin {
844 1.1 justin
845 1.1 justin if (rw->o) {
846 1.1 justin rw->o = NULL;
847 1.1 justin } else {
848 1.1 justin rw->v--;
849 1.1 justin }
850 1.1 justin
851 1.1 justin /* standard procedure, don't let readers starve out writers */
852 1.1 justin if (!TAILQ_EMPTY(&rw->wwait)) {
853 1.1 justin if (rw->o == NULL)
854 1.1 justin wakeup_one(&rw->wwait);
855 1.1 justin } else if (!TAILQ_EMPTY(&rw->rwait) && rw->o == NULL) {
856 1.1 justin wakeup_all(&rw->rwait);
857 1.1 justin }
858 1.1 justin }
859 1.1 justin
860 1.1 justin void
861 1.1 justin rumpuser_rw_destroy(struct rumpuser_rw *rw)
862 1.1 justin {
863 1.1 justin
864 1.1 justin free(rw);
865 1.1 justin }
866 1.1 justin
867 1.1 justin void
868 1.1 justin rumpuser_rw_held(int enum_rumprwlock, struct rumpuser_rw *rw, int *rvp)
869 1.1 justin {
870 1.1 justin enum rumprwlock lk = enum_rumprwlock;
871 1.1 justin
872 1.1 justin switch (lk) {
873 1.1 justin case RUMPUSER_RW_WRITER:
874 1.1 justin *rvp = rw->o == rumpuser_curlwp();
875 1.1 justin break;
876 1.1 justin case RUMPUSER_RW_READER:
877 1.1 justin *rvp = rw->v > 0;
878 1.1 justin break;
879 1.1 justin }
880 1.1 justin }
881 1.1 justin
882 1.1 justin void
883 1.1 justin rumpuser_rw_downgrade(struct rumpuser_rw *rw)
884 1.1 justin {
885 1.1 justin
886 1.1 justin assert(rw->o == rumpuser_curlwp());
887 1.1 justin rw->v = -1;
888 1.1 justin }
889 1.1 justin
890 1.1 justin int
891 1.1 justin rumpuser_rw_tryupgrade(struct rumpuser_rw *rw)
892 1.1 justin {
893 1.1 justin
894 1.1 justin if (rw->v == -1) {
895 1.1 justin rw->v = 1;
896 1.1 justin rw->o = rumpuser_curlwp();
897 1.1 justin return 0;
898 1.1 justin }
899 1.1 justin
900 1.1 justin return EBUSY;
901 1.1 justin }
902 1.1 justin
903 1.1 justin struct rumpuser_cv {
904 1.1 justin struct waithead waiters;
905 1.1 justin int nwaiters;
906 1.1 justin };
907 1.1 justin
908 1.1 justin void
909 1.1 justin rumpuser_cv_init(struct rumpuser_cv **cvp)
910 1.1 justin {
911 1.1 justin struct rumpuser_cv *cv;
912 1.1 justin
913 1.1 justin cv = malloc(sizeof(*cv));
914 1.1 justin memset(cv, 0, sizeof(*cv));
915 1.1 justin TAILQ_INIT(&cv->waiters);
916 1.1 justin *cvp = cv;
917 1.1 justin }
918 1.1 justin
919 1.1 justin void
920 1.1 justin rumpuser_cv_destroy(struct rumpuser_cv *cv)
921 1.1 justin {
922 1.1 justin
923 1.1 justin assert(cv->nwaiters == 0);
924 1.1 justin free(cv);
925 1.1 justin }
926 1.1 justin
927 1.1 justin static void
928 1.1 justin cv_unsched(struct rumpuser_mtx *mtx, int *nlocks)
929 1.1 justin {
930 1.1 justin
931 1.1 justin rumpkern_unsched(nlocks, mtx);
932 1.1 justin rumpuser_mutex_exit(mtx);
933 1.1 justin }
934 1.1 justin
935 1.1 justin static void
936 1.1 justin cv_resched(struct rumpuser_mtx *mtx, int nlocks)
937 1.1 justin {
938 1.1 justin
939 1.1 justin /* see rumpuser(3) */
940 1.1 justin if ((mtx->flags & (RUMPUSER_MTX_KMUTEX | RUMPUSER_MTX_SPIN)) ==
941 1.1 justin (RUMPUSER_MTX_KMUTEX | RUMPUSER_MTX_SPIN)) {
942 1.1 justin rumpkern_sched(nlocks, mtx);
943 1.1 justin rumpuser_mutex_enter_nowrap(mtx);
944 1.1 justin } else {
945 1.1 justin rumpuser_mutex_enter_nowrap(mtx);
946 1.1 justin rumpkern_sched(nlocks, mtx);
947 1.1 justin }
948 1.1 justin }
949 1.1 justin
950 1.1 justin void
951 1.1 justin rumpuser_cv_wait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
952 1.1 justin {
953 1.1 justin int nlocks;
954 1.1 justin
955 1.1 justin cv->nwaiters++;
956 1.1 justin cv_unsched(mtx, &nlocks);
957 1.1 justin wait(&cv->waiters, 0);
958 1.1 justin cv_resched(mtx, nlocks);
959 1.1 justin cv->nwaiters--;
960 1.1 justin }
961 1.1 justin
962 1.1 justin void
963 1.1 justin rumpuser_cv_wait_nowrap(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
964 1.1 justin {
965 1.1 justin
966 1.1 justin cv->nwaiters++;
967 1.1 justin rumpuser_mutex_exit(mtx);
968 1.1 justin wait(&cv->waiters, 0);
969 1.1 justin rumpuser_mutex_enter_nowrap(mtx);
970 1.1 justin cv->nwaiters--;
971 1.1 justin }
972 1.1 justin
973 1.1 justin int
974 1.1 justin rumpuser_cv_timedwait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx,
975 1.1 justin int64_t sec, int64_t nsec)
976 1.1 justin {
977 1.1 justin int nlocks;
978 1.1 justin int rv;
979 1.1 justin
980 1.1 justin cv->nwaiters++;
981 1.1 justin cv_unsched(mtx, &nlocks);
982 1.1 justin rv = wait(&cv->waiters, sec * 1000 + nsec / (1000*1000));
983 1.1 justin cv_resched(mtx, nlocks);
984 1.1 justin cv->nwaiters--;
985 1.1 justin
986 1.1 justin return rv;
987 1.1 justin }
988 1.1 justin
989 1.1 justin void
990 1.1 justin rumpuser_cv_signal(struct rumpuser_cv *cv)
991 1.1 justin {
992 1.1 justin
993 1.1 justin wakeup_one(&cv->waiters);
994 1.1 justin }
995 1.1 justin
996 1.1 justin void
997 1.1 justin rumpuser_cv_broadcast(struct rumpuser_cv *cv)
998 1.1 justin {
999 1.1 justin
1000 1.1 justin wakeup_all(&cv->waiters);
1001 1.1 justin }
1002 1.1 justin
1003 1.1 justin void
1004 1.1 justin rumpuser_cv_has_waiters(struct rumpuser_cv *cv, int *rvp)
1005 1.1 justin {
1006 1.1 justin
1007 1.1 justin *rvp = cv->nwaiters != 0;
1008 1.1 justin }
1009 1.1 justin
1010 1.1 justin /*
1011 1.1 justin * curlwp
1012 1.1 justin */
1013 1.1 justin
1014 1.1 justin void
1015 1.1 justin rumpuser_curlwpop(int enum_rumplwpop, struct lwp *l)
1016 1.1 justin {
1017 1.1 justin struct thread *thread;
1018 1.1 justin enum rumplwpop op = enum_rumplwpop;
1019 1.1 justin
1020 1.1 justin switch (op) {
1021 1.1 justin case RUMPUSER_LWP_CREATE:
1022 1.1 justin case RUMPUSER_LWP_DESTROY:
1023 1.1 justin break;
1024 1.1 justin case RUMPUSER_LWP_SET:
1025 1.1 justin thread = get_current();
1026 1.1 justin thread->lwp = l;
1027 1.1 justin break;
1028 1.1 justin case RUMPUSER_LWP_CLEAR:
1029 1.1 justin thread = get_current();
1030 1.1 justin assert(thread->lwp == l);
1031 1.1 justin thread->lwp = NULL;
1032 1.1 justin break;
1033 1.1 justin }
1034 1.1 justin }
1035 1.1 justin
1036 1.1 justin struct lwp *
1037 1.1 justin rumpuser_curlwp(void)
1038 1.1 justin {
1039 1.1 justin
1040 1.1 justin return get_current()->lwp;
1041 1.1 justin }
1042