Module Name: src
Committed By: justin
Date: Fri Jul 11 20:26:31 UTC 2014
Modified Files:
src/lib/librumpuser: Makefile
src/sys/rump: README.compileopts
Added Files:
src/lib/librumpuser: rumpfiber.c rumpfiber.h rumpfiber_bio.c
rumpfiber_sp.c
Log Message:
Add a fiber based implementation of librumpuser in addition to the
default pthreads based version.
Discussed with pooka@
To generate a diff of this commit:
cvs rdiff -u -r1.19 -r1.20 src/lib/librumpuser/Makefile
cvs rdiff -u -r0 -r1.1 src/lib/librumpuser/rumpfiber.c \
src/lib/librumpuser/rumpfiber.h src/lib/librumpuser/rumpfiber_bio.c \
src/lib/librumpuser/rumpfiber_sp.c
cvs rdiff -u -r1.5 -r1.6 src/sys/rump/README.compileopts
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/lib/librumpuser/Makefile
diff -u src/lib/librumpuser/Makefile:1.19 src/lib/librumpuser/Makefile:1.20
--- src/lib/librumpuser/Makefile:1.19 Wed Jul 9 23:41:40 2014
+++ src/lib/librumpuser/Makefile Fri Jul 11 20:26:31 2014
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.19 2014/07/09 23:41:40 justin Exp $
+# $NetBSD: Makefile,v 1.20 2014/07/11 20:26:31 justin Exp $
#
.include <bsd.own.mk>
@@ -8,8 +8,13 @@ WARNS?= 5
# rumpuser.h is in sys/rump for inclusion by kernel components
.PATH: ${.CURDIR}/../../sys/rump/include/rump
+RUMPUSER_THREADS?=pthread
+
LIB= rumpuser
+
+.if ${RUMPUSER_THREADS} == "pthread"
LIBDPLIBS+= pthread ${.CURDIR}/../libpthread
+.endif
.for lib in ${RUMPUSER_EXTERNAL_DPLIBS}
LIBDO.${lib}= _external
LIBDPLIBS+= ${lib} lib
@@ -17,15 +22,32 @@ LIBDPLIBS+= ${lib} lib
CPPFLAGS+= -DLIBRUMPUSER
#CPPFLAGS+= -D_DIAGNOSTIC
-SRCS= rumpuser.c
-SRCS+= rumpuser_pth.c
-SRCS+= rumpuser_component.c rumpuser_bio.c
+
+.if ${RUMPUSER_THREADS} == "pthread"
+SRCS= rumpuser.c rumpuser_pth.c rumpuser_bio.c
+SRCS+= rumpuser_sp.c
+.elif ${RUMPUSER_THREADS} == "none"
+SRCS= rumpuser.c rumpuser_pth_dummy.c rumpuser_bio.c
+SRCS+= rumpuser_sp.c
+.elif ${RUMPUSER_THREADS} == "fiber"
+.if defined(RUMP_CURLWP)
+.if ${RUMP_CURLWP} != "hypercall"
+.error Unsupported curlwp scheme for thread model: ${RUMP_CURLWP}
+.endif
+.endif
+SRCS= rumpfiber.c rumpfiber_bio.c
+SRCS+= rumpfiber_sp.c
+.else
+.error Unsupported rumpuser threading type: ${RUMPUSER_THREADS}
+.endif
+
+SRCS+= rumpuser_component.c
SRCS+= rumpuser_file.c rumpuser_mem.c
SRCS+= rumpuser_errtrans.c rumpuser_sigtrans.c
# optional
-SRCS+= rumpuser_dl.c rumpuser_sp.c rumpuser_daemonize.c
+SRCS+= rumpuser_dl.c rumpuser_daemonize.c
INCSDIR= /usr/include/rump
INCS= rumpuser.h rumpuser_component.h rumpuser_port.h
@@ -34,5 +56,4 @@ MAN= rumpuser.3
CPPFLAGS+= -D_REENTRANT
-
.include <bsd.lib.mk>
Index: src/sys/rump/README.compileopts
diff -u src/sys/rump/README.compileopts:1.5 src/sys/rump/README.compileopts:1.6
--- src/sys/rump/README.compileopts:1.5 Fri Jun 20 12:36:31 2014
+++ src/sys/rump/README.compileopts Fri Jul 11 20:26:31 2014
@@ -1,4 +1,4 @@
- $NetBSD: README.compileopts,v 1.5 2014/06/20 12:36:31 pooka Exp $
+ $NetBSD: README.compileopts,v 1.6 2014/07/11 20:26:31 justin Exp $
This file describes compile-time options for rump kernels. Additionally,
NetBSD build options will have an effect. See src/share/mk/bsd.README
@@ -79,6 +79,22 @@ effect: Control how curlwp is obtained i
================================================================================
+Rumpuser options:
+
+ RUMPUSER_THREADS
+
+values: pthread/none/fiber or <undefined>
+defval: <undefined>
+effect: Define the way threading is implemented in the rumpuser hypercall
+ implmentation.
+ <undefined> - use default implementation (currently "pthread")
+ pthread - use pthreads to implement threading
+ none - do not support kernel threads at all
+ fiber - user a fiber interface, cooperatively scheduled contexts
+
+
+================================================================================
+
Per-component options:
RUMP_SYM_NORENAME
Added files:
Index: src/lib/librumpuser/rumpfiber.c
diff -u /dev/null src/lib/librumpuser/rumpfiber.c:1.1
--- /dev/null Fri Jul 11 20:26:31 2014
+++ src/lib/librumpuser/rumpfiber.c Fri Jul 11 20:26:31 2014
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (c) 2007-2013 Antti Kantee. All Rights Reserved.
+ * Copyright (c) 2014 Justin Cormack. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Based partly on code from Xen Minios with the following license */
+
+/*
+ ****************************************************************************
+ * (C) 2005 - Grzegorz Milos - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: sched.c
+ * Author: Grzegorz Milos
+ * Changes: Robert Kaiser
+ *
+ * Date: Aug 2005
+ *
+ * Environment: Xen Minimal OS
+ * Description: simple scheduler for Mini-Os
+ *
+ * The scheduler is non-preemptive (cooperative), and schedules according
+ * to Round Robin algorithm.
+ *
+ ****************************************************************************
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "rumpuser_port.h"
+
+#if !defined(lint)
+__RCSID("$NetBSD: rumpfiber.c,v 1.1 2014/07/11 20:26:31 justin Exp $");
+#endif /* !lint */
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rump/rumpuser.h>
+
+#include "rumpuser_int.h"
+#include "rumpfiber.h"
+
+static void init_sched(void);
+static void join_thread(struct thread *);
+static void switch_threads(struct thread *prev, struct thread *next);
+static struct thread *get_current(void);
+static int64_t now(void);
+static void msleep(uint64_t millisecs);
+static void abssleep(uint64_t millisecs);
+
+TAILQ_HEAD(thread_list, thread);
+
+static struct thread_list exited_threads = TAILQ_HEAD_INITIALIZER(exited_threads);
+static struct thread_list thread_list = TAILQ_HEAD_INITIALIZER(thread_list);
+static struct thread *current_thread = NULL;
+
+static void (*scheduler_hook)(void *, void *);
+
+static struct thread *
+get_current(void)
+{
+
+ return current_thread;
+}
+
+static int64_t
+now(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000LL);
+}
+
+void
+schedule(void)
+{
+ struct thread *prev, *next, *thread, *tmp;
+ int64_t tm, wakeup;
+ struct timespec sl;
+
+ prev = get_current();
+
+ do {
+ tm = now();
+ wakeup = tm + 1000; /* wake up in 1s max */
+ next = NULL;
+ TAILQ_FOREACH_SAFE(thread, &thread_list, thread_list, tmp) {
+ if (!is_runnable(thread) && thread->wakeup_time >= 0) {
+ if (thread->wakeup_time <= tm) {
+ thread->flags |= THREAD_TIMEDOUT;
+ wake(thread);
+ } else if (thread->wakeup_time < wakeup)
+ wakeup = thread->wakeup_time;
+ }
+ if (is_runnable(thread)) {
+ next = thread;
+ /* Put this thread on the end of the list */
+ TAILQ_REMOVE(&thread_list, thread, thread_list);
+ TAILQ_INSERT_TAIL(&thread_list, thread, thread_list);
+ break;
+ }
+ }
+ if (next)
+ break;
+ sl.tv_sec = (wakeup - tm) / 1000;
+ sl.tv_nsec = ((wakeup - tm) - 1000 * sl.tv_sec) * 1000000;
+#ifdef HAVE_CLOCK_NANOSLEEP
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &sl, NULL);
+#else
+ nanosleep(&sl, NULL);
+#endif
+ } while (1);
+
+ if (prev != next)
+ switch_threads(prev, next);
+
+ TAILQ_FOREACH_SAFE(thread, &exited_threads, thread_list, tmp) {
+ if (thread != prev) {
+ TAILQ_REMOVE(&exited_threads, thread, thread_list);
+ if ((thread->flags & THREAD_EXTSTACK) == 0)
+ munmap(thread->ctx.uc_stack.ss_sp, STACKSIZE);
+ free(thread->name);
+ free(thread);
+ }
+ }
+}
+
+static void
+create_ctx(ucontext_t *ctx, void *stack, size_t stack_size)
+{
+
+ getcontext(ctx);
+ ctx->uc_stack.ss_sp = stack;
+ ctx->uc_stack.ss_size = stack_size;
+ ctx->uc_stack.ss_flags = 0;
+ ctx->uc_link = NULL; /* TODO may link to main thread */
+}
+
+/* may have to do bounce function to call, if args to makecontext are ints */
+/* TODO see notes in rumpuser_thread_create, have flags here */
+struct thread *
+create_thread(const char *name, void *cookie, void (*f)(void *), void *data,
+ void *stack, size_t stack_size)
+{
+ struct thread *thread = calloc(1, sizeof(struct thread));
+
+ if (!stack) {
+ assert(stack_size == 0);
+ stack = mmap(NULL, STACKSIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ if (stack == MAP_FAILED) {
+ return NULL;
+ }
+ stack_size = STACKSIZE;
+ } else {
+ thread->flags = THREAD_EXTSTACK;
+ }
+ create_ctx(&thread->ctx, stack, stack_size);
+ makecontext(&thread->ctx, (void (*)(void))f, 1, data);
+
+ thread->name = strdup(name);
+ thread->cookie = cookie;
+
+ /* Not runnable, not exited, not sleeping */
+ thread->wakeup_time = -1;
+ thread->lwp = NULL;
+ set_runnable(thread);
+ TAILQ_INSERT_TAIL(&thread_list, thread, thread_list);
+
+ return thread;
+}
+
+static void
+switch_threads(struct thread *prev, struct thread *next)
+{
+ int ret;
+
+ current_thread = next;
+ if (scheduler_hook)
+ scheduler_hook(prev->cookie, next->cookie);
+ ret = swapcontext(&prev->ctx, &next->ctx);
+ if (ret < 0) {
+ printk("swapcontext failed\n");
+ abort();
+ }
+}
+
+struct join_waiter {
+ struct thread *jw_thread;
+ struct thread *jw_wanted;
+ TAILQ_ENTRY(join_waiter) jw_entries;
+};
+static TAILQ_HEAD(, join_waiter) joinwq = TAILQ_HEAD_INITIALIZER(joinwq);
+
+void
+exit_thread(void)
+{
+ struct thread *thread = get_current();
+ struct join_waiter *jw_iter;
+
+ /* if joinable, gate until we are allowed to exit */
+ while (thread->flags & THREAD_MUSTJOIN) {
+ thread->flags |= THREAD_JOINED;
+
+ /* see if the joiner is already there */
+ TAILQ_FOREACH(jw_iter, &joinwq, jw_entries) {
+ if (jw_iter->jw_wanted == thread) {
+ wake(jw_iter->jw_thread);
+ break;
+ }
+ }
+ block(thread);
+ schedule();
+ }
+
+ /* Remove from the thread list */
+ TAILQ_REMOVE(&thread_list, thread, thread_list);
+ clear_runnable(thread);
+ /* Put onto exited list */
+ TAILQ_INSERT_HEAD(&exited_threads, thread, thread_list);
+
+ /* Schedule will free the resources */
+ while (1) {
+ schedule();
+ printk("schedule() returned! Trying again\n");
+ }
+}
+
+static void
+join_thread(struct thread *joinable)
+{
+ struct join_waiter jw;
+ struct thread *thread = get_current();
+
+ assert(joinable->flags & THREAD_MUSTJOIN);
+
+ /* wait for exiting thread to hit thread_exit() */
+ while (! (joinable->flags & THREAD_JOINED)) {
+
+ jw.jw_thread = thread;
+ jw.jw_wanted = joinable;
+ TAILQ_INSERT_TAIL(&joinwq, &jw, jw_entries);
+ block(thread);
+ schedule();
+ TAILQ_REMOVE(&joinwq, &jw, jw_entries);
+ }
+
+ /* signal exiting thread that we have seen it and it may now exit */
+ assert(joinable->flags & THREAD_JOINED);
+ joinable->flags &= ~THREAD_MUSTJOIN;
+
+ wake(joinable);
+}
+
+static void msleep(uint64_t millisecs)
+{
+ struct thread *thread = get_current();
+
+ thread->wakeup_time = now() + millisecs;
+ clear_runnable(thread);
+ schedule();
+}
+
+static void abssleep(uint64_t millisecs)
+{
+ struct thread *thread = get_current();
+
+ thread->wakeup_time = millisecs;
+ clear_runnable(thread);
+ schedule();
+}
+
+/* like abssleep, except against realtime clock instead of monotonic clock */
+int abssleep_real(uint64_t millisecs)
+{
+ struct thread *thread = get_current();
+ struct timespec ts;
+ uint64_t real_now;
+ int rv;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ real_now = 1000*ts.tv_sec + ts.tv_nsec/(1000*1000);
+ thread->wakeup_time = now() + (millisecs - real_now);
+
+ clear_runnable(thread);
+ schedule();
+
+ rv = !!(thread->flags & THREAD_TIMEDOUT);
+ thread->flags &= ~THREAD_TIMEDOUT;
+ return rv;
+}
+
+void wake(struct thread *thread)
+{
+
+ thread->wakeup_time = -1;
+ set_runnable(thread);
+}
+
+void block(struct thread *thread)
+{
+
+ thread->wakeup_time = -1;
+ clear_runnable(thread);
+}
+
+int is_runnable(struct thread *thread)
+{
+
+ return thread->flags & RUNNABLE_FLAG;
+}
+
+void set_runnable(struct thread *thread)
+{
+
+ thread->flags |= RUNNABLE_FLAG;
+}
+
+void clear_runnable(struct thread *thread)
+{
+
+ thread->flags &= ~RUNNABLE_FLAG;
+}
+
+static void
+init_sched(void)
+{
+ struct thread *thread = calloc(1, sizeof(struct thread));
+
+ getcontext(&thread->ctx);
+ thread->name = strdup("init");
+ thread->flags = 0;
+ thread->wakeup_time = -1;
+ thread->lwp = NULL;
+ set_runnable(thread);
+ TAILQ_INSERT_TAIL(&thread_list, thread, thread_list);
+ current_thread = thread;
+}
+
+void
+set_sched_hook(void (*f)(void *, void *))
+{
+
+ scheduler_hook = f;
+}
+
+struct thread *
+init_mainthread(void *cookie)
+{
+
+ current_thread->cookie = cookie;
+ return current_thread;
+}
+
+/* rump functions below */
+
+struct rumpuser_hyperup rumpuser__hyp;
+
+int
+rumpuser_init(int version, const struct rumpuser_hyperup *hyp)
+{
+
+ if (version != RUMPUSER_VERSION) {
+ printk("rumpuser version mismatch\n");
+ return 1;
+ }
+
+#ifdef RUMPUSER_USE_DEVRANDOM
+ uint32_t rv;
+ int fd;
+
+ if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
+ srandom(time(NULL));
+ } else {
+ if (read(fd, &rv, sizeof(rv)) != sizeof(rv))
+ srandom(time(NULL));
+ else
+ srandom(rv);
+ close(fd);
+ }
+#endif
+
+ rumpuser__hyp = *hyp;
+
+ init_sched();
+
+ return 0;
+}
+
+int
+rumpuser_clock_gettime(int enum_rumpclock, int64_t *sec, long *nsec)
+{
+ enum rumpclock rclk = enum_rumpclock;
+ struct timespec ts;
+ clockid_t clk;
+ int rv;
+
+ switch (rclk) {
+ case RUMPUSER_CLOCK_RELWALL:
+ clk = CLOCK_REALTIME;
+ break;
+ case RUMPUSER_CLOCK_ABSMONO:
+ clk = CLOCK_MONOTONIC;
+ break;
+ default:
+ abort();
+ }
+
+ if (clock_gettime(clk, &ts) == -1) {
+ rv = errno;
+ } else {
+ *sec = ts.tv_sec;
+ *nsec = ts.tv_nsec;
+ rv = 0;
+ }
+
+ ET(rv);
+}
+
+int
+rumpuser_clock_sleep(int enum_rumpclock, int64_t sec, long nsec)
+{
+ enum rumpclock rclk = enum_rumpclock;
+ uint32_t msec;
+ int nlocks;
+
+ rumpkern_unsched(&nlocks, NULL);
+ switch (rclk) {
+ case RUMPUSER_CLOCK_RELWALL:
+ msec = sec * 1000 + nsec / (1000*1000UL);
+ msleep(msec);
+ break;
+ case RUMPUSER_CLOCK_ABSMONO:
+ msec = sec * 1000 + nsec / (1000*1000UL);
+ abssleep(msec);
+ break;
+ }
+ rumpkern_sched(nlocks, NULL);
+
+ return 0;
+}
+
+int
+rumpuser_getparam(const char *name, void *buf, size_t blen)
+{
+ int rv;
+ const char *ncpu = "1";
+
+ if (strcmp(name, RUMPUSER_PARAM_NCPU) == 0) {
+ strncpy(buf, ncpu, blen);
+ rv = 0;
+ } else if (strcmp(name, RUMPUSER_PARAM_HOSTNAME) == 0) {
+ char tmp[MAXHOSTNAMELEN];
+
+ if (gethostname(tmp, sizeof(tmp)) == -1) {
+ snprintf(buf, blen, "rump-%05d", (int)getpid());
+ } else {
+ snprintf(buf, blen, "rump-%05d.%s",
+ (int)getpid(), tmp);
+ }
+ rv = 0;
+ } else if (*name == '_') {
+ rv = EINVAL;
+ } else {
+ if (getenv_r(name, buf, blen) == -1)
+ rv = errno;
+ else
+ rv = 0;
+ }
+
+ ET(rv);
+}
+
+void
+rumpuser_putchar(int c)
+{
+
+ putchar(c);
+}
+
+__dead void
+rumpuser_exit(int rv)
+{
+
+ if (rv == RUMPUSER_PANIC)
+ abort();
+ else
+ exit(rv);
+}
+
+void
+rumpuser_seterrno(int error)
+{
+
+ errno = error;
+}
+
+/*
+ * This is meant for safe debugging prints from the kernel.
+ */
+void
+rumpuser_dprintf(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
+int
+rumpuser_kill(int64_t pid, int rumpsig)
+{
+ int sig;
+
+ sig = rumpuser__sig_rump2host(rumpsig);
+ if (sig > 0)
+ raise(sig);
+ return 0;
+}
+
+int
+rumpuser_getrandom(void *buf, size_t buflen, int flags, size_t *retp)
+{
+ size_t origlen = buflen;
+ uint32_t *p = buf;
+ uint32_t tmp;
+ int chunk;
+
+ do {
+ chunk = buflen < 4 ? buflen : 4; /* portable MIN ... */
+ tmp = RUMPUSER_RANDOM();
+ memcpy(p, &tmp, chunk);
+ p++;
+ buflen -= chunk;
+ } while (chunk);
+
+ *retp = origlen;
+ ET(0);
+}
+
+/* thread functions */
+
+TAILQ_HEAD(waithead, waiter);
+struct waiter {
+ struct thread *who;
+ TAILQ_ENTRY(waiter) entries;
+ int onlist;
+};
+
+static int
+wait(struct waithead *wh, uint64_t msec)
+{
+ struct waiter w;
+
+ w.who = get_current();
+ TAILQ_INSERT_TAIL(wh, &w, entries);
+ w.onlist = 1;
+ block(w.who);
+ if (msec)
+ w.who->wakeup_time = now() + msec;
+ schedule();
+
+ /* woken up by timeout? */
+ if (w.onlist)
+ TAILQ_REMOVE(wh, &w, entries);
+
+ return w.onlist ? ETIMEDOUT : 0;
+}
+
+static void
+wakeup_one(struct waithead *wh)
+{
+ struct waiter *w;
+
+ if ((w = TAILQ_FIRST(wh)) != NULL) {
+ TAILQ_REMOVE(wh, w, entries);
+ w->onlist = 0;
+ wake(w->who);
+ }
+}
+
+static void
+wakeup_all(struct waithead *wh)
+{
+ struct waiter *w;
+
+ while ((w = TAILQ_FIRST(wh)) != NULL) {
+ TAILQ_REMOVE(wh, w, entries);
+ w->onlist = 0;
+ wake(w->who);
+ }
+}
+
+int
+rumpuser_thread_create(void *(*f)(void *), void *arg, const char *thrname,
+ int joinable, int pri, int cpuidx, void **tptr)
+{
+ struct thread *thr;
+
+ thr = create_thread(thrname, NULL, (void (*)(void *))f, arg, NULL, 0);
+ /*
+ * XXX: should be supplied as a flag to create_thread() so as to
+ * _ensure_ it's set before the thread runs (and could exit).
+ * now we're trusting unclear semantics of create_thread()
+ */
+ if (thr && joinable)
+ thr->flags |= THREAD_MUSTJOIN;
+
+ if (!thr)
+ return EINVAL;
+
+ *tptr = thr;
+ return 0;
+}
+
+void
+rumpuser_thread_exit(void)
+{
+
+ exit_thread();
+}
+
+int
+rumpuser_thread_join(void *p)
+{
+
+ join_thread(p);
+ return 0;
+}
+
+struct rumpuser_mtx {
+ struct waithead waiters;
+ int v;
+ int flags;
+ struct lwp *o;
+};
+
+void
+rumpuser_mutex_init(struct rumpuser_mtx **mtxp, int flags)
+{
+ struct rumpuser_mtx *mtx;
+
+ mtx = malloc(sizeof(*mtx));
+ memset(mtx, 0, sizeof(*mtx));
+ mtx->flags = flags;
+ TAILQ_INIT(&mtx->waiters);
+ *mtxp = mtx;
+}
+
+void
+rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
+{
+ int nlocks;
+
+ if (rumpuser_mutex_tryenter(mtx) != 0) {
+ rumpkern_unsched(&nlocks, NULL);
+ while (rumpuser_mutex_tryenter(mtx) != 0)
+ wait(&mtx->waiters, 0);
+ rumpkern_sched(nlocks, NULL);
+ }
+}
+
+void
+rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx)
+{
+ int rv;
+
+ rv = rumpuser_mutex_tryenter(mtx);
+ /* one VCPU supported, no preemption => must succeed */
+ if (rv != 0) {
+ printk("no voi ei\n");
+ }
+}
+
+int
+rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
+{
+ struct lwp *l = get_current()->lwp;
+
+ if (mtx->v && mtx->o != l)
+ return EBUSY;
+
+ mtx->v++;
+ mtx->o = l;
+
+ return 0;
+}
+
+void
+rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
+{
+
+ assert(mtx->v > 0);
+ if (--mtx->v == 0) {
+ mtx->o = NULL;
+ wakeup_one(&mtx->waiters);
+ }
+}
+
+void
+rumpuser_mutex_destroy(struct rumpuser_mtx *mtx)
+{
+
+ assert(TAILQ_EMPTY(&mtx->waiters) && mtx->o == NULL);
+ free(mtx);
+}
+
+void
+rumpuser_mutex_owner(struct rumpuser_mtx *mtx, struct lwp **lp)
+{
+
+ *lp = mtx->o;
+}
+
+struct rumpuser_rw {
+ struct waithead rwait;
+ struct waithead wwait;
+ int v;
+ struct lwp *o;
+};
+
+void
+rumpuser_rw_init(struct rumpuser_rw **rwp)
+{
+ struct rumpuser_rw *rw;
+
+ rw = malloc(sizeof(*rw));
+ memset(rw, 0, sizeof(*rw));
+ TAILQ_INIT(&rw->rwait);
+ TAILQ_INIT(&rw->wwait);
+
+ *rwp = rw;
+}
+
+void
+rumpuser_rw_enter(int enum_rumprwlock, struct rumpuser_rw *rw)
+{
+ enum rumprwlock lk = enum_rumprwlock;
+ struct waithead *w = NULL;
+ int nlocks;
+
+ switch (lk) {
+ case RUMPUSER_RW_WRITER:
+ w = &rw->wwait;
+ break;
+ case RUMPUSER_RW_READER:
+ w = &rw->rwait;
+ break;
+ }
+
+ if (rumpuser_rw_tryenter(enum_rumprwlock, rw) != 0) {
+ rumpkern_unsched(&nlocks, NULL);
+ while (rumpuser_rw_tryenter(enum_rumprwlock, rw) != 0)
+ wait(w, 0);
+ rumpkern_sched(nlocks, NULL);
+ }
+}
+
+int
+rumpuser_rw_tryenter(int enum_rumprwlock, struct rumpuser_rw *rw)
+{
+ enum rumprwlock lk = enum_rumprwlock;
+ int rv;
+
+ switch (lk) {
+ case RUMPUSER_RW_WRITER:
+ if (rw->o == NULL) {
+ rw->o = rumpuser_curlwp();
+ rv = 0;
+ } else {
+ rv = EBUSY;
+ }
+ break;
+ case RUMPUSER_RW_READER:
+ if (rw->o == NULL && TAILQ_EMPTY(&rw->wwait)) {
+ rw->v++;
+ rv = 0;
+ } else {
+ rv = EBUSY;
+ }
+ break;
+ default:
+ rv = EINVAL;
+ }
+
+ return rv;
+}
+
+void
+rumpuser_rw_exit(struct rumpuser_rw *rw)
+{
+
+ if (rw->o) {
+ rw->o = NULL;
+ } else {
+ rw->v--;
+ }
+
+ /* standard procedure, don't let readers starve out writers */
+ if (!TAILQ_EMPTY(&rw->wwait)) {
+ if (rw->o == NULL)
+ wakeup_one(&rw->wwait);
+ } else if (!TAILQ_EMPTY(&rw->rwait) && rw->o == NULL) {
+ wakeup_all(&rw->rwait);
+ }
+}
+
+void
+rumpuser_rw_destroy(struct rumpuser_rw *rw)
+{
+
+ free(rw);
+}
+
+void
+rumpuser_rw_held(int enum_rumprwlock, struct rumpuser_rw *rw, int *rvp)
+{
+ enum rumprwlock lk = enum_rumprwlock;
+
+ switch (lk) {
+ case RUMPUSER_RW_WRITER:
+ *rvp = rw->o == rumpuser_curlwp();
+ break;
+ case RUMPUSER_RW_READER:
+ *rvp = rw->v > 0;
+ break;
+ }
+}
+
+void
+rumpuser_rw_downgrade(struct rumpuser_rw *rw)
+{
+
+ assert(rw->o == rumpuser_curlwp());
+ rw->v = -1;
+}
+
+int
+rumpuser_rw_tryupgrade(struct rumpuser_rw *rw)
+{
+
+ if (rw->v == -1) {
+ rw->v = 1;
+ rw->o = rumpuser_curlwp();
+ return 0;
+ }
+
+ return EBUSY;
+}
+
+struct rumpuser_cv {
+ struct waithead waiters;
+ int nwaiters;
+};
+
+void
+rumpuser_cv_init(struct rumpuser_cv **cvp)
+{
+ struct rumpuser_cv *cv;
+
+ cv = malloc(sizeof(*cv));
+ memset(cv, 0, sizeof(*cv));
+ TAILQ_INIT(&cv->waiters);
+ *cvp = cv;
+}
+
+void
+rumpuser_cv_destroy(struct rumpuser_cv *cv)
+{
+
+ assert(cv->nwaiters == 0);
+ free(cv);
+}
+
+static void
+cv_unsched(struct rumpuser_mtx *mtx, int *nlocks)
+{
+
+ rumpkern_unsched(nlocks, mtx);
+ rumpuser_mutex_exit(mtx);
+}
+
+static void
+cv_resched(struct rumpuser_mtx *mtx, int nlocks)
+{
+
+ /* see rumpuser(3) */
+ if ((mtx->flags & (RUMPUSER_MTX_KMUTEX | RUMPUSER_MTX_SPIN)) ==
+ (RUMPUSER_MTX_KMUTEX | RUMPUSER_MTX_SPIN)) {
+ rumpkern_sched(nlocks, mtx);
+ rumpuser_mutex_enter_nowrap(mtx);
+ } else {
+ rumpuser_mutex_enter_nowrap(mtx);
+ rumpkern_sched(nlocks, mtx);
+ }
+}
+
+void
+rumpuser_cv_wait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
+{
+ int nlocks;
+
+ cv->nwaiters++;
+ cv_unsched(mtx, &nlocks);
+ wait(&cv->waiters, 0);
+ cv_resched(mtx, nlocks);
+ cv->nwaiters--;
+}
+
+void
+rumpuser_cv_wait_nowrap(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx)
+{
+
+ cv->nwaiters++;
+ rumpuser_mutex_exit(mtx);
+ wait(&cv->waiters, 0);
+ rumpuser_mutex_enter_nowrap(mtx);
+ cv->nwaiters--;
+}
+
+int
+rumpuser_cv_timedwait(struct rumpuser_cv *cv, struct rumpuser_mtx *mtx,
+ int64_t sec, int64_t nsec)
+{
+ int nlocks;
+ int rv;
+
+ cv->nwaiters++;
+ cv_unsched(mtx, &nlocks);
+ rv = wait(&cv->waiters, sec * 1000 + nsec / (1000*1000));
+ cv_resched(mtx, nlocks);
+ cv->nwaiters--;
+
+ return rv;
+}
+
+void
+rumpuser_cv_signal(struct rumpuser_cv *cv)
+{
+
+ wakeup_one(&cv->waiters);
+}
+
+void
+rumpuser_cv_broadcast(struct rumpuser_cv *cv)
+{
+
+ wakeup_all(&cv->waiters);
+}
+
+void
+rumpuser_cv_has_waiters(struct rumpuser_cv *cv, int *rvp)
+{
+
+ *rvp = cv->nwaiters != 0;
+}
+
+/*
+ * curlwp
+ */
+
+void
+rumpuser_curlwpop(int enum_rumplwpop, struct lwp *l)
+{
+ struct thread *thread;
+ enum rumplwpop op = enum_rumplwpop;
+
+ switch (op) {
+ case RUMPUSER_LWP_CREATE:
+ case RUMPUSER_LWP_DESTROY:
+ break;
+ case RUMPUSER_LWP_SET:
+ thread = get_current();
+ thread->lwp = l;
+ break;
+ case RUMPUSER_LWP_CLEAR:
+ thread = get_current();
+ assert(thread->lwp == l);
+ thread->lwp = NULL;
+ break;
+ }
+}
+
+struct lwp *
+rumpuser_curlwp(void)
+{
+
+ return get_current()->lwp;
+}
Index: src/lib/librumpuser/rumpfiber.h
diff -u /dev/null src/lib/librumpuser/rumpfiber.h:1.1
--- /dev/null Fri Jul 11 20:26:31 2014
+++ src/lib/librumpuser/rumpfiber.h Fri Jul 11 20:26:31 2014
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Justin Cormack. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+static void printk(const char *s);
+
+static void
+printk(const char *msg)
+{
+ int ret __attribute__((unused));
+
+ ret = write(2, msg, strlen(msg));
+}
+
+struct thread {
+ char *name;
+ void *lwp;
+ void *cookie;
+ int64_t wakeup_time;
+ TAILQ_ENTRY(thread) thread_list;
+ ucontext_t ctx;
+ uint32_t flags;
+ int threrrno;
+};
+
+#define RUNNABLE_FLAG 0x00000001
+#define THREAD_MUSTJOIN 0x00000002
+#define THREAD_JOINED 0x00000004
+#define THREAD_EXTSTACK 0x00000008
+#define THREAD_TIMEDOUT 0x00000010
+
+#define STACKSIZE 65536
+
+/* used by experimental _lwp code */
+void schedule(void);
+void wake(struct thread *thread);
+void block(struct thread *thread);
+struct thread *init_mainthread(void *);
+void exit_thread(void) __attribute__((noreturn));
+void set_sched_hook(void (*f)(void *, void *));
+int abssleep_real(uint64_t millisecs);
+struct thread* create_thread(const char *name, void *cookie,
+ void (*f)(void *), void *data,
+ void *stack, size_t stack_size);
+int is_runnable(struct thread *);
+void set_runnable(struct thread *);
+void clear_runnable(struct thread *);
+
Index: src/lib/librumpuser/rumpfiber_bio.c
diff -u /dev/null src/lib/librumpuser/rumpfiber_bio.c:1.1
--- /dev/null Fri Jul 11 20:26:31 2014
+++ src/lib/librumpuser/rumpfiber_bio.c Fri Jul 11 20:26:31 2014
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2014 Antti Kantee. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "rumpuser_port.h"
+
+#if !defined(lint)
+__RCSID("$NetBSD: rumpfiber_bio.c,v 1.1 2014/07/11 20:26:31 justin Exp $");
+#endif /* !lint */
+
+#include <sys/types.h>
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <rump/rumpuser.h>
+
+#include "rumpuser_int.h"
+
+void
+rumpuser_bio(int fd, int op, void *data, size_t dlen, int64_t doff,
+ rump_biodone_fn biodone, void *bioarg)
+{
+ ssize_t rv;
+ int error = 0;
+
+ if (op & RUMPUSER_BIO_READ) {
+ if ((rv = pread(fd, data, dlen, doff)) == -1)
+ error = errno;
+ } else {
+ if ((rv = pwrite(fd, data, dlen, doff)) == -1)
+ error = errno;
+ if (error == 0 && (op & RUMPUSER_BIO_SYNC)) {
+#ifdef __NetBSD__
+ fsync_range(fd, FDATASYNC, doff, dlen);
+#else
+ fsync(fd);
+#endif
+ }
+ }
+ if (rv == -1)
+ rv = 0;
+ biodone(bioarg, (size_t)rv, error);
+}
Index: src/lib/librumpuser/rumpfiber_sp.c
diff -u /dev/null src/lib/librumpuser/rumpfiber_sp.c:1.1
--- /dev/null Fri Jul 11 20:26:31 2014
+++ src/lib/librumpuser/rumpfiber_sp.c Fri Jul 11 20:26:31 2014
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2014 Justin Cormack. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* stubs for sp functions as not supported for fibers yet */
+
+#include "rumpuser_port.h"
+
+#if !defined(lint)
+__RCSID("$NetBSD: rumpfiber_sp.c,v 1.1 2014/07/11 20:26:31 justin Exp $");
+#endif /* !lint */
+
+#include <stdlib.h>
+
+#include <rump/rumpuser.h>
+
+#include "rumpfiber.h"
+
+/*ARGSUSED*/
+int
+rumpuser_sp_init(const char *url,
+ const char *ostype, const char *osrelease, const char *machine)
+{
+
+ return 0;
+}
+
+/*ARGSUSED*/
+void
+rumpuser_sp_fini(void *arg)
+{
+
+}
+
+/*ARGSUSED*/
+int
+rumpuser_sp_raise(void *arg, int signo)
+{
+
+ printk("rumphyper: unimplemented rumpuser_sp_raise\n");
+ exit(1);
+}
+
+/*ARGSUSED*/
+int
+rumpuser_sp_copyin(void *arg, const void *raddr, void *laddr, size_t len)
+{
+
+ printk("rumphyper: unimplemented rumpuser_sp_copyin\n");
+ exit(1);
+}
+
+/*ARGSUSED*/
+int
+rumpuser_sp_copyinstr(void *arg, const void *raddr, void *laddr, size_t *len)
+{
+
+ printk("rumphyper: unimplemented rumpuser_sp_copyinstr\n");
+ exit(1);
+}
+
+/*ARGSUSED*/
+int
+rumpuser_sp_copyout(void *arg, const void *laddr, void *raddr, size_t dlen)
+{
+
+ printk("rumphyper: unimplemented rumpuser_sp_copyout\n");
+ exit(1);
+}
+
+/*ARGSUSED*/
+int
+rumpuser_sp_copyoutstr(void *arg, const void *laddr, void *raddr, size_t *dlen)
+{
+
+ printk("rumphyper: unimplemented rumpuser_sp_copyoutstr\n");
+ exit(1);
+}
+
+/*ARGSUSED*/
+int
+rumpuser_sp_anonmmap(void *arg, size_t howmuch, void **addr)
+{
+
+ printk("rumphyper: unimplemented rumpuser_sp_anonmmap\n");
+ exit(1);
+}