1 /*- 2 * BSD LICENSE 3 * 4 * Copyright(c) 2015 Intel Corporation. All rights reserved. 5 * 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 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * Some portions of this software is derived from the 36 * https://github.com/halayli/lthread which carrys the following license. 37 * 38 * Copyright (C) 2012, Hasan Alayli <[email protected]> 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 49 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 #define RTE_MEM 1 63 64 #include <stdio.h> 65 #include <stdlib.h> 66 #include <string.h> 67 #include <stdint.h> 68 #include <stddef.h> 69 #include <limits.h> 70 #include <inttypes.h> 71 #include <unistd.h> 72 #include <pthread.h> 73 #include <fcntl.h> 74 #include <sys/time.h> 75 #include <sys/mman.h> 76 77 #include <rte_log.h> 78 #include <ctx.h> 79 #include <stack.h> 80 81 #include "lthread_api.h" 82 #include "lthread.h" 83 #include "lthread_timer.h" 84 #include "lthread_tls.h" 85 #include "lthread_objcache.h" 86 #include "lthread_diag.h" 87 88 89 /* 90 * This function gets called after an lthread function has returned. 91 */ 92 void _lthread_exit_handler(struct lthread *lt) 93 { 94 95 lt->state |= BIT(ST_LT_EXITED); 96 97 if (!(lt->state & BIT(ST_LT_DETACH))) { 98 /* thread is this not explicitly detached 99 * it must be joinable, so we call lthread_exit(). 100 */ 101 lthread_exit(NULL); 102 } 103 104 /* if we get here the thread is detached so we can reschedule it, 105 * allowing the scheduler to free it 106 */ 107 _reschedule(); 108 } 109 110 111 /* 112 * Free resources allocated to an lthread 113 */ 114 void _lthread_free(struct lthread *lt) 115 { 116 117 DIAG_EVENT(lt, LT_DIAG_LTHREAD_FREE, lt, 0); 118 119 /* invoke any user TLS destructor functions */ 120 _lthread_tls_destroy(lt); 121 122 /* free memory allocated for TLS defined using RTE_PER_LTHREAD macros */ 123 if (sizeof(void *) < (uint64_t)RTE_PER_LTHREAD_SECTION_SIZE) 124 _lthread_objcache_free(lt->tls->root_sched->per_lthread_cache, 125 lt->per_lthread_data); 126 127 /* free pthread style TLS memory */ 128 _lthread_objcache_free(lt->tls->root_sched->tls_cache, lt->tls); 129 130 /* free the stack */ 131 _lthread_objcache_free(lt->stack_container->root_sched->stack_cache, 132 lt->stack_container); 133 134 /* now free the thread */ 135 _lthread_objcache_free(lt->root_sched->lthread_cache, lt); 136 137 } 138 139 /* 140 * Allocate a stack and maintain a cache of stacks 141 */ 142 struct lthread_stack *_stack_alloc(void) 143 { 144 struct lthread_stack *s; 145 146 s = _lthread_objcache_alloc((THIS_SCHED)->stack_cache); 147 RTE_ASSERT(s != NULL); 148 149 s->root_sched = THIS_SCHED; 150 s->stack_size = LTHREAD_MAX_STACK_SIZE; 151 return s; 152 } 153 154 /* 155 * Execute a ctx by invoking the start function 156 * On return call an exit handler if the user has provided one 157 */ 158 static void _lthread_exec(void *arg) 159 { 160 struct lthread *lt = (struct lthread *)arg; 161 162 /* invoke the contexts function */ 163 lt->fun(lt->arg); 164 /* do exit handling */ 165 if (lt->exit_handler != NULL) 166 lt->exit_handler(lt); 167 } 168 169 /* 170 * Initialize an lthread 171 * Set its function, args, and exit handler 172 */ 173 void 174 _lthread_init(struct lthread *lt, 175 lthread_func_t fun, void *arg, lthread_exit_func exit_handler) 176 { 177 178 /* set ctx func and args */ 179 lt->fun = fun; 180 lt->arg = arg; 181 lt->exit_handler = exit_handler; 182 183 /* set initial state */ 184 lt->birth = _sched_now(); 185 lt->state = BIT(ST_LT_INIT); 186 lt->join = LT_JOIN_INITIAL; 187 } 188 189 /* 190 * set the lthread stack 191 */ 192 void _lthread_set_stack(struct lthread *lt, void *stack, size_t stack_size) 193 { 194 /* set stack */ 195 lt->stack = stack; 196 lt->stack_size = stack_size; 197 198 arch_set_stack(lt, _lthread_exec); 199 } 200 201 /* 202 * Create an lthread on the current scheduler 203 * If there is no current scheduler on this pthread then first create one 204 */ 205 int 206 lthread_create(struct lthread **new_lt, int lcore_id, 207 lthread_func_t fun, void *arg) 208 { 209 if ((new_lt == NULL) || (fun == NULL)) 210 return POSIX_ERRNO(EINVAL); 211 212 if (lcore_id < 0) 213 lcore_id = rte_lcore_id(); 214 else if (lcore_id > LTHREAD_MAX_LCORES) 215 return POSIX_ERRNO(EINVAL); 216 217 struct lthread *lt = NULL; 218 219 if (THIS_SCHED == NULL) { 220 THIS_SCHED = _lthread_sched_create(0); 221 if (THIS_SCHED == NULL) { 222 perror("Failed to create scheduler"); 223 return POSIX_ERRNO(EAGAIN); 224 } 225 } 226 227 /* allocate a thread structure */ 228 lt = _lthread_objcache_alloc((THIS_SCHED)->lthread_cache); 229 if (lt == NULL) 230 return POSIX_ERRNO(EAGAIN); 231 232 bzero(lt, sizeof(struct lthread)); 233 lt->root_sched = THIS_SCHED; 234 235 /* set the function args and exit handlder */ 236 _lthread_init(lt, fun, arg, _lthread_exit_handler); 237 238 /* put it in the ready queue */ 239 *new_lt = lt; 240 241 if (lcore_id < 0) 242 lcore_id = rte_lcore_id(); 243 244 DIAG_CREATE_EVENT(lt, LT_DIAG_LTHREAD_CREATE); 245 246 rte_wmb(); 247 _ready_queue_insert(_lthread_sched_get(lcore_id), lt); 248 return 0; 249 } 250 251 /* 252 * Schedules lthread to sleep for `nsecs` 253 * setting the lthread state to LT_ST_SLEEPING. 254 * lthread state is cleared upon resumption or expiry. 255 */ 256 static inline void _lthread_sched_sleep(struct lthread *lt, uint64_t nsecs) 257 { 258 uint64_t state = lt->state; 259 uint64_t clks = _ns_to_clks(nsecs); 260 261 if (clks) { 262 _timer_start(lt, clks); 263 lt->state = state | BIT(ST_LT_SLEEPING); 264 } 265 DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0); 266 _suspend(); 267 } 268 269 270 271 /* 272 * Cancels any running timer. 273 * This can be called multiple times on the same lthread regardless if it was 274 * sleeping or not. 275 */ 276 int _lthread_desched_sleep(struct lthread *lt) 277 { 278 uint64_t state = lt->state; 279 280 if (state & BIT(ST_LT_SLEEPING)) { 281 _timer_stop(lt); 282 state &= (CLEARBIT(ST_LT_SLEEPING) & CLEARBIT(ST_LT_EXPIRED)); 283 lt->state = state | BIT(ST_LT_READY); 284 return 1; 285 } 286 return 0; 287 } 288 289 /* 290 * set user data pointer in an lthread 291 */ 292 void lthread_set_data(void *data) 293 { 294 if (sizeof(void *) == RTE_PER_LTHREAD_SECTION_SIZE) 295 THIS_LTHREAD->per_lthread_data = data; 296 } 297 298 /* 299 * Retrieve user data pointer from an lthread 300 */ 301 void *lthread_get_data(void) 302 { 303 return THIS_LTHREAD->per_lthread_data; 304 } 305 306 /* 307 * Return the current lthread handle 308 */ 309 struct lthread *lthread_current(void) 310 { 311 struct lthread_sched *sched = THIS_SCHED; 312 313 if (sched) 314 return sched->current_lthread; 315 return NULL; 316 } 317 318 319 320 /* 321 * Tasklet to cancel a thread 322 */ 323 static void * 324 _cancel(void *arg) 325 { 326 struct lthread *lt = (struct lthread *) arg; 327 328 lt->state |= BIT(ST_LT_CANCELLED); 329 lthread_detach(); 330 return NULL; 331 } 332 333 334 /* 335 * Mark the specified as canceled 336 */ 337 int lthread_cancel(struct lthread *cancel_lt) 338 { 339 struct lthread *lt; 340 341 if ((cancel_lt == NULL) || (cancel_lt == THIS_LTHREAD)) 342 return POSIX_ERRNO(EINVAL); 343 344 DIAG_EVENT(cancel_lt, LT_DIAG_LTHREAD_CANCEL, cancel_lt, 0); 345 346 if (cancel_lt->sched != THIS_SCHED) { 347 348 /* spawn task-let to cancel the thread */ 349 lthread_create(<, 350 cancel_lt->sched->lcore_id, 351 _cancel, 352 cancel_lt); 353 return 0; 354 } 355 cancel_lt->state |= BIT(ST_LT_CANCELLED); 356 return 0; 357 } 358 359 /* 360 * Suspend the current lthread for specified time 361 */ 362 void lthread_sleep(uint64_t nsecs) 363 { 364 struct lthread *lt = THIS_LTHREAD; 365 366 _lthread_sched_sleep(lt, nsecs); 367 368 } 369 370 /* 371 * Suspend the current lthread for specified time 372 */ 373 void lthread_sleep_clks(uint64_t clks) 374 { 375 struct lthread *lt = THIS_LTHREAD; 376 uint64_t state = lt->state; 377 378 if (clks) { 379 _timer_start(lt, clks); 380 lt->state = state | BIT(ST_LT_SLEEPING); 381 } 382 DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0); 383 _suspend(); 384 } 385 386 /* 387 * Requeue the current thread to the back of the ready queue 388 */ 389 void lthread_yield(void) 390 { 391 struct lthread *lt = THIS_LTHREAD; 392 393 DIAG_EVENT(lt, LT_DIAG_LTHREAD_YIELD, 0, 0); 394 395 _ready_queue_insert(THIS_SCHED, lt); 396 ctx_switch(&(THIS_SCHED)->ctx, <->ctx); 397 } 398 399 /* 400 * Exit the current lthread 401 * If a thread is joining pass the user pointer to it 402 */ 403 void lthread_exit(void *ptr) 404 { 405 struct lthread *lt = THIS_LTHREAD; 406 407 /* if thread is detached (this is not valid) just exit */ 408 if (lt->state & BIT(ST_LT_DETACH)) 409 return; 410 411 /* There is a race between lthread_join() and lthread_exit() 412 * - if exit before join then we suspend and resume on join 413 * - if join before exit then we resume the joining thread 414 */ 415 if ((lt->join == LT_JOIN_INITIAL) 416 && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL, 417 LT_JOIN_EXITING)) { 418 419 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 1, 0); 420 _suspend(); 421 /* set the exit value */ 422 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL)) 423 *(lt->lt_join->lt_exit_ptr) = ptr; 424 425 /* let the joining thread know we have set the exit value */ 426 lt->join = LT_JOIN_EXIT_VAL_SET; 427 } else { 428 429 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 0, 0); 430 /* set the exit value */ 431 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL)) 432 *(lt->lt_join->lt_exit_ptr) = ptr; 433 /* let the joining thread know we have set the exit value */ 434 lt->join = LT_JOIN_EXIT_VAL_SET; 435 _ready_queue_insert(lt->lt_join->sched, 436 (struct lthread *)lt->lt_join); 437 } 438 439 440 /* wait until the joinging thread has collected the exit value */ 441 while (lt->join != LT_JOIN_EXIT_VAL_READ) 442 _reschedule(); 443 444 /* reset join state */ 445 lt->join = LT_JOIN_INITIAL; 446 447 /* detach it so its resources can be released */ 448 lt->state |= (BIT(ST_LT_DETACH) | BIT(ST_LT_EXITED)); 449 } 450 451 /* 452 * Join an lthread 453 * Suspend until the joined thread returns 454 */ 455 int lthread_join(struct lthread *lt, void **ptr) 456 { 457 if (lt == NULL) 458 return POSIX_ERRNO(EINVAL); 459 460 struct lthread *current = THIS_LTHREAD; 461 uint64_t lt_state = lt->state; 462 463 /* invalid to join a detached thread, or a thread that is joined */ 464 if ((lt_state & BIT(ST_LT_DETACH)) || (lt->join == LT_JOIN_THREAD_SET)) 465 return POSIX_ERRNO(EINVAL); 466 /* pointer to the joining thread and a poingter to return a value */ 467 lt->lt_join = current; 468 current->lt_exit_ptr = ptr; 469 /* There is a race between lthread_join() and lthread_exit() 470 * - if join before exit we suspend and will resume when exit is called 471 * - if exit before join we resume the exiting thread 472 */ 473 if ((lt->join == LT_JOIN_INITIAL) 474 && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL, 475 LT_JOIN_THREAD_SET)) { 476 477 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 1); 478 _suspend(); 479 } else { 480 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 0); 481 _ready_queue_insert(lt->sched, lt); 482 } 483 484 /* wait for exiting thread to set return value */ 485 while (lt->join != LT_JOIN_EXIT_VAL_SET) 486 _reschedule(); 487 488 /* collect the return value */ 489 if (ptr != NULL) 490 *ptr = *current->lt_exit_ptr; 491 492 /* let the exiting thread proceed to exit */ 493 lt->join = LT_JOIN_EXIT_VAL_READ; 494 return 0; 495 } 496 497 498 /* 499 * Detach current lthread 500 * A detached thread cannot be joined 501 */ 502 void lthread_detach(void) 503 { 504 struct lthread *lt = THIS_LTHREAD; 505 506 DIAG_EVENT(lt, LT_DIAG_LTHREAD_DETACH, 0, 0); 507 508 uint64_t state = lt->state; 509 510 lt->state = state | BIT(ST_LT_DETACH); 511 } 512 513 /* 514 * Set function name of an lthread 515 * this is a debug aid 516 */ 517 void lthread_set_funcname(const char *f) 518 { 519 struct lthread *lt = THIS_LTHREAD; 520 521 strncpy(lt->funcname, f, sizeof(lt->funcname)); 522 lt->funcname[sizeof(lt->funcname)-1] = 0; 523 } 524