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