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