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