1/*- 2 * Copyright (c) 2014, 2015 Antti Kantee. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#define _lwp_park ___lwp_park60 27#include <sys/cdefs.h> 28 29#include <sys/param.h> 30#include <sys/lwpctl.h> 31#include <sys/lwp.h> 32#include <sys/queue.h> 33#include <sys/time.h> 34#include <sys/tls.h> 35 36#include <assert.h> 37#include <errno.h> 38#include <lwp.h> 39#include <sched.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <unistd.h> 44 45#include <rump/rump.h> 46 47#include <bmk-core/core.h> 48#include <bmk-core/sched.h> 49 50#include <rumprun-base/makelwp.h> 51 52#include "rumprun-private.h" 53 54struct rumprun_lwp { 55 struct bmk_thread *rl_thread; 56 int rl_lwpid; 57 char rl_name[MAXCOMLEN+1]; 58 void (*rl_start)(void *); 59 void *rl_arg; 60 61 struct lwpctl rl_lwpctl; 62 int rl_no_parking_hare; /* a looney tunes reference ... finally! */ 63 64 TAILQ_ENTRY(rumprun_lwp) rl_entries; 65}; 66static TAILQ_HEAD(, rumprun_lwp) all_lwp = TAILQ_HEAD_INITIALIZER(all_lwp); 67static __thread struct rumprun_lwp *me; 68 69#define FIRST_LWPID 1 70static int curlwpid = FIRST_LWPID; 71 72static struct rumprun_lwp mainthread = { 73 .rl_lwpid = FIRST_LWPID, 74}; 75 76static void rumprun_makelwp_tramp(void *); 77 78static ptrdiff_t meoff; 79static void 80assignme(void *tcb, struct rumprun_lwp *value) 81{ 82 struct rumprun_lwp **dst = (void *)((uintptr_t)tcb + meoff); 83 84 *dst = value; 85} 86 87int 88_lwp_ctl(int ctl, struct lwpctl **data) 89{ 90 91 *data = (struct lwpctl *)&me->rl_lwpctl; 92 return 0; 93} 94 95int 96rumprun_makelwp(void (*start)(void *), void *arg, void *private, 97 void *stack_base, size_t stack_size, unsigned long flag, lwpid_t *lid) 98{ 99 struct rumprun_lwp *rl; 100 struct lwp *curlwp, *newlwp; 101 102 rl = calloc(1, sizeof(*rl)); 103 if (rl == NULL) 104 return errno; 105 assignme(private, rl); 106 107 curlwp = rump_pub_lwproc_curlwp(); 108 if ((errno = rump_pub_lwproc_newlwp(getpid())) != 0) { 109 free(rl); 110 return errno; 111 } 112 newlwp = rump_pub_lwproc_curlwp(); 113 rl->rl_start = start; 114 rl->rl_arg = arg; 115 rl->rl_lwpid = ++curlwpid; 116 rl->rl_thread = bmk_sched_create_withtls("lwp", rl, 0, 117 rumprun_makelwp_tramp, newlwp, stack_base, stack_size, private); 118 if (rl->rl_thread == NULL) { 119 free(rl); 120 rump_pub_lwproc_releaselwp(); 121 rump_pub_lwproc_switch(curlwp); 122 return EBUSY; /* ??? */ 123 } 124 rump_pub_lwproc_switch(curlwp); 125 126 *lid = rl->rl_lwpid; 127 TAILQ_INSERT_TAIL(&all_lwp, rl, rl_entries); 128 129 return 0; 130} 131 132static void 133rumprun_makelwp_tramp(void *arg) 134{ 135 136 rump_pub_lwproc_switch(arg); 137 (me->rl_start)(me->rl_arg); 138} 139 140static struct rumprun_lwp * 141lwpid2rl(lwpid_t lid) 142{ 143 struct rumprun_lwp *rl; 144 145 if (lid == 0) 146 return &mainthread; 147 TAILQ_FOREACH(rl, &all_lwp, rl_entries) { 148 if (rl->rl_lwpid == lid) 149 return rl; 150 } 151 return NULL; 152} 153 154int 155_lwp_unpark(lwpid_t lid, const void *hint) 156{ 157 struct rumprun_lwp *rl; 158 159 if ((rl = lwpid2rl(lid)) == NULL) { 160 return -1; 161 } 162 163 bmk_sched_wake(rl->rl_thread); 164 return 0; 165} 166 167ssize_t 168_lwp_unpark_all(const lwpid_t *targets, size_t ntargets, const void *hint) 169{ 170 ssize_t rv; 171 172 if (targets == NULL) 173 return 1024; 174 175 rv = ntargets; 176 while (ntargets--) { 177 if (_lwp_unpark(*targets, NULL) != 0) 178 rv--; 179 targets++; 180 } 181 //assert(rv >= 0); 182 return rv; 183} 184 185/* 186 * called by the scheduler when a context switch is made 187 * nb. cookie is null when non-lwp threads are being run 188 */ 189static void 190schedhook(void *prevcookie, void *nextcookie) 191{ 192 struct rumprun_lwp *prev, *next; 193 194 prev = prevcookie; 195 next = nextcookie; 196 197 if (prev && prev->rl_lwpctl.lc_curcpu != LWPCTL_CPU_EXITED) { 198 prev->rl_lwpctl.lc_curcpu = LWPCTL_CPU_NONE; 199 } 200 if (next) { 201 next->rl_lwpctl.lc_curcpu = 0; 202 next->rl_lwpctl.lc_pctr++; 203 } 204} 205 206void 207rumprun_lwp_init(void) 208{ 209 void *tcb = bmk_sched_gettcb(); 210 211 bmk_sched_set_hook(schedhook); 212 213 meoff = (uintptr_t)&me - (uintptr_t)tcb; 214 assignme(tcb, &mainthread); 215 mainthread.rl_thread = bmk_sched_init_mainlwp(&mainthread); 216 217 TAILQ_INSERT_TAIL(&all_lwp, me, rl_entries); 218} 219 220int 221_lwp_park(clockid_t clock_id, int flags, const struct timespec *ts, 222 lwpid_t unpark, const void *hint, const void *unparkhint) 223{ 224 int rv; 225 226 if (unpark) 227 _lwp_unpark(unpark, unparkhint); 228 229 if (me->rl_no_parking_hare) { 230 me->rl_no_parking_hare = 0; 231 return 0; 232 } 233 234 if (ts) { 235 bmk_time_t nsecs = ts->tv_sec*1000*1000*1000 + ts->tv_nsec; 236 237 if (flags & TIMER_ABSTIME) { 238 nsecs -= bmk_platform_cpu_clock_epochoffset(); 239 } else { 240 nsecs += bmk_platform_cpu_clock_monotonic(); 241 } 242 bmk_sched_blockprepare_timeout(nsecs); 243 } else { 244 bmk_sched_blockprepare(); 245 } 246 rv = bmk_sched_block(); 247 bmk_assert(rv == 0 || rv == ETIMEDOUT); 248 249 if (rv) { 250 errno = rv; 251 rv = -1; 252 } 253 return rv; 254} 255 256int 257_lwp_exit(void) 258{ 259 260 me->rl_lwpctl.lc_curcpu = LWPCTL_CPU_EXITED; 261 rump_pub_lwproc_releaselwp(); 262 TAILQ_REMOVE(&all_lwp, me, rl_entries); 263 264 /* could just assign it here, but for symmetry! */ 265 assignme(bmk_sched_gettcb(), NULL); 266 267 bmk_sched_exit_withtls(); 268 269 return 0; 270} 271 272int 273_lwp_continue(lwpid_t lid) 274{ 275 struct rumprun_lwp *rl; 276 277 if ((rl = lwpid2rl(lid)) == NULL) { 278 return ESRCH; 279 } 280 281 bmk_sched_unsuspend(rl->rl_thread); 282 return 0; 283} 284 285int 286_lwp_suspend(lwpid_t lid) 287{ 288 struct rumprun_lwp *rl; 289 290 if ((rl = lwpid2rl(lid)) == NULL) { 291 return ESRCH; 292 } 293 294 bmk_sched_suspend(rl->rl_thread); 295 return 0; 296} 297 298int 299_lwp_wakeup(lwpid_t lid) 300{ 301 struct rumprun_lwp *rl; 302 303 if ((rl = lwpid2rl(lid)) == NULL) 304 return ESRCH; 305 306 bmk_sched_wake(rl->rl_thread); 307 return 0; 308} 309 310int 311_lwp_setname(lwpid_t lid, const char *name) 312{ 313 struct rumprun_lwp *rl; 314 315 if ((rl = lwpid2rl(lid)) == NULL) 316 return ESRCH; 317 strlcpy(rl->rl_name, name, sizeof(rl->rl_name)); 318 319 return 0; 320} 321 322lwpid_t 323_lwp_self(void) 324{ 325 326 return me->rl_lwpid; 327} 328 329/* XXX: messy. see sched.h, libc, libpthread, and all over */ 330int _sys_sched_yield(void); 331int 332_sys_sched_yield(void) 333{ 334 335 bmk_sched_yield(); 336 return 0; 337} 338__weak_alias(sched_yield,_sys_sched_yield); 339 340struct tls_tcb * 341_rtld_tls_allocate(void) 342{ 343 344 return bmk_sched_tls_alloc(); 345} 346 347void 348_rtld_tls_free(struct tls_tcb *arg) 349{ 350 351 return bmk_sched_tls_free(arg); 352} 353 354void * 355_lwp_getprivate(void) 356{ 357 358 return bmk_sched_gettcb(); 359} 360 361void _lwpnullop(void); 362void _lwpnullop(void) { } 363 364int _lwpsuccess(void); 365int _lwpsuccess(void) { return 0; } 366 367void _lwpabort(void); 368void __dead 369_lwpabort(void) 370{ 371 372 printf("_lwpabort() called\n"); 373 _exit(1); 374} 375 376__strong_alias(_sys_setcontext,_lwpabort); 377__strong_alias(_lwp_kill,_lwpabort); 378 379__strong_alias(__libc_static_tls_setup,_lwpnullop); 380 381int rasctl(void); 382int rasctl(void) { return ENOSYS; } 383 384/* 385 * There is ongoing work to support these in the rump kernel, 386 * so I will just stub them out for now. 387 */ 388__strong_alias(_sched_getaffinity,_lwpnullop); 389__strong_alias(_sched_getparam,_lwpnullop); 390__strong_alias(_sched_setaffinity,_lwpnullop); 391__strong_alias(_sched_setparam,_lwpnullop); 392 393/* 394 * Technically, specifying a lower >0 protection level is an error, 395 * but we don't flag that error for now. 396 */ 397__strong_alias(_sched_protect,_lwpsuccess); 398