xref: /dpdk/lib/eal/unix/rte_thread.c (revision af0785a2447b307965377b62f46a5f39457a85a3)
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 				&param.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, &param);
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 		&param);
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, &param.sched_priority,
266 		&policy);
267 	if (ret != 0)
268 		return ret;
269 
270 	return pthread_setschedparam((pthread_t)thread_id.opaque_id, policy,
271 		&param);
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