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