xref: /dpdk/lib/eal/windows/rte_thread.c (revision 592ab76f9f0f41993bebb44da85c37750a93ece9)
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 rte_thread_t
65 rte_thread_self(void)
66 {
67 	rte_thread_t thread_id;
68 
69 	thread_id.opaque_id = GetCurrentThreadId();
70 
71 	return thread_id;
72 }
73 
74 int
75 rte_thread_key_create(rte_thread_key *key,
76 		__rte_unused void (*destructor)(void *))
77 {
78 	*key = malloc(sizeof(**key));
79 	if ((*key) == NULL) {
80 		RTE_LOG(DEBUG, EAL, "Cannot allocate TLS key.\n");
81 		rte_errno = ENOMEM;
82 		return -1;
83 	}
84 	(*key)->thread_index = TlsAlloc();
85 	if ((*key)->thread_index == TLS_OUT_OF_INDEXES) {
86 		RTE_LOG_WIN32_ERR("TlsAlloc()");
87 		free(*key);
88 		rte_errno = ENOEXEC;
89 		return -1;
90 	}
91 	return 0;
92 }
93 
94 int
95 rte_thread_key_delete(rte_thread_key key)
96 {
97 	if (!key) {
98 		RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n");
99 		rte_errno = EINVAL;
100 		return -1;
101 	}
102 	if (!TlsFree(key->thread_index)) {
103 		RTE_LOG_WIN32_ERR("TlsFree()");
104 		free(key);
105 		rte_errno = ENOEXEC;
106 		return -1;
107 	}
108 	free(key);
109 	return 0;
110 }
111 
112 int
113 rte_thread_value_set(rte_thread_key key, const void *value)
114 {
115 	char *p;
116 
117 	if (!key) {
118 		RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n");
119 		rte_errno = EINVAL;
120 		return -1;
121 	}
122 	/* discard const qualifier */
123 	p = (char *) (uintptr_t) value;
124 	if (!TlsSetValue(key->thread_index, p)) {
125 		RTE_LOG_WIN32_ERR("TlsSetValue()");
126 		rte_errno = ENOEXEC;
127 		return -1;
128 	}
129 	return 0;
130 }
131 
132 void *
133 rte_thread_value_get(rte_thread_key key)
134 {
135 	void *output;
136 
137 	if (!key) {
138 		RTE_LOG(DEBUG, EAL, "Invalid TLS key.\n");
139 		rte_errno = EINVAL;
140 		return NULL;
141 	}
142 	output = TlsGetValue(key->thread_index);
143 	if (GetLastError() != ERROR_SUCCESS) {
144 		RTE_LOG_WIN32_ERR("TlsGetValue()");
145 		rte_errno = ENOEXEC;
146 		return NULL;
147 	}
148 	return output;
149 }
150 
151 static int
152 convert_cpuset_to_affinity(const rte_cpuset_t *cpuset,
153 		PGROUP_AFFINITY affinity)
154 {
155 	int ret = 0;
156 	PGROUP_AFFINITY cpu_affinity = NULL;
157 	unsigned int cpu_idx;
158 
159 	memset(affinity, 0, sizeof(GROUP_AFFINITY));
160 	affinity->Group = (USHORT)-1;
161 
162 	/* Check that all cpus of the set belong to the same processor group and
163 	 * accumulate thread affinity to be applied.
164 	 */
165 	for (cpu_idx = 0; cpu_idx < CPU_SETSIZE; cpu_idx++) {
166 		if (!CPU_ISSET(cpu_idx, cpuset))
167 			continue;
168 
169 		cpu_affinity = eal_get_cpu_affinity(cpu_idx);
170 
171 		if (affinity->Group == (USHORT)-1) {
172 			affinity->Group = cpu_affinity->Group;
173 		} else if (affinity->Group != cpu_affinity->Group) {
174 			RTE_LOG(DEBUG, EAL, "All processors must belong to the same processor group\n");
175 			ret = ENOTSUP;
176 			goto cleanup;
177 		}
178 
179 		affinity->Mask |= cpu_affinity->Mask;
180 	}
181 
182 	if (affinity->Mask == 0) {
183 		ret = EINVAL;
184 		goto cleanup;
185 	}
186 
187 cleanup:
188 	return ret;
189 }
190 
191 int
192 rte_thread_set_affinity_by_id(rte_thread_t thread_id,
193 		const rte_cpuset_t *cpuset)
194 {
195 	int ret = 0;
196 	GROUP_AFFINITY thread_affinity;
197 	HANDLE thread_handle = NULL;
198 
199 	if (cpuset == NULL) {
200 		ret = EINVAL;
201 		goto cleanup;
202 	}
203 
204 	ret = convert_cpuset_to_affinity(cpuset, &thread_affinity);
205 	if (ret != 0) {
206 		RTE_LOG(DEBUG, EAL, "Unable to convert cpuset to thread affinity\n");
207 		goto cleanup;
208 	}
209 
210 	thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE,
211 		thread_id.opaque_id);
212 	if (thread_handle == NULL) {
213 		ret = thread_log_last_error("OpenThread()");
214 		goto cleanup;
215 	}
216 
217 	if (!SetThreadGroupAffinity(thread_handle, &thread_affinity, NULL)) {
218 		ret = thread_log_last_error("SetThreadGroupAffinity()");
219 		goto cleanup;
220 	}
221 
222 cleanup:
223 	if (thread_handle != NULL) {
224 		CloseHandle(thread_handle);
225 		thread_handle = NULL;
226 	}
227 
228 	return ret;
229 }
230 
231 int
232 rte_thread_get_affinity_by_id(rte_thread_t thread_id,
233 		rte_cpuset_t *cpuset)
234 {
235 	HANDLE thread_handle = NULL;
236 	PGROUP_AFFINITY cpu_affinity;
237 	GROUP_AFFINITY thread_affinity;
238 	unsigned int cpu_idx;
239 	int ret = 0;
240 
241 	if (cpuset == NULL) {
242 		ret = EINVAL;
243 		goto cleanup;
244 	}
245 
246 	thread_handle = OpenThread(THREAD_ALL_ACCESS, FALSE,
247 		thread_id.opaque_id);
248 	if (thread_handle == NULL) {
249 		ret = thread_log_last_error("OpenThread()");
250 		goto cleanup;
251 	}
252 
253 	/* obtain previous thread affinity */
254 	if (!GetThreadGroupAffinity(thread_handle, &thread_affinity)) {
255 		ret = thread_log_last_error("GetThreadGroupAffinity()");
256 		goto cleanup;
257 	}
258 
259 	CPU_ZERO(cpuset);
260 
261 	/* Convert affinity to DPDK cpu set */
262 	for (cpu_idx = 0; cpu_idx < CPU_SETSIZE; cpu_idx++) {
263 
264 		cpu_affinity = eal_get_cpu_affinity(cpu_idx);
265 
266 		if ((cpu_affinity->Group == thread_affinity.Group) &&
267 		   ((cpu_affinity->Mask & thread_affinity.Mask) != 0)) {
268 			CPU_SET(cpu_idx, cpuset);
269 		}
270 	}
271 
272 cleanup:
273 	if (thread_handle != NULL) {
274 		CloseHandle(thread_handle);
275 		thread_handle = NULL;
276 	}
277 	return ret;
278 }
279