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