1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright 2021 Mellanox Technologies, Ltd 3 * Copyright (C) 2022 Microsoft Corporation 4 */ 5 6 #include <errno.h> 7 #include <pthread.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include <rte_errno.h> 12 #include <rte_log.h> 13 #include <rte_thread.h> 14 15 struct eal_tls_key { 16 pthread_key_t thread_index; 17 }; 18 19 struct thread_routine_ctx { 20 rte_thread_func thread_func; 21 void *routine_args; 22 }; 23 24 static int 25 thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, int *os_pri, 26 int *pol) 27 { 28 /* Clear the output parameters. */ 29 *os_pri = sched_get_priority_min(SCHED_OTHER) - 1; 30 *pol = -1; 31 32 switch (eal_pri) { 33 case RTE_THREAD_PRIORITY_NORMAL: 34 *pol = SCHED_OTHER; 35 36 /* 37 * Choose the middle of the range to represent the priority 38 * 'normal'. 39 * On Linux, this should be 0, since both 40 * sched_get_priority_min/_max return 0 for SCHED_OTHER. 41 */ 42 *os_pri = (sched_get_priority_min(SCHED_OTHER) + 43 sched_get_priority_max(SCHED_OTHER)) / 2; 44 break; 45 case RTE_THREAD_PRIORITY_REALTIME_CRITICAL: 46 *pol = SCHED_RR; 47 *os_pri = sched_get_priority_max(SCHED_RR); 48 break; 49 default: 50 RTE_LOG(DEBUG, EAL, "The requested priority value is invalid.\n"); 51 return EINVAL; 52 } 53 54 return 0; 55 } 56 57 static int 58 thread_map_os_priority_to_eal_priority(int policy, int os_pri, 59 enum rte_thread_priority *eal_pri) 60 { 61 switch (policy) { 62 case SCHED_OTHER: 63 if (os_pri == (sched_get_priority_min(SCHED_OTHER) + 64 sched_get_priority_max(SCHED_OTHER)) / 2) { 65 *eal_pri = RTE_THREAD_PRIORITY_NORMAL; 66 return 0; 67 } 68 break; 69 case SCHED_RR: 70 if (os_pri == sched_get_priority_max(SCHED_RR)) { 71 *eal_pri = RTE_THREAD_PRIORITY_REALTIME_CRITICAL; 72 return 0; 73 } 74 break; 75 default: 76 RTE_LOG(DEBUG, EAL, "The OS priority value does not map to an EAL-defined priority.\n"); 77 return EINVAL; 78 } 79 80 return 0; 81 } 82 83 static void * 84 thread_func_wrapper(void *arg) 85 { 86 struct thread_routine_ctx ctx = *(struct thread_routine_ctx *)arg; 87 88 free(arg); 89 90 return (void *)(uintptr_t)ctx.thread_func(ctx.routine_args); 91 } 92 93 int 94 rte_thread_create(rte_thread_t *thread_id, 95 const rte_thread_attr_t *thread_attr, 96 rte_thread_func thread_func, void *args) 97 { 98 int ret = 0; 99 pthread_attr_t attr; 100 pthread_attr_t *attrp = NULL; 101 struct thread_routine_ctx *ctx; 102 struct sched_param param = { 103 .sched_priority = 0, 104 }; 105 int policy = SCHED_OTHER; 106 107 ctx = calloc(1, sizeof(*ctx)); 108 if (ctx == NULL) { 109 RTE_LOG(DEBUG, EAL, "Insufficient memory for thread context allocations\n"); 110 ret = ENOMEM; 111 goto cleanup; 112 } 113 ctx->routine_args = args; 114 ctx->thread_func = thread_func; 115 116 if (thread_attr != NULL) { 117 ret = pthread_attr_init(&attr); 118 if (ret != 0) { 119 RTE_LOG(DEBUG, EAL, "pthread_attr_init failed\n"); 120 goto cleanup; 121 } 122 123 attrp = &attr; 124 125 /* 126 * Set the inherit scheduler parameter to explicit, 127 * otherwise the priority attribute is ignored. 128 */ 129 ret = pthread_attr_setinheritsched(attrp, 130 PTHREAD_EXPLICIT_SCHED); 131 if (ret != 0) { 132 RTE_LOG(DEBUG, EAL, "pthread_attr_setinheritsched failed\n"); 133 goto cleanup; 134 } 135 136 137 if (thread_attr->priority == 138 RTE_THREAD_PRIORITY_REALTIME_CRITICAL) { 139 ret = ENOTSUP; 140 goto cleanup; 141 } 142 ret = thread_map_priority_to_os_value(thread_attr->priority, 143 ¶m.sched_priority, &policy); 144 if (ret != 0) 145 goto cleanup; 146 147 ret = pthread_attr_setschedpolicy(attrp, policy); 148 if (ret != 0) { 149 RTE_LOG(DEBUG, EAL, "pthread_attr_setschedpolicy failed\n"); 150 goto cleanup; 151 } 152 153 ret = pthread_attr_setschedparam(attrp, ¶m); 154 if (ret != 0) { 155 RTE_LOG(DEBUG, EAL, "pthread_attr_setschedparam failed\n"); 156 goto cleanup; 157 } 158 } 159 160 ret = pthread_create((pthread_t *)&thread_id->opaque_id, attrp, 161 thread_func_wrapper, ctx); 162 if (ret != 0) { 163 RTE_LOG(DEBUG, EAL, "pthread_create failed\n"); 164 goto cleanup; 165 } 166 167 if (thread_attr != NULL && CPU_COUNT(&thread_attr->cpuset) > 0) { 168 ret = rte_thread_set_affinity_by_id(*thread_id, 169 &thread_attr->cpuset); 170 if (ret != 0) { 171 RTE_LOG(DEBUG, EAL, "rte_thread_set_affinity_by_id failed\n"); 172 goto cleanup; 173 } 174 } 175 176 ctx = NULL; 177 cleanup: 178 free(ctx); 179 if (attrp != NULL) 180 pthread_attr_destroy(&attr); 181 182 return ret; 183 } 184 185 int 186 rte_thread_join(rte_thread_t thread_id, uint32_t *value_ptr) 187 { 188 int ret = 0; 189 void *res = (void *)(uintptr_t)0; 190 void **pres = NULL; 191 192 if (value_ptr != NULL) 193 pres = &res; 194 195 ret = pthread_join((pthread_t)thread_id.opaque_id, pres); 196 if (ret != 0) { 197 RTE_LOG(DEBUG, EAL, "pthread_join failed\n"); 198 return ret; 199 } 200 201 if (value_ptr != NULL) 202 *value_ptr = (uint32_t)(uintptr_t)res; 203 204 return 0; 205 } 206 207 int 208 rte_thread_detach(rte_thread_t thread_id) 209 { 210 return pthread_detach((pthread_t)thread_id.opaque_id); 211 } 212 213 int 214 rte_thread_equal(rte_thread_t t1, rte_thread_t t2) 215 { 216 return pthread_equal((pthread_t)t1.opaque_id, (pthread_t)t2.opaque_id); 217 } 218 219 rte_thread_t 220 rte_thread_self(void) 221 { 222 RTE_BUILD_BUG_ON(sizeof(pthread_t) > sizeof(uintptr_t)); 223 224 rte_thread_t thread_id; 225 226 thread_id.opaque_id = (uintptr_t)pthread_self(); 227 228 return thread_id; 229 } 230 231 int 232 rte_thread_get_priority(rte_thread_t thread_id, 233 enum rte_thread_priority *priority) 234 { 235 struct sched_param param; 236 int policy; 237 int ret; 238 239 ret = pthread_getschedparam((pthread_t)thread_id.opaque_id, &policy, 240 ¶m); 241 if (ret != 0) { 242 RTE_LOG(DEBUG, EAL, "pthread_getschedparam failed\n"); 243 goto cleanup; 244 } 245 246 return thread_map_os_priority_to_eal_priority(policy, 247 param.sched_priority, priority); 248 249 cleanup: 250 return ret; 251 } 252 253 int 254 rte_thread_set_priority(rte_thread_t thread_id, 255 enum rte_thread_priority priority) 256 { 257 struct sched_param param; 258 int policy; 259 int ret; 260 261 /* Realtime priority can cause crashes on non-Windows platforms. */ 262 if (priority == RTE_THREAD_PRIORITY_REALTIME_CRITICAL) 263 return ENOTSUP; 264 265 ret = thread_map_priority_to_os_value(priority, ¶m.sched_priority, 266 &policy); 267 if (ret != 0) 268 return ret; 269 270 return pthread_setschedparam((pthread_t)thread_id.opaque_id, policy, 271 ¶m); 272 } 273 274 int 275 rte_thread_key_create(rte_thread_key *key, void (*destructor)(void *)) 276 { 277 int err; 278 279 *key = malloc(sizeof(**key)); 280 if ((*key) == NULL) { 281 RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n"); 282 rte_errno = ENOMEM; 283 return -1; 284 } 285 err = pthread_key_create(&((*key)->thread_index), destructor); 286 if (err) { 287 RTE_LOG(DEBUG, EAL, "pthread_key_create failed: %s\n", 288 strerror(err)); 289 free(*key); 290 rte_errno = ENOEXEC; 291 return -1; 292 } 293 return 0; 294 } 295 296 int 297 rte_thread_key_delete(rte_thread_key key) 298 { 299 int err; 300 301 if (!key) { 302 RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); 303 rte_errno = EINVAL; 304 return -1; 305 } 306 err = pthread_key_delete(key->thread_index); 307 if (err) { 308 RTE_LOG(DEBUG, EAL, "pthread_key_delete failed: %s\n", 309 strerror(err)); 310 free(key); 311 rte_errno = ENOEXEC; 312 return -1; 313 } 314 free(key); 315 return 0; 316 } 317 318 int 319 rte_thread_value_set(rte_thread_key key, const void *value) 320 { 321 int err; 322 323 if (!key) { 324 RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); 325 rte_errno = EINVAL; 326 return -1; 327 } 328 err = pthread_setspecific(key->thread_index, value); 329 if (err) { 330 RTE_LOG(DEBUG, EAL, "pthread_setspecific failed: %s\n", 331 strerror(err)); 332 rte_errno = ENOEXEC; 333 return -1; 334 } 335 return 0; 336 } 337 338 void * 339 rte_thread_value_get(rte_thread_key key) 340 { 341 if (!key) { 342 RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n"); 343 rte_errno = EINVAL; 344 return NULL; 345 } 346 return pthread_getspecific(key->thread_index); 347 } 348 349 int 350 rte_thread_set_affinity_by_id(rte_thread_t thread_id, 351 const rte_cpuset_t *cpuset) 352 { 353 return pthread_setaffinity_np((pthread_t)thread_id.opaque_id, 354 sizeof(*cpuset), cpuset); 355 } 356 357 int 358 rte_thread_get_affinity_by_id(rte_thread_t thread_id, 359 rte_cpuset_t *cpuset) 360 { 361 return pthread_getaffinity_np((pthread_t)thread_id.opaque_id, 362 sizeof(*cpuset), cpuset); 363 } 364