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