xref: /dpdk/lib/eal/windows/rte_thread.c (revision 8f1d23ece06adff5eae9f1b4365bdbbd3abee2b2)
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 
8 #include <rte_common.h>
9 #include <rte_errno.h>
10 #include <rte_thread.h>
11 
12 #include "eal_windows.h"
13 
14 struct eal_tls_key {
15 	DWORD thread_index;
16 };
17 
18 /* Translates the most common error codes related to threads */
19 static int
20 thread_translate_win32_error(DWORD error)
21 {
22 	switch (error) {
23 	case ERROR_SUCCESS:
24 		return 0;
25 
26 	case ERROR_INVALID_PARAMETER:
27 		return EINVAL;
28 
29 	case ERROR_INVALID_HANDLE:
30 		return EFAULT;
31 
32 	case ERROR_NOT_ENOUGH_MEMORY:
33 		/* FALLTHROUGH */
34 	case ERROR_NO_SYSTEM_RESOURCES:
35 		return ENOMEM;
36 
37 	case ERROR_PRIVILEGE_NOT_HELD:
38 		/* FALLTHROUGH */
39 	case ERROR_ACCESS_DENIED:
40 		return EACCES;
41 
42 	case ERROR_ALREADY_EXISTS:
43 		return EEXIST;
44 
45 	case ERROR_POSSIBLE_DEADLOCK:
46 		return EDEADLK;
47 
48 	case ERROR_INVALID_FUNCTION:
49 		/* FALLTHROUGH */
50 	case ERROR_CALL_NOT_IMPLEMENTED:
51 		return ENOSYS;
52 	}
53 
54 	return EINVAL;
55 }
56 
57 static int
58 thread_log_last_error(const char *message)
59 {
60 	DWORD error = GetLastError();
61 	RTE_LOG(DEBUG, EAL, "GetLastError()=%lu: %s\n", error, message);
62 
63 	return thread_translate_win32_error(error);
64 }
65 
66 static int
67 thread_map_priority_to_os_value(enum rte_thread_priority eal_pri, int *os_pri,
68 	DWORD *pri_class)
69 {
70 	/* Clear the output parameters. */
71 	*os_pri = -1;
72 	*pri_class = -1;
73 
74 	switch (eal_pri) {
75 	case RTE_THREAD_PRIORITY_NORMAL:
76 		*pri_class = NORMAL_PRIORITY_CLASS;
77 		*os_pri = THREAD_PRIORITY_NORMAL;
78 		break;
79 	case RTE_THREAD_PRIORITY_REALTIME_CRITICAL:
80 		*pri_class = REALTIME_PRIORITY_CLASS;
81 		*os_pri = THREAD_PRIORITY_TIME_CRITICAL;
82 		break;
83 	default:
84 		RTE_LOG(DEBUG, EAL, "The requested priority value is invalid.\n");
85 		return EINVAL;
86 	}
87 
88 	return 0;
89 }
90 
91 static int
92 thread_map_os_priority_to_eal_value(int os_pri, DWORD pri_class,
93 	enum rte_thread_priority *eal_pri)
94 {
95 	switch (pri_class) {
96 	case NORMAL_PRIORITY_CLASS:
97 		if (os_pri == THREAD_PRIORITY_NORMAL) {
98 			*eal_pri = RTE_THREAD_PRIORITY_NORMAL;
99 			return 0;
100 		}
101 		break;
102 	case HIGH_PRIORITY_CLASS:
103 		RTE_LOG(WARNING, EAL, "The OS priority class is high not real-time.\n");
104 		/* FALLTHROUGH */
105 	case REALTIME_PRIORITY_CLASS:
106 		if (os_pri == THREAD_PRIORITY_TIME_CRITICAL) {
107 			*eal_pri = RTE_THREAD_PRIORITY_REALTIME_CRITICAL;
108 			return 0;
109 		}
110 		break;
111 	default:
112 		RTE_LOG(DEBUG, EAL, "The OS priority value does not map to an EAL-defined priority.\n");
113 		return EINVAL;
114 	}
115 
116 	return 0;
117 }
118 
119 rte_thread_t
120 rte_thread_self(void)
121 {
122 	rte_thread_t thread_id;
123 
124 	thread_id.opaque_id = GetCurrentThreadId();
125 
126 	return thread_id;
127 }
128 
129 int
130 rte_thread_get_priority(rte_thread_t thread_id,
131 	enum rte_thread_priority *priority)
132 {
133 	HANDLE thread_handle = NULL;
134 	DWORD pri_class;
135 	int os_pri;
136 	int ret;
137 
138 	pri_class = GetPriorityClass(GetCurrentProcess());
139 	if (pri_class == 0) {
140 		ret = thread_log_last_error("GetPriorityClass()");
141 		goto cleanup;
142 	}
143 
144 	thread_handle = OpenThread(THREAD_SET_INFORMATION |
145 		THREAD_QUERY_INFORMATION, FALSE, thread_id.opaque_id);
146 	if (thread_handle == NULL) {
147 		ret = thread_log_last_error("OpenThread()");
148 		goto cleanup;
149 	}
150 
151 	os_pri = GetThreadPriority(thread_handle);
152 	if (os_pri == THREAD_PRIORITY_ERROR_RETURN) {
153 		ret = thread_log_last_error("GetThreadPriority()");
154 		goto cleanup;
155 	}
156 
157 	ret = thread_map_os_priority_to_eal_value(os_pri, pri_class, priority);
158 	if (ret != 0)
159 		goto cleanup;
160 
161 cleanup:
162 	if (thread_handle != NULL)
163 		CloseHandle(thread_handle);
164 
165 	return ret;
166 }
167 
168 int
169 rte_thread_set_priority(rte_thread_t thread_id,
170 			enum rte_thread_priority priority)
171 {
172 	HANDLE thread_handle;
173 	DWORD priority_class;
174 	int os_priority;
175 	int ret = 0;
176 
177 	thread_handle = OpenThread(THREAD_SET_INFORMATION |
178 		THREAD_QUERY_INFORMATION, FALSE, thread_id.opaque_id);
179 	if (thread_handle == NULL) {
180 		ret = thread_log_last_error("OpenThread()");
181 		goto cleanup;
182 	}
183 
184 	ret = thread_map_priority_to_os_value(priority, &os_priority,
185 		&priority_class);
186 	if (ret != 0)
187 		goto cleanup;
188 
189 	if (!SetPriorityClass(GetCurrentProcess(), priority_class)) {
190 		ret = thread_log_last_error("SetPriorityClass()");
191 		goto cleanup;
192 	}
193 
194 	if (!SetThreadPriority(thread_handle, os_priority)) {
195 		ret = thread_log_last_error("SetThreadPriority()");
196 		goto cleanup;
197 	}
198 
199 cleanup:
200 	if (thread_handle != NULL)
201 		CloseHandle(thread_handle);
202 
203 	return ret;
204 }
205 
206 int
207 rte_thread_key_create(rte_thread_key *key,
208 		__rte_unused void (*destructor)(void *))
209 {
210 	*key = malloc(sizeof(**key));
211 	if ((*key) == NULL) {
212 		RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n");
213 		rte_errno = ENOMEM;
214 		return -1;
215 	}
216 	(*key)->thread_index = TlsAlloc();
217 	if ((*key)->thread_index == TLS_OUT_OF_INDEXES) {
218 		RTE_LOG_WIN32_ERR("TlsAlloc()");
219 		free(*key);
220 		rte_errno = ENOEXEC;
221 		return -1;
222 	}
223 	return 0;
224 }
225 
226 int
227 rte_thread_key_delete(rte_thread_key key)
228 {
229 	if (!key) {
230 		RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n");
231 		rte_errno = EINVAL;
232 		return -1;
233 	}
234 	if (!TlsFree(key->thread_index)) {
235 		RTE_LOG_WIN32_ERR("TlsFree()");
236 		free(key);
237 		rte_errno = ENOEXEC;
238 		return -1;
239 	}
240 	free(key);
241 	return 0;
242 }
243 
244 int
245 rte_thread_value_set(rte_thread_key key, const void *value)
246 {
247 	char *p;
248 
249 	if (!key) {
250 		RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n");
251 		rte_errno = EINVAL;
252 		return -1;
253 	}
254 	/* discard const qualifier */
255 	p = (char *) (uintptr_t) value;
256 	if (!TlsSetValue(key->thread_index, p)) {
257 		RTE_LOG_WIN32_ERR("TlsSetValue()");
258 		rte_errno = ENOEXEC;
259 		return -1;
260 	}
261 	return 0;
262 }
263 
264 void *
265 rte_thread_value_get(rte_thread_key key)
266 {
267 	void *output;
268 
269 	if (!key) {
270 		RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n");
271 		rte_errno = EINVAL;
272 		return NULL;
273 	}
274 	output = TlsGetValue(key->thread_index);
275 	if (GetLastError() != ERROR_SUCCESS) {
276 		RTE_LOG_WIN32_ERR("TlsGetValue()");
277 		rte_errno = ENOEXEC;
278 		return NULL;
279 	}
280 	return output;
281 }
282 
283 static int
284 convert_cpuset_to_affinity(const rte_cpuset_t *cpuset,
285 		PGROUP_AFFINITY affinity)
286 {
287 	int ret = 0;
288 	PGROUP_AFFINITY cpu_affinity = NULL;
289 	unsigned int cpu_idx;
290 
291 	memset(affinity, 0, sizeof(GROUP_AFFINITY));
292 	affinity->Group = (USHORT)-1;
293 
294 	/* Check that all cpus of the set belong to the same processor group and
295 	 * accumulate thread affinity to be applied.
296 	 */
297 	for (cpu_idx = 0; cpu_idx < CPU_SETSIZE; cpu_idx++) {
298 		if (!CPU_ISSET(cpu_idx, cpuset))
299 			continue;
300 
301 		cpu_affinity = eal_get_cpu_affinity(cpu_idx);
302 
303 		if (affinity->Group == (USHORT)-1) {
304 			affinity->Group = cpu_affinity->Group;
305 		} else if (affinity->Group != cpu_affinity->Group) {
306 			RTE_LOG(DEBUG, EAL, "All processors must belong to the same processor group\n");
307 			ret = ENOTSUP;
308 			goto cleanup;
309 		}
310 
311 		affinity->Mask |= cpu_affinity->Mask;
312 	}
313 
314 	if (affinity->Mask == 0) {
315 		ret = EINVAL;
316 		goto cleanup;
317 	}
318 
319 cleanup:
320 	return ret;
321 }
322 
323 int
324 rte_thread_set_affinity_by_id(rte_thread_t thread_id,
325 		const rte_cpuset_t *cpuset)
326 {
327 	int ret = 0;
328 	GROUP_AFFINITY thread_affinity;
329 	HANDLE thread_handle = NULL;
330 
331 	if (cpuset == NULL) {
332 		ret = EINVAL;
333 		goto cleanup;
334 	}
335 
336 	ret = convert_cpuset_to_affinity(cpuset, &thread_affinity);
337 	if (ret != 0) {
338 		RTE_LOG(DEBUG, EAL, "Unable to convert cpuset to thread affinity\n");
339 		goto cleanup;
340 	}
341 
342 	thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE,
343 		thread_id.opaque_id);
344 	if (thread_handle == NULL) {
345 		ret = thread_log_last_error("OpenThread()");
346 		goto cleanup;
347 	}
348 
349 	if (!SetThreadGroupAffinity(thread_handle, &thread_affinity, NULL)) {
350 		ret = thread_log_last_error("SetThreadGroupAffinity()");
351 		goto cleanup;
352 	}
353 
354 cleanup:
355 	if (thread_handle != NULL) {
356 		CloseHandle(thread_handle);
357 		thread_handle = NULL;
358 	}
359 
360 	return ret;
361 }
362 
363 int
364 rte_thread_get_affinity_by_id(rte_thread_t thread_id,
365 		rte_cpuset_t *cpuset)
366 {
367 	HANDLE thread_handle = NULL;
368 	PGROUP_AFFINITY cpu_affinity;
369 	GROUP_AFFINITY thread_affinity;
370 	unsigned int cpu_idx;
371 	int ret = 0;
372 
373 	if (cpuset == NULL) {
374 		ret = EINVAL;
375 		goto cleanup;
376 	}
377 
378 	thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE,
379 		thread_id.opaque_id);
380 	if (thread_handle == NULL) {
381 		ret = thread_log_last_error("OpenThread()");
382 		goto cleanup;
383 	}
384 
385 	/* obtain previous thread affinity */
386 	if (!GetThreadGroupAffinity(thread_handle, &thread_affinity)) {
387 		ret = thread_log_last_error("GetThreadGroupAffinity()");
388 		goto cleanup;
389 	}
390 
391 	CPU_ZERO(cpuset);
392 
393 	/* Convert affinity to DPDK cpu set */
394 	for (cpu_idx = 0; cpu_idx < CPU_SETSIZE; cpu_idx++) {
395 
396 		cpu_affinity = eal_get_cpu_affinity(cpu_idx);
397 
398 		if ((cpu_affinity->Group == thread_affinity.Group) &&
399 		   ((cpu_affinity->Mask & thread_affinity.Mask) != 0)) {
400 			CPU_SET(cpu_idx, cpuset);
401 		}
402 	}
403 
404 cleanup:
405 	if (thread_handle != NULL) {
406 		CloseHandle(thread_handle);
407 		thread_handle = NULL;
408 	}
409 	return ret;
410 }
411