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