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 } 331 332 333 /* 334 * Mark the specified as canceled 335 */ 336 int lthread_cancel(struct lthread *cancel_lt) 337 { 338 struct lthread *lt; 339 340 if ((cancel_lt == NULL) || (cancel_lt == THIS_LTHREAD)) 341 return POSIX_ERRNO(EINVAL); 342 343 DIAG_EVENT(cancel_lt, LT_DIAG_LTHREAD_CANCEL, cancel_lt, 0); 344 345 if (cancel_lt->sched != THIS_SCHED) { 346 347 /* spawn task-let to cancel the thread */ 348 lthread_create(<, 349 cancel_lt->sched->lcore_id, 350 _cancel, 351 cancel_lt); 352 return 0; 353 } 354 cancel_lt->state |= BIT(ST_LT_CANCELLED); 355 return 0; 356 } 357 358 /* 359 * Suspend the current lthread for specified time 360 */ 361 void lthread_sleep(uint64_t nsecs) 362 { 363 struct lthread *lt = THIS_LTHREAD; 364 365 _lthread_sched_sleep(lt, nsecs); 366 367 } 368 369 /* 370 * Suspend the current lthread for specified time 371 */ 372 void lthread_sleep_clks(uint64_t clks) 373 { 374 struct lthread *lt = THIS_LTHREAD; 375 uint64_t state = lt->state; 376 377 if (clks) { 378 _timer_start(lt, clks); 379 lt->state = state | BIT(ST_LT_SLEEPING); 380 } 381 DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0); 382 _suspend(); 383 } 384 385 /* 386 * Requeue the current thread to the back of the ready queue 387 */ 388 void lthread_yield(void) 389 { 390 struct lthread *lt = THIS_LTHREAD; 391 392 DIAG_EVENT(lt, LT_DIAG_LTHREAD_YIELD, 0, 0); 393 394 _ready_queue_insert(THIS_SCHED, lt); 395 ctx_switch(&(THIS_SCHED)->ctx, <->ctx); 396 } 397 398 /* 399 * Exit the current lthread 400 * If a thread is joining pass the user pointer to it 401 */ 402 void lthread_exit(void *ptr) 403 { 404 struct lthread *lt = THIS_LTHREAD; 405 406 /* if thread is detached (this is not valid) just exit */ 407 if (lt->state & BIT(ST_LT_DETACH)) 408 return; 409 410 /* There is a race between lthread_join() and lthread_exit() 411 * - if exit before join then we suspend and resume on join 412 * - if join before exit then we resume the joining thread 413 */ 414 if ((lt->join == LT_JOIN_INITIAL) 415 && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL, 416 LT_JOIN_EXITING)) { 417 418 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 1, 0); 419 _suspend(); 420 /* set the exit value */ 421 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL)) 422 *(lt->lt_join->lt_exit_ptr) = ptr; 423 424 /* let the joining thread know we have set the exit value */ 425 lt->join = LT_JOIN_EXIT_VAL_SET; 426 } else { 427 428 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 0, 0); 429 /* set the exit value */ 430 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL)) 431 *(lt->lt_join->lt_exit_ptr) = ptr; 432 /* let the joining thread know we have set the exit value */ 433 lt->join = LT_JOIN_EXIT_VAL_SET; 434 _ready_queue_insert(lt->lt_join->sched, 435 (struct lthread *)lt->lt_join); 436 } 437 438 439 /* wait until the joinging thread has collected the exit value */ 440 while (lt->join != LT_JOIN_EXIT_VAL_READ) 441 _reschedule(); 442 443 /* reset join state */ 444 lt->join = LT_JOIN_INITIAL; 445 446 /* detach it so its resources can be released */ 447 lt->state |= (BIT(ST_LT_DETACH) | BIT(ST_LT_EXITED)); 448 } 449 450 /* 451 * Join an lthread 452 * Suspend until the joined thread returns 453 */ 454 int lthread_join(struct lthread *lt, void **ptr) 455 { 456 if (lt == NULL) 457 return POSIX_ERRNO(EINVAL); 458 459 struct lthread *current = THIS_LTHREAD; 460 uint64_t lt_state = lt->state; 461 462 /* invalid to join a detached thread, or a thread that is joined */ 463 if ((lt_state & BIT(ST_LT_DETACH)) || (lt->join == LT_JOIN_THREAD_SET)) 464 return POSIX_ERRNO(EINVAL); 465 /* pointer to the joining thread and a poingter to return a value */ 466 lt->lt_join = current; 467 current->lt_exit_ptr = ptr; 468 /* There is a race between lthread_join() and lthread_exit() 469 * - if join before exit we suspend and will resume when exit is called 470 * - if exit before join we resume the exiting thread 471 */ 472 if ((lt->join == LT_JOIN_INITIAL) 473 && rte_atomic64_cmpset(<->join, LT_JOIN_INITIAL, 474 LT_JOIN_THREAD_SET)) { 475 476 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 1); 477 _suspend(); 478 } else { 479 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 0); 480 _ready_queue_insert(lt->sched, lt); 481 } 482 483 /* wait for exiting thread to set return value */ 484 while (lt->join != LT_JOIN_EXIT_VAL_SET) 485 _reschedule(); 486 487 /* collect the return value */ 488 if (ptr != NULL) 489 *ptr = *current->lt_exit_ptr; 490 491 /* let the exiting thread proceed to exit */ 492 lt->join = LT_JOIN_EXIT_VAL_READ; 493 return 0; 494 } 495 496 497 /* 498 * Detach current lthread 499 * A detached thread cannot be joined 500 */ 501 void lthread_detach(void) 502 { 503 struct lthread *lt = THIS_LTHREAD; 504 505 DIAG_EVENT(lt, LT_DIAG_LTHREAD_DETACH, 0, 0); 506 507 uint64_t state = lt->state; 508 509 lt->state = state | BIT(ST_LT_DETACH); 510 } 511 512 /* 513 * Set function name of an lthread 514 * this is a debug aid 515 */ 516 void lthread_set_funcname(const char *f) 517 { 518 struct lthread *lt = THIS_LTHREAD; 519 520 strncpy(lt->funcname, f, sizeof(lt->funcname)); 521 lt->funcname[sizeof(lt->funcname)-1] = 0; 522 } 523