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