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