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