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