eal/riscv: support RISC-V architecture
[dpdk.git] / lib / eal / windows / rte_thread.c
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 }