199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright 2021 Mellanox Technologies, Ltd 356539289STyler Retzlaff * Copyright (C) 2022 Microsoft Corporation 499a2dd95SBruce Richardson */ 599a2dd95SBruce Richardson 699a2dd95SBruce Richardson #include <errno.h> 799a2dd95SBruce Richardson #include <pthread.h> 899a2dd95SBruce Richardson #include <stdlib.h> 999a2dd95SBruce Richardson #include <string.h> 1099a2dd95SBruce Richardson 1199a2dd95SBruce Richardson #include <rte_errno.h> 1299a2dd95SBruce Richardson #include <rte_log.h> 1399a2dd95SBruce Richardson #include <rte_thread.h> 1499a2dd95SBruce Richardson 1599a2dd95SBruce Richardson struct eal_tls_key { 1699a2dd95SBruce Richardson pthread_key_t thread_index; 1799a2dd95SBruce Richardson }; 1899a2dd95SBruce Richardson 19*ce6e911dSTyler Retzlaff struct thread_routine_ctx { 20*ce6e911dSTyler Retzlaff rte_thread_func thread_func; 21*ce6e911dSTyler Retzlaff void *routine_args; 22*ce6e911dSTyler Retzlaff }; 23*ce6e911dSTyler Retzlaff 24ca04c78bSTyler Retzlaff static int 25ca04c78bSTyler Retzlaff thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, int *os_pri, 26ca04c78bSTyler Retzlaff int *pol) 27ca04c78bSTyler Retzlaff { 28ca04c78bSTyler Retzlaff /* Clear the output parameters. */ 29ca04c78bSTyler Retzlaff *os_pri = sched_get_priority_min(SCHED_OTHER) - 1; 30ca04c78bSTyler Retzlaff *pol = -1; 31ca04c78bSTyler Retzlaff 32ca04c78bSTyler Retzlaff switch (eal_pri) { 33ca04c78bSTyler Retzlaff case RTE_THREAD_PRIORITY_NORMAL: 34ca04c78bSTyler Retzlaff *pol = SCHED_OTHER; 35ca04c78bSTyler Retzlaff 36ca04c78bSTyler Retzlaff /* 37ca04c78bSTyler Retzlaff * Choose the middle of the range to represent the priority 38ca04c78bSTyler Retzlaff * 'normal'. 39ca04c78bSTyler Retzlaff * On Linux, this should be 0, since both 40ca04c78bSTyler Retzlaff * sched_get_priority_min/_max return 0 for SCHED_OTHER. 41ca04c78bSTyler Retzlaff */ 42ca04c78bSTyler Retzlaff *os_pri = (sched_get_priority_min(SCHED_OTHER) + 43ca04c78bSTyler Retzlaff sched_get_priority_max(SCHED_OTHER)) / 2; 44ca04c78bSTyler Retzlaff break; 45ca04c78bSTyler Retzlaff case RTE_THREAD_PRIORITY_REALTIME_CRITICAL: 46ca04c78bSTyler Retzlaff *pol = SCHED_RR; 47ca04c78bSTyler Retzlaff *os_pri = sched_get_priority_max(SCHED_RR); 48ca04c78bSTyler Retzlaff break; 49ca04c78bSTyler Retzlaff default: 50ca04c78bSTyler Retzlaff RTE_LOG(DEBUG, EAL, "The requested priority value is invalid.\n"); 51ca04c78bSTyler Retzlaff return EINVAL; 52ca04c78bSTyler Retzlaff } 53ca04c78bSTyler Retzlaff 54ca04c78bSTyler Retzlaff return 0; 55ca04c78bSTyler Retzlaff } 56ca04c78bSTyler Retzlaff 57ca04c78bSTyler Retzlaff static int 58ca04c78bSTyler Retzlaff thread_map_os_priority_to_eal_priority(int policy, int os_pri, 59ca04c78bSTyler Retzlaff enum rte_thread_priority *eal_pri) 60ca04c78bSTyler Retzlaff { 61ca04c78bSTyler Retzlaff switch (policy) { 62ca04c78bSTyler Retzlaff case SCHED_OTHER: 63ca04c78bSTyler Retzlaff if (os_pri == (sched_get_priority_min(SCHED_OTHER) + 64ca04c78bSTyler Retzlaff sched_get_priority_max(SCHED_OTHER)) / 2) { 65ca04c78bSTyler Retzlaff *eal_pri = RTE_THREAD_PRIORITY_NORMAL; 66ca04c78bSTyler Retzlaff return 0; 67ca04c78bSTyler Retzlaff } 68ca04c78bSTyler Retzlaff break; 69ca04c78bSTyler Retzlaff case SCHED_RR: 70ca04c78bSTyler Retzlaff if (os_pri == sched_get_priority_max(SCHED_RR)) { 71ca04c78bSTyler Retzlaff *eal_pri = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; 72ca04c78bSTyler Retzlaff return 0; 73ca04c78bSTyler Retzlaff } 74ca04c78bSTyler Retzlaff break; 75ca04c78bSTyler Retzlaff default: 76ca04c78bSTyler Retzlaff RTE_LOG(DEBUG, EAL, "The OS priority value does not map to an EAL-defined priority.\n"); 77ca04c78bSTyler Retzlaff return EINVAL; 78ca04c78bSTyler Retzlaff } 79ca04c78bSTyler Retzlaff 80ca04c78bSTyler Retzlaff return 0; 81ca04c78bSTyler Retzlaff } 82ca04c78bSTyler Retzlaff 83*ce6e911dSTyler Retzlaff static void * 84*ce6e911dSTyler Retzlaff thread_func_wrapper(void *arg) 85*ce6e911dSTyler Retzlaff { 86*ce6e911dSTyler Retzlaff struct thread_routine_ctx ctx = *(struct thread_routine_ctx *)arg; 87*ce6e911dSTyler Retzlaff 88*ce6e911dSTyler Retzlaff free(arg); 89*ce6e911dSTyler Retzlaff 90*ce6e911dSTyler Retzlaff return (void *)(uintptr_t)ctx.thread_func(ctx.routine_args); 91*ce6e911dSTyler Retzlaff } 92*ce6e911dSTyler Retzlaff 93*ce6e911dSTyler Retzlaff int 94*ce6e911dSTyler Retzlaff rte_thread_create(rte_thread_t *thread_id, 95*ce6e911dSTyler Retzlaff const rte_thread_attr_t *thread_attr, 96*ce6e911dSTyler Retzlaff rte_thread_func thread_func, void *args) 97*ce6e911dSTyler Retzlaff { 98*ce6e911dSTyler Retzlaff int ret = 0; 99*ce6e911dSTyler Retzlaff pthread_attr_t attr; 100*ce6e911dSTyler Retzlaff pthread_attr_t *attrp = NULL; 101*ce6e911dSTyler Retzlaff struct thread_routine_ctx *ctx; 102*ce6e911dSTyler Retzlaff struct sched_param param = { 103*ce6e911dSTyler Retzlaff .sched_priority = 0, 104*ce6e911dSTyler Retzlaff }; 105*ce6e911dSTyler Retzlaff int policy = SCHED_OTHER; 106*ce6e911dSTyler Retzlaff 107*ce6e911dSTyler Retzlaff ctx = calloc(1, sizeof(*ctx)); 108*ce6e911dSTyler Retzlaff if (ctx == NULL) { 109*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "Insufficient memory for thread context allocations\n"); 110*ce6e911dSTyler Retzlaff ret = ENOMEM; 111*ce6e911dSTyler Retzlaff goto cleanup; 112*ce6e911dSTyler Retzlaff } 113*ce6e911dSTyler Retzlaff ctx->routine_args = args; 114*ce6e911dSTyler Retzlaff ctx->thread_func = thread_func; 115*ce6e911dSTyler Retzlaff 116*ce6e911dSTyler Retzlaff if (thread_attr != NULL) { 117*ce6e911dSTyler Retzlaff ret = pthread_attr_init(&attr); 118*ce6e911dSTyler Retzlaff if (ret != 0) { 119*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "pthread_attr_init failed\n"); 120*ce6e911dSTyler Retzlaff goto cleanup; 121*ce6e911dSTyler Retzlaff } 122*ce6e911dSTyler Retzlaff 123*ce6e911dSTyler Retzlaff attrp = &attr; 124*ce6e911dSTyler Retzlaff 125*ce6e911dSTyler Retzlaff /* 126*ce6e911dSTyler Retzlaff * Set the inherit scheduler parameter to explicit, 127*ce6e911dSTyler Retzlaff * otherwise the priority attribute is ignored. 128*ce6e911dSTyler Retzlaff */ 129*ce6e911dSTyler Retzlaff ret = pthread_attr_setinheritsched(attrp, 130*ce6e911dSTyler Retzlaff PTHREAD_EXPLICIT_SCHED); 131*ce6e911dSTyler Retzlaff if (ret != 0) { 132*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "pthread_attr_setinheritsched failed\n"); 133*ce6e911dSTyler Retzlaff goto cleanup; 134*ce6e911dSTyler Retzlaff } 135*ce6e911dSTyler Retzlaff 136*ce6e911dSTyler Retzlaff 137*ce6e911dSTyler Retzlaff if (thread_attr->priority == 138*ce6e911dSTyler Retzlaff RTE_THREAD_PRIORITY_REALTIME_CRITICAL) { 139*ce6e911dSTyler Retzlaff ret = ENOTSUP; 140*ce6e911dSTyler Retzlaff goto cleanup; 141*ce6e911dSTyler Retzlaff } 142*ce6e911dSTyler Retzlaff ret = thread_map_priority_to_os_value(thread_attr->priority, 143*ce6e911dSTyler Retzlaff ¶m.sched_priority, &policy); 144*ce6e911dSTyler Retzlaff if (ret != 0) 145*ce6e911dSTyler Retzlaff goto cleanup; 146*ce6e911dSTyler Retzlaff 147*ce6e911dSTyler Retzlaff ret = pthread_attr_setschedpolicy(attrp, policy); 148*ce6e911dSTyler Retzlaff if (ret != 0) { 149*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "pthread_attr_setschedpolicy failed\n"); 150*ce6e911dSTyler Retzlaff goto cleanup; 151*ce6e911dSTyler Retzlaff } 152*ce6e911dSTyler Retzlaff 153*ce6e911dSTyler Retzlaff ret = pthread_attr_setschedparam(attrp, ¶m); 154*ce6e911dSTyler Retzlaff if (ret != 0) { 155*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "pthread_attr_setschedparam failed\n"); 156*ce6e911dSTyler Retzlaff goto cleanup; 157*ce6e911dSTyler Retzlaff } 158*ce6e911dSTyler Retzlaff } 159*ce6e911dSTyler Retzlaff 160*ce6e911dSTyler Retzlaff ret = pthread_create((pthread_t *)&thread_id->opaque_id, attrp, 161*ce6e911dSTyler Retzlaff thread_func_wrapper, ctx); 162*ce6e911dSTyler Retzlaff if (ret != 0) { 163*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "pthread_create failed\n"); 164*ce6e911dSTyler Retzlaff goto cleanup; 165*ce6e911dSTyler Retzlaff } 166*ce6e911dSTyler Retzlaff 167*ce6e911dSTyler Retzlaff if (thread_attr != NULL && CPU_COUNT(&thread_attr->cpuset) > 0) { 168*ce6e911dSTyler Retzlaff ret = rte_thread_set_affinity_by_id(*thread_id, 169*ce6e911dSTyler Retzlaff &thread_attr->cpuset); 170*ce6e911dSTyler Retzlaff if (ret != 0) { 171*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "rte_thread_set_affinity_by_id failed\n"); 172*ce6e911dSTyler Retzlaff goto cleanup; 173*ce6e911dSTyler Retzlaff } 174*ce6e911dSTyler Retzlaff } 175*ce6e911dSTyler Retzlaff 176*ce6e911dSTyler Retzlaff ctx = NULL; 177*ce6e911dSTyler Retzlaff cleanup: 178*ce6e911dSTyler Retzlaff free(ctx); 179*ce6e911dSTyler Retzlaff if (attrp != NULL) 180*ce6e911dSTyler Retzlaff pthread_attr_destroy(&attr); 181*ce6e911dSTyler Retzlaff 182*ce6e911dSTyler Retzlaff return ret; 183*ce6e911dSTyler Retzlaff } 184*ce6e911dSTyler Retzlaff 185*ce6e911dSTyler Retzlaff int 186*ce6e911dSTyler Retzlaff rte_thread_join(rte_thread_t thread_id, uint32_t *value_ptr) 187*ce6e911dSTyler Retzlaff { 188*ce6e911dSTyler Retzlaff int ret = 0; 189*ce6e911dSTyler Retzlaff void *res = (void *)(uintptr_t)0; 190*ce6e911dSTyler Retzlaff void **pres = NULL; 191*ce6e911dSTyler Retzlaff 192*ce6e911dSTyler Retzlaff if (value_ptr != NULL) 193*ce6e911dSTyler Retzlaff pres = &res; 194*ce6e911dSTyler Retzlaff 195*ce6e911dSTyler Retzlaff ret = pthread_join((pthread_t)thread_id.opaque_id, pres); 196*ce6e911dSTyler Retzlaff if (ret != 0) { 197*ce6e911dSTyler Retzlaff RTE_LOG(DEBUG, EAL, "pthread_join failed\n"); 198*ce6e911dSTyler Retzlaff return ret; 199*ce6e911dSTyler Retzlaff } 200*ce6e911dSTyler Retzlaff 201*ce6e911dSTyler Retzlaff if (value_ptr != NULL) 202*ce6e911dSTyler Retzlaff *value_ptr = (uint32_t)(uintptr_t)res; 203*ce6e911dSTyler Retzlaff 204*ce6e911dSTyler Retzlaff return 0; 205*ce6e911dSTyler Retzlaff } 206*ce6e911dSTyler Retzlaff 207*ce6e911dSTyler Retzlaff int 208*ce6e911dSTyler Retzlaff rte_thread_detach(rte_thread_t thread_id) 209*ce6e911dSTyler Retzlaff { 210*ce6e911dSTyler Retzlaff return pthread_detach((pthread_t)thread_id.opaque_id); 211*ce6e911dSTyler Retzlaff } 212*ce6e911dSTyler Retzlaff 21356539289STyler Retzlaff rte_thread_t 21456539289STyler Retzlaff rte_thread_self(void) 21556539289STyler Retzlaff { 21656539289STyler Retzlaff RTE_BUILD_BUG_ON(sizeof(pthread_t) > sizeof(uintptr_t)); 21756539289STyler Retzlaff 21856539289STyler Retzlaff rte_thread_t thread_id; 21956539289STyler Retzlaff 22056539289STyler Retzlaff thread_id.opaque_id = (uintptr_t)pthread_self(); 22156539289STyler Retzlaff 22256539289STyler Retzlaff return thread_id; 22356539289STyler Retzlaff } 22456539289STyler Retzlaff 22599a2dd95SBruce Richardson int 226ca04c78bSTyler Retzlaff rte_thread_get_priority(rte_thread_t thread_id, 227ca04c78bSTyler Retzlaff enum rte_thread_priority *priority) 228ca04c78bSTyler Retzlaff { 229ca04c78bSTyler Retzlaff struct sched_param param; 230ca04c78bSTyler Retzlaff int policy; 231ca04c78bSTyler Retzlaff int ret; 232ca04c78bSTyler Retzlaff 233ca04c78bSTyler Retzlaff ret = pthread_getschedparam((pthread_t)thread_id.opaque_id, &policy, 234ca04c78bSTyler Retzlaff ¶m); 235ca04c78bSTyler Retzlaff if (ret != 0) { 236ca04c78bSTyler Retzlaff RTE_LOG(DEBUG, EAL, "pthread_getschedparam failed\n"); 237ca04c78bSTyler Retzlaff goto cleanup; 238ca04c78bSTyler Retzlaff } 239ca04c78bSTyler Retzlaff 240ca04c78bSTyler Retzlaff return thread_map_os_priority_to_eal_priority(policy, 241ca04c78bSTyler Retzlaff param.sched_priority, priority); 242ca04c78bSTyler Retzlaff 243ca04c78bSTyler Retzlaff cleanup: 244ca04c78bSTyler Retzlaff return ret; 245ca04c78bSTyler Retzlaff } 246ca04c78bSTyler Retzlaff 247ca04c78bSTyler Retzlaff int 248ca04c78bSTyler Retzlaff rte_thread_set_priority(rte_thread_t thread_id, 249ca04c78bSTyler Retzlaff enum rte_thread_priority priority) 250ca04c78bSTyler Retzlaff { 251ca04c78bSTyler Retzlaff struct sched_param param; 252ca04c78bSTyler Retzlaff int policy; 253ca04c78bSTyler Retzlaff int ret; 254ca04c78bSTyler Retzlaff 255ca04c78bSTyler Retzlaff /* Realtime priority can cause crashes on non-Windows platforms. */ 256ca04c78bSTyler Retzlaff if (priority == RTE_THREAD_PRIORITY_REALTIME_CRITICAL) 257ca04c78bSTyler Retzlaff return ENOTSUP; 258ca04c78bSTyler Retzlaff 259ca04c78bSTyler Retzlaff ret = thread_map_priority_to_os_value(priority, ¶m.sched_priority, 260ca04c78bSTyler Retzlaff &policy); 261ca04c78bSTyler Retzlaff if (ret != 0) 262ca04c78bSTyler Retzlaff return ret; 263ca04c78bSTyler Retzlaff 264ca04c78bSTyler Retzlaff return pthread_setschedparam((pthread_t)thread_id.opaque_id, policy, 265ca04c78bSTyler Retzlaff ¶m); 266ca04c78bSTyler Retzlaff } 267ca04c78bSTyler Retzlaff 268ca04c78bSTyler Retzlaff int 26999a2dd95SBruce Richardson rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) 27099a2dd95SBruce Richardson { 27199a2dd95SBruce Richardson int err; 27299a2dd95SBruce Richardson 27399a2dd95SBruce Richardson *key = malloc(sizeof(**key)); 27499a2dd95SBruce Richardson if ((*key) == NULL) { 27599a2dd95SBruce Richardson RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); 27699a2dd95SBruce Richardson rte_errno = ENOMEM; 27799a2dd95SBruce Richardson return -1; 27899a2dd95SBruce Richardson } 27999a2dd95SBruce Richardson err = pthread_key_create(&((*key)->thread_index), destructor); 28099a2dd95SBruce Richardson if (err) { 28199a2dd95SBruce Richardson RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", 28299a2dd95SBruce Richardson strerror(err)); 28399a2dd95SBruce Richardson free(*key); 28499a2dd95SBruce Richardson rte_errno = ENOEXEC; 28599a2dd95SBruce Richardson return -1; 28699a2dd95SBruce Richardson } 28799a2dd95SBruce Richardson return 0; 28899a2dd95SBruce Richardson } 28999a2dd95SBruce Richardson 29099a2dd95SBruce Richardson int 29199a2dd95SBruce Richardson rte_thread_key_delete(rte_thread_key key) 29299a2dd95SBruce Richardson { 29399a2dd95SBruce Richardson int err; 29499a2dd95SBruce Richardson 29599a2dd95SBruce Richardson if (!key) { 29699a2dd95SBruce Richardson RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); 29799a2dd95SBruce Richardson rte_errno = EINVAL; 29899a2dd95SBruce Richardson return -1; 29999a2dd95SBruce Richardson } 30099a2dd95SBruce Richardson err = pthread_key_delete(key->thread_index); 30199a2dd95SBruce Richardson if (err) { 30299a2dd95SBruce Richardson RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", 30399a2dd95SBruce Richardson strerror(err)); 30499a2dd95SBruce Richardson free(key); 30599a2dd95SBruce Richardson rte_errno = ENOEXEC; 30699a2dd95SBruce Richardson return -1; 30799a2dd95SBruce Richardson } 30899a2dd95SBruce Richardson free(key); 30999a2dd95SBruce Richardson return 0; 31099a2dd95SBruce Richardson } 31199a2dd95SBruce Richardson 31299a2dd95SBruce Richardson int 31399a2dd95SBruce Richardson rte_thread_value_set(rte_thread_key key, const void *value) 31499a2dd95SBruce Richardson { 31599a2dd95SBruce Richardson int err; 31699a2dd95SBruce Richardson 31799a2dd95SBruce Richardson if (!key) { 31899a2dd95SBruce Richardson RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); 31999a2dd95SBruce Richardson rte_errno = EINVAL; 32099a2dd95SBruce Richardson return -1; 32199a2dd95SBruce Richardson } 32299a2dd95SBruce Richardson err = pthread_setspecific(key->thread_index, value); 32399a2dd95SBruce Richardson if (err) { 32499a2dd95SBruce Richardson RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", 32599a2dd95SBruce Richardson strerror(err)); 32699a2dd95SBruce Richardson rte_errno = ENOEXEC; 32799a2dd95SBruce Richardson return -1; 32899a2dd95SBruce Richardson } 32999a2dd95SBruce Richardson return 0; 33099a2dd95SBruce Richardson } 33199a2dd95SBruce Richardson 33299a2dd95SBruce Richardson void * 33399a2dd95SBruce Richardson rte_thread_value_get(rte_thread_key key) 33499a2dd95SBruce Richardson { 33599a2dd95SBruce Richardson if (!key) { 33699a2dd95SBruce Richardson RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); 33799a2dd95SBruce Richardson rte_errno = EINVAL; 33899a2dd95SBruce Richardson return NULL; 33999a2dd95SBruce Richardson } 34099a2dd95SBruce Richardson return pthread_getspecific(key->thread_index); 34199a2dd95SBruce Richardson } 342b70a9b78STyler Retzlaff 343b70a9b78STyler Retzlaff int 344b70a9b78STyler Retzlaff rte_thread_set_affinity_by_id(rte_thread_t thread_id, 345b70a9b78STyler Retzlaff const rte_cpuset_t *cpuset) 346b70a9b78STyler Retzlaff { 347b70a9b78STyler Retzlaff return pthread_setaffinity_np((pthread_t)thread_id.opaque_id, 348b70a9b78STyler Retzlaff sizeof(*cpuset), cpuset); 349b70a9b78STyler Retzlaff } 350b70a9b78STyler Retzlaff 351b70a9b78STyler Retzlaff int 352b70a9b78STyler Retzlaff rte_thread_get_affinity_by_id(rte_thread_t thread_id, 353b70a9b78STyler Retzlaff rte_cpuset_t *cpuset) 354b70a9b78STyler Retzlaff { 355b70a9b78STyler Retzlaff return pthread_getaffinity_np((pthread_t)thread_id.opaque_id, 356b70a9b78STyler Retzlaff sizeof(*cpuset), cpuset); 357b70a9b78STyler Retzlaff } 358