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