/src/suricata8/src/util-affinity.c
Line | Count | Source |
1 | | /* Copyright (C) 2010-2016 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** \file |
19 | | * |
20 | | * \author Eric Leblond <eric@regit.org> |
21 | | * |
22 | | * CPU affinity related code and helper. |
23 | | */ |
24 | | |
25 | | #include "suricata-common.h" |
26 | | #include "suricata.h" |
27 | | #define _THREAD_AFFINITY |
28 | | #include "util-affinity.h" |
29 | | #include "conf.h" |
30 | | #include "conf-yaml-loader.h" |
31 | | #include "runmodes.h" |
32 | | #include "util-cpu.h" |
33 | | #include "util-byte.h" |
34 | | #include "util-debug.h" |
35 | | #include "util-dpdk.h" |
36 | | #include "util-unittest.h" |
37 | | |
38 | | ThreadsAffinityType thread_affinity[MAX_CPU_SET] = { |
39 | | { |
40 | | .name = "receive-cpu-set", |
41 | | .mode_flag = EXCLUSIVE_AFFINITY, |
42 | | .prio = PRIO_MEDIUM, |
43 | | .lcpu = { 0 }, |
44 | | }, |
45 | | { |
46 | | .name = "worker-cpu-set", |
47 | | .mode_flag = EXCLUSIVE_AFFINITY, |
48 | | .prio = PRIO_MEDIUM, |
49 | | .lcpu = { 0 }, |
50 | | }, |
51 | | { |
52 | | .name = "verdict-cpu-set", |
53 | | .mode_flag = BALANCED_AFFINITY, |
54 | | .prio = PRIO_MEDIUM, |
55 | | .lcpu = { 0 }, |
56 | | }, |
57 | | { |
58 | | .name = "management-cpu-set", |
59 | | .mode_flag = BALANCED_AFFINITY, |
60 | | .prio = PRIO_MEDIUM, |
61 | | .lcpu = { 0 }, |
62 | | }, |
63 | | |
64 | | }; |
65 | | |
66 | | int thread_affinity_init_done = 0; |
67 | | |
68 | | #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun |
69 | | #ifdef HAVE_HWLOC |
70 | | static hwloc_topology_t topology = NULL; |
71 | | #endif /* HAVE_HWLOC */ |
72 | | #endif /* OS_WIN32 and __OpenBSD__ */ |
73 | | |
74 | | static ThreadsAffinityType *AllocAndInitAffinityType( |
75 | | const char *name, const char *interface_name, ThreadsAffinityType *parent) |
76 | 0 | { |
77 | 0 | ThreadsAffinityType *new_affinity = SCCalloc(1, sizeof(ThreadsAffinityType)); |
78 | 0 | if (new_affinity == NULL) { |
79 | 0 | FatalError("Unable to allocate memory for new CPU affinity type"); |
80 | 0 | } |
81 | | |
82 | 0 | new_affinity->name = SCStrdup(interface_name); |
83 | 0 | if (new_affinity->name == NULL) { |
84 | 0 | FatalError("Unable to allocate memory for new CPU affinity type name"); |
85 | 0 | } |
86 | 0 | new_affinity->parent = parent; |
87 | 0 | new_affinity->mode_flag = EXCLUSIVE_AFFINITY; |
88 | 0 | new_affinity->prio = PRIO_MEDIUM; |
89 | 0 | for (int i = 0; i < MAX_NUMA_NODES; i++) { |
90 | 0 | new_affinity->lcpu[i] = 0; |
91 | 0 | } |
92 | |
|
93 | 0 | if (parent != NULL) { |
94 | 0 | if (parent->nb_children == parent->nb_children_capacity) { |
95 | 0 | if (parent->nb_children_capacity == 0) { |
96 | 0 | parent->nb_children_capacity = 2; |
97 | 0 | } else { |
98 | 0 | parent->nb_children_capacity *= 2; |
99 | 0 | } |
100 | 0 | void *p = SCRealloc( |
101 | 0 | parent->children, parent->nb_children_capacity * sizeof(ThreadsAffinityType *)); |
102 | 0 | if (p == NULL) { |
103 | 0 | FatalError("Unable to reallocate memory for children CPU affinity types"); |
104 | 0 | } |
105 | 0 | parent->children = p; |
106 | 0 | } |
107 | 0 | parent->children[parent->nb_children++] = new_affinity; |
108 | 0 | } |
109 | | |
110 | 0 | return new_affinity; |
111 | 0 | } |
112 | | |
113 | | ThreadsAffinityType *FindAffinityByInterface( |
114 | | ThreadsAffinityType *parent, const char *interface_name) |
115 | 0 | { |
116 | 0 | if (parent == NULL || interface_name == NULL || parent->nb_children == 0 || |
117 | 0 | parent->children == NULL) { |
118 | 0 | return NULL; |
119 | 0 | } |
120 | | |
121 | 0 | for (uint32_t i = 0; i < parent->nb_children; i++) { |
122 | 0 | if (parent->children[i] && parent->children[i]->name && |
123 | 0 | strcmp(parent->children[i]->name, interface_name) == 0) { |
124 | 0 | return parent->children[i]; |
125 | 0 | } |
126 | 0 | } |
127 | 0 | return NULL; |
128 | 0 | } |
129 | | |
130 | | /** |
131 | | * \brief Find affinity by name (*-cpu-set name) and an interface name. |
132 | | * \param name the name of the affinity (e.g. worker-cpu-set, receive-cpu-set). |
133 | | * The name is required and cannot be NULL. |
134 | | * \param interface_name the name of the interface. |
135 | | * If NULL, the affinity is looked up by name only. |
136 | | * \retval a pointer to the affinity or NULL if not found |
137 | | */ |
138 | | ThreadsAffinityType *GetAffinityTypeForNameAndIface(const char *name, const char *interface_name) |
139 | 0 | { |
140 | 0 | if (name == NULL || *name == '\0') { |
141 | 0 | return NULL; |
142 | 0 | } |
143 | | |
144 | 0 | ThreadsAffinityType *parent_affinity = NULL; |
145 | 0 | for (int i = 0; i < MAX_CPU_SET; i++) { |
146 | 0 | if (thread_affinity[i].name != NULL && strcmp(thread_affinity[i].name, name) == 0) { |
147 | 0 | parent_affinity = &thread_affinity[i]; |
148 | 0 | break; |
149 | 0 | } |
150 | 0 | } |
151 | |
|
152 | 0 | if (parent_affinity == NULL) { |
153 | 0 | SCLogError("CPU affinity with name \"%s\" not found", name); |
154 | 0 | return NULL; |
155 | 0 | } |
156 | | |
157 | 0 | if (interface_name != NULL) { |
158 | 0 | ThreadsAffinityType *child_affinity = |
159 | 0 | FindAffinityByInterface(parent_affinity, interface_name); |
160 | | // found or not found, it is returned |
161 | 0 | return child_affinity; |
162 | 0 | } |
163 | | |
164 | 0 | return parent_affinity; |
165 | 0 | } |
166 | | |
167 | | /** |
168 | | * \brief Finds affinity by its name and interface name. |
169 | | * Interfaces are children of cpu-set names. If the queried interface is not |
170 | | * found, then it is allocated, initialized and assigned to the queried cpu-set. |
171 | | * \param name the name of the affinity (e.g. worker-cpu-set, receive-cpu-set). |
172 | | * The name is required and cannot be NULL. |
173 | | * \param interface_name the name of the interface. |
174 | | * If NULL, the affinity is looked up by name only. |
175 | | * \retval a pointer to the affinity or NULL if not found |
176 | | */ |
177 | | ThreadsAffinityType *GetOrAllocAffinityTypeForIfaceOfName( |
178 | | const char *name, const char *interface_name) |
179 | 0 | { |
180 | 0 | int i; |
181 | 0 | ThreadsAffinityType *parent_affinity = NULL; |
182 | |
|
183 | 0 | for (i = 0; i < MAX_CPU_SET; i++) { |
184 | 0 | if (strcmp(thread_affinity[i].name, name) == 0) { |
185 | 0 | parent_affinity = &thread_affinity[i]; |
186 | 0 | break; |
187 | 0 | } |
188 | 0 | } |
189 | |
|
190 | 0 | if (parent_affinity == NULL) { |
191 | 0 | SCLogError("CPU affinity with name \"%s\" not found", name); |
192 | 0 | return NULL; |
193 | 0 | } |
194 | | |
195 | 0 | if (interface_name != NULL) { |
196 | 0 | ThreadsAffinityType *child_affinity = |
197 | 0 | FindAffinityByInterface(parent_affinity, interface_name); |
198 | 0 | if (child_affinity != NULL) { |
199 | 0 | return child_affinity; |
200 | 0 | } |
201 | | |
202 | | // If not found, allocate and initialize a new child affinity |
203 | 0 | return AllocAndInitAffinityType(name, interface_name, parent_affinity); |
204 | 0 | } |
205 | | |
206 | 0 | return parent_affinity; |
207 | 0 | } |
208 | | |
209 | | #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun |
210 | | static void AffinitySetupInit(void) |
211 | 0 | { |
212 | 0 | int i, j; |
213 | 0 | int ncpu = UtilCpuGetNumProcessorsOnline(); |
214 | |
|
215 | 0 | SCLogDebug("Initialize CPU affinity setup"); |
216 | | /* be conservative relatively to OS: use all cpus by default */ |
217 | 0 | for (i = 0; i < MAX_CPU_SET; i++) { |
218 | 0 | cpu_set_t *cs = &thread_affinity[i].cpu_set; |
219 | 0 | CPU_ZERO(cs); |
220 | 0 | for (j = 0; j < ncpu; j++) { |
221 | 0 | CPU_SET(j, cs); |
222 | 0 | } |
223 | 0 | SCMutexInit(&thread_affinity[i].taf_mutex, NULL); |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | | int BuildCpusetWithCallback( |
228 | | const char *name, SCConfNode *node, void (*Callback)(int i, void *data), void *data) |
229 | 0 | { |
230 | 0 | SCConfNode *lnode; |
231 | 0 | TAILQ_FOREACH(lnode, &node->head, next) { |
232 | 0 | uint32_t i; |
233 | 0 | uint32_t a, b; |
234 | 0 | uint32_t stop = 0; |
235 | 0 | uint32_t max = UtilCpuGetNumProcessorsOnline(); |
236 | 0 | if (max > 0) { |
237 | 0 | max--; |
238 | 0 | } |
239 | 0 | if (!strcmp(lnode->val, "all")) { |
240 | 0 | a = 0; |
241 | 0 | b = max; |
242 | 0 | stop = 1; |
243 | 0 | } else if (strchr(lnode->val, '-') != NULL) { |
244 | 0 | char *sep = strchr(lnode->val, '-'); |
245 | 0 | if (StringParseUint32(&a, 10, sep - lnode->val, lnode->val) <= 0) { |
246 | 0 | SCLogError("%s: invalid cpu range (start invalid): \"%s\"", name, lnode->val); |
247 | 0 | return -1; |
248 | 0 | } |
249 | 0 | if (StringParseUint32(&b, 10, strlen(sep) - 1, sep + 1) <= 0) { |
250 | 0 | SCLogError("%s: invalid cpu range (end invalid): \"%s\"", name, lnode->val); |
251 | 0 | return -1; |
252 | 0 | } |
253 | 0 | if (a > b) { |
254 | 0 | SCLogError("%s: invalid cpu range (bad order): \"%s\"", name, lnode->val); |
255 | 0 | return -1; |
256 | 0 | } |
257 | 0 | if (b > max) { |
258 | 0 | SCLogError("%s: upper bound (%d) of cpu set is too high, only %d cpu(s)", name, b, |
259 | 0 | max + 1); |
260 | 0 | return -1; |
261 | 0 | } |
262 | 0 | } else { |
263 | 0 | if (StringParseUint32(&a, 10, strlen(lnode->val), lnode->val) <= 0) { |
264 | 0 | SCLogError("%s: invalid cpu range (not an integer): \"%s\"", name, lnode->val); |
265 | 0 | return -1; |
266 | 0 | } |
267 | 0 | b = a; |
268 | 0 | } |
269 | 0 | for (i = a; i<= b; i++) { |
270 | 0 | Callback(i, data); |
271 | 0 | } |
272 | 0 | if (stop) { |
273 | 0 | break; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | return 0; |
277 | 0 | } |
278 | | |
279 | | static void AffinityCallback(int i, void *data) |
280 | 0 | { |
281 | 0 | CPU_SET(i, (cpu_set_t *)data); |
282 | 0 | } |
283 | | |
284 | | static int BuildCpuset(const char *name, SCConfNode *node, cpu_set_t *cpu) |
285 | 0 | { |
286 | 0 | return BuildCpusetWithCallback(name, node, AffinityCallback, (void *)cpu); |
287 | 0 | } |
288 | | |
289 | | /** |
290 | | * \brief Get the appropriate set name for a given affinity value. |
291 | | */ |
292 | | static const char *GetAffinitySetName(const char *val) |
293 | 0 | { |
294 | 0 | if (val == NULL) { |
295 | 0 | return NULL; |
296 | 0 | } |
297 | 0 | if (strcmp(val, "decode-cpu-set") == 0 || strcmp(val, "stream-cpu-set") == 0 || |
298 | 0 | strcmp(val, "reject-cpu-set") == 0 || strcmp(val, "output-cpu-set") == 0) { |
299 | 0 | return NULL; |
300 | 0 | } |
301 | | |
302 | 0 | return (strcmp(val, "detect-cpu-set") == 0) ? "worker-cpu-set" : val; |
303 | 0 | } |
304 | | |
305 | | /** |
306 | | * \brief Set up CPU sets for the given affinity type. |
307 | | */ |
308 | | static void SetupCpuSets(ThreadsAffinityType *taf, SCConfNode *affinity, const char *setname) |
309 | 0 | { |
310 | 0 | CPU_ZERO(&taf->cpu_set); |
311 | 0 | SCConfNode *cpu_node = SCConfNodeLookupChild(affinity, "cpu"); |
312 | 0 | if (cpu_node != NULL) { |
313 | 0 | if (BuildCpuset(setname, cpu_node, &taf->cpu_set) < 0) { |
314 | 0 | SCLogWarning("Failed to parse CPU set for %s", setname); |
315 | 0 | } |
316 | 0 | } else { |
317 | 0 | SCLogWarning("Unable to find 'cpu' node for set %s", setname); |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | | /** |
322 | | * \brief Build a priority CPU set for the given priority level. |
323 | | */ |
324 | | static void BuildPriorityCpuset(ThreadsAffinityType *taf, SCConfNode *prio_node, |
325 | | const char *priority, cpu_set_t *cpuset, const char *setname) |
326 | 0 | { |
327 | 0 | SCConfNode *node = SCConfNodeLookupChild(prio_node, priority); |
328 | 0 | if (node != NULL) { |
329 | 0 | if (BuildCpuset(setname, node, cpuset) < 0) { |
330 | 0 | SCLogWarning("Failed to parse %s priority CPU set for %s", priority, setname); |
331 | 0 | } |
332 | 0 | } else { |
333 | 0 | SCLogDebug("Unable to find '%s' priority for set %s", priority, setname); |
334 | 0 | } |
335 | 0 | } |
336 | | |
337 | | /** |
338 | | * \brief Set up the default priority for the given affinity type. |
339 | | * \retval 0 on success, -1 on error |
340 | | */ |
341 | | static int SetupDefaultPriority( |
342 | | ThreadsAffinityType *taf, SCConfNode *prio_node, const char *setname) |
343 | 0 | { |
344 | 0 | SCConfNode *default_node = SCConfNodeLookupChild(prio_node, "default"); |
345 | 0 | if (default_node == NULL) { |
346 | 0 | return 0; |
347 | 0 | } |
348 | | |
349 | 0 | if (strcmp(default_node->val, "low") == 0) { |
350 | 0 | taf->prio = PRIO_LOW; |
351 | 0 | } else if (strcmp(default_node->val, "medium") == 0) { |
352 | 0 | taf->prio = PRIO_MEDIUM; |
353 | 0 | } else if (strcmp(default_node->val, "high") == 0) { |
354 | 0 | taf->prio = PRIO_HIGH; |
355 | 0 | } else { |
356 | 0 | SCLogError("Unknown default CPU affinity priority: %s", default_node->val); |
357 | 0 | return -1; |
358 | 0 | } |
359 | | |
360 | 0 | SCLogConfig("Using default priority '%s' for set %s", default_node->val, setname); |
361 | 0 | return 0; |
362 | 0 | } |
363 | | |
364 | | /** |
365 | | * \brief Set up priority CPU sets for the given affinity type. |
366 | | * \retval 0 on success, -1 on error |
367 | | */ |
368 | | static int SetupAffinityPriority( |
369 | | ThreadsAffinityType *taf, SCConfNode *affinity, const char *setname) |
370 | 0 | { |
371 | 0 | CPU_ZERO(&taf->lowprio_cpu); |
372 | 0 | CPU_ZERO(&taf->medprio_cpu); |
373 | 0 | CPU_ZERO(&taf->hiprio_cpu); |
374 | 0 | SCConfNode *prio_node = SCConfNodeLookupChild(affinity, "prio"); |
375 | 0 | if (prio_node == NULL) { |
376 | 0 | return 0; |
377 | 0 | } |
378 | | |
379 | 0 | BuildPriorityCpuset(taf, prio_node, "low", &taf->lowprio_cpu, setname); |
380 | 0 | BuildPriorityCpuset(taf, prio_node, "medium", &taf->medprio_cpu, setname); |
381 | 0 | BuildPriorityCpuset(taf, prio_node, "high", &taf->hiprio_cpu, setname); |
382 | 0 | return SetupDefaultPriority(taf, prio_node, setname); |
383 | 0 | } |
384 | | |
385 | | /** |
386 | | * \brief Set up CPU affinity mode for the given affinity type. |
387 | | * \retval 0 on success, -1 on error |
388 | | */ |
389 | | static int SetupAffinityMode(ThreadsAffinityType *taf, SCConfNode *affinity) |
390 | 0 | { |
391 | 0 | SCConfNode *mode_node = SCConfNodeLookupChild(affinity, "mode"); |
392 | 0 | if (mode_node == NULL) { |
393 | 0 | return 0; |
394 | 0 | } |
395 | | |
396 | 0 | if (strcmp(mode_node->val, "exclusive") == 0) { |
397 | 0 | taf->mode_flag = EXCLUSIVE_AFFINITY; |
398 | 0 | } else if (strcmp(mode_node->val, "balanced") == 0) { |
399 | 0 | taf->mode_flag = BALANCED_AFFINITY; |
400 | 0 | } else { |
401 | 0 | SCLogError("Unknown CPU affinity mode: %s", mode_node->val); |
402 | 0 | return -1; |
403 | 0 | } |
404 | 0 | return 0; |
405 | 0 | } |
406 | | |
407 | | /** |
408 | | * \brief Set up the number of threads for the given affinity type. |
409 | | * \retval 0 on success, -1 on error |
410 | | */ |
411 | | static int SetupAffinityThreads(ThreadsAffinityType *taf, SCConfNode *affinity) |
412 | 0 | { |
413 | 0 | SCConfNode *threads_node = SCConfNodeLookupChild(affinity, "threads"); |
414 | 0 | if (threads_node == NULL) { |
415 | 0 | return 0; |
416 | 0 | } |
417 | | |
418 | 0 | if (StringParseUint32(&taf->nb_threads, 10, 0, threads_node->val) < 0 || taf->nb_threads == 0) { |
419 | 0 | SCLogError("Invalid thread count: %s", threads_node->val); |
420 | 0 | return -1; |
421 | 0 | } |
422 | 0 | return 0; |
423 | 0 | } |
424 | | |
425 | | /** |
426 | | * \brief Get the YAML path for the given affinity type. |
427 | | * The path is built using the parent name (if available) and the affinity name. |
428 | | * Do not free the returned string. |
429 | | * \param taf the affinity type - if NULL, the path is built for the root node |
430 | | * \return a string containing the YAML path, or NULL if the path is too long |
431 | | */ |
432 | | char *AffinityGetYamlPath(ThreadsAffinityType *taf) |
433 | 0 | { |
434 | 0 | static char rootpath[] = "threading.cpu-affinity"; |
435 | 0 | static char path[1024] = { 0 }; |
436 | 0 | char subpath[256] = { 0 }; |
437 | |
|
438 | 0 | if (taf == NULL) { |
439 | 0 | return rootpath; |
440 | 0 | } |
441 | | |
442 | 0 | if (taf->parent != NULL) { |
443 | 0 | long r = snprintf( |
444 | 0 | subpath, sizeof(subpath), "%s.interface-specific-cpu-set.", taf->parent->name); |
445 | 0 | if (r < 0 || r >= (long)sizeof(subpath)) { |
446 | 0 | SCLogError("Unable to build YAML path for CPU affinity %s.%s", taf->parent->name, |
447 | 0 | taf->name); |
448 | 0 | return NULL; |
449 | 0 | } |
450 | 0 | } else { |
451 | 0 | subpath[0] = '\0'; |
452 | 0 | } |
453 | | |
454 | 0 | long r = snprintf(path, sizeof(path), "%s.%s%s", rootpath, subpath, taf->name); |
455 | 0 | if (r < 0 || r >= (long)sizeof(path)) { |
456 | 0 | SCLogError("Unable to build YAML path for CPU affinity %s", taf->name); |
457 | 0 | return NULL; |
458 | 0 | } |
459 | | |
460 | 0 | return path; |
461 | 0 | } |
462 | | |
463 | | static void ResetCPUs(ThreadsAffinityType *taf) |
464 | 0 | { |
465 | 0 | for (int i = 0; i < MAX_NUMA_NODES; i++) { |
466 | 0 | taf->lcpu[i] = 0; |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | | /** |
471 | | * \brief Check if the set name corresponds to a worker CPU set. |
472 | | */ |
473 | | static bool IsWorkerCpuSet(const char *setname) |
474 | 0 | { |
475 | 0 | return (strcmp(setname, "worker-cpu-set") == 0); |
476 | 0 | } |
477 | | |
478 | | /** |
479 | | * \brief Check if the set name corresponds to a receive CPU set. |
480 | | */ |
481 | | static bool IsReceiveCpuSet(const char *setname) |
482 | 0 | { |
483 | 0 | return (strcmp(setname, "receive-cpu-set") == 0); |
484 | 0 | } |
485 | | |
486 | | /** |
487 | | * \brief Set up affinity configuration for a single interface. |
488 | | */ |
489 | | /** |
490 | | * \brief Set up affinity configuration for a single interface. |
491 | | * \retval 0 on success, -1 on error |
492 | | */ |
493 | | static int SetupSingleIfaceAffinity(ThreadsAffinityType *taf, SCConfNode *iface_node) |
494 | 0 | { |
495 | | // offload to Setup function |
496 | 0 | SCConfNode *child_node; |
497 | 0 | const char *interface_name = NULL; |
498 | 0 | TAILQ_FOREACH (child_node, &iface_node->head, next) { |
499 | 0 | if (strcmp(child_node->name, "interface") == 0) { |
500 | 0 | interface_name = child_node->val; |
501 | 0 | break; |
502 | 0 | } |
503 | 0 | } |
504 | 0 | if (interface_name == NULL) { |
505 | 0 | return 0; |
506 | 0 | } |
507 | | |
508 | 0 | ThreadsAffinityType *iface_taf = |
509 | 0 | GetOrAllocAffinityTypeForIfaceOfName(taf->name, interface_name); |
510 | 0 | if (iface_taf == NULL) { |
511 | 0 | SCLogError("Failed to allocate CPU affinity type for interface: %s", interface_name); |
512 | 0 | return -1; |
513 | 0 | } |
514 | | |
515 | 0 | SetupCpuSets(iface_taf, iface_node, interface_name); |
516 | 0 | if (SetupAffinityPriority(iface_taf, iface_node, interface_name) < 0) { |
517 | 0 | return -1; |
518 | 0 | } |
519 | 0 | if (SetupAffinityMode(iface_taf, iface_node) < 0) { |
520 | 0 | return -1; |
521 | 0 | } |
522 | 0 | if (SetupAffinityThreads(iface_taf, iface_node) < 0) { |
523 | 0 | return -1; |
524 | 0 | } |
525 | 0 | return 0; |
526 | 0 | } |
527 | | |
528 | | /** |
529 | | * \brief Set up per-interface affinity configurations. |
530 | | * \retval 0 on success, -1 on error |
531 | | */ |
532 | | static int SetupPerIfaceAffinity(ThreadsAffinityType *taf, SCConfNode *affinity) |
533 | 0 | { |
534 | 0 | char if_af[] = "interface-specific-cpu-set"; |
535 | 0 | SCConfNode *per_iface_node = SCConfNodeLookupChild(affinity, if_af); |
536 | 0 | if (per_iface_node == NULL) { |
537 | 0 | return 0; |
538 | 0 | } |
539 | | |
540 | 0 | SCConfNode *iface_node; |
541 | 0 | TAILQ_FOREACH (iface_node, &per_iface_node->head, next) { |
542 | 0 | if (strcmp(iface_node->val, "interface") == 0) { |
543 | 0 | if (SetupSingleIfaceAffinity(taf, iface_node) < 0) { |
544 | 0 | return -1; |
545 | 0 | } |
546 | 0 | } else { |
547 | 0 | SCLogWarning("Unknown node in %s: %s", if_af, iface_node->name); |
548 | 0 | } |
549 | 0 | } |
550 | 0 | return 0; |
551 | 0 | } |
552 | | |
553 | | /** |
554 | | * \brief Check if CPU affinity configuration node follows format used in Suricata 7 and below |
555 | | * \retval true if CPU affinity uses Suricata <=7.0, false if it uses the new format (Suricata |
556 | | * >=8.0) |
557 | | */ |
558 | | static bool AffinityConfigIsLegacy(void) |
559 | 0 | { |
560 | 0 | static bool is_using_legacy_affinity_format = false; |
561 | 0 | if (thread_affinity_init_done == 0) { |
562 | | // reset the flag |
563 | 0 | is_using_legacy_affinity_format = false; |
564 | 0 | } else { |
565 | 0 | return is_using_legacy_affinity_format; |
566 | 0 | } |
567 | | |
568 | 0 | SCConfNode *root = SCConfGetNode(AffinityGetYamlPath(NULL)); |
569 | 0 | if (root == NULL) { |
570 | 0 | return is_using_legacy_affinity_format; |
571 | 0 | } |
572 | | |
573 | 0 | SCConfNode *affinity; |
574 | 0 | TAILQ_FOREACH (affinity, &root->head, next) { |
575 | | // If a child does not contain "-cpu-set", then the conf is legacy |
576 | | // Names in the legacy format (list of *-cpu-sets) contain |
577 | | // list item IDs - "0" : "management-cpu-set", "1" : "worker-cpu-set" |
578 | 0 | if (strstr(affinity->name, "-cpu-set") == NULL) { |
579 | 0 | is_using_legacy_affinity_format = true; |
580 | 0 | return is_using_legacy_affinity_format; |
581 | 0 | } |
582 | 0 | } |
583 | | |
584 | 0 | return is_using_legacy_affinity_format; |
585 | 0 | } |
586 | | #endif /* OS_WIN32 and __OpenBSD__ */ |
587 | | |
588 | | /** |
589 | | * \brief Extract CPU affinity configuration from current config file |
590 | | */ |
591 | | void AffinitySetupLoadFromConfig(void) |
592 | 0 | { |
593 | 0 | #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun |
594 | 0 | if (thread_affinity_init_done == 0) { |
595 | 0 | AffinitySetupInit(); |
596 | 0 | AffinityConfigIsLegacy(); |
597 | 0 | thread_affinity_init_done = 1; |
598 | 0 | } |
599 | |
|
600 | 0 | SCLogDebug("Loading %s from config", AffinityGetYamlPath(NULL)); |
601 | 0 | SCConfNode *root = SCConfGetNode(AffinityGetYamlPath(NULL)); |
602 | 0 | if (root == NULL) { |
603 | 0 | SCLogInfo("Cannot find %s node in config", AffinityGetYamlPath(NULL)); |
604 | 0 | return; |
605 | 0 | } |
606 | | |
607 | 0 | SCConfNode *affinity; |
608 | 0 | TAILQ_FOREACH(affinity, &root->head, next) { |
609 | 0 | char *v = AffinityConfigIsLegacy() ? affinity->val : affinity->name; |
610 | 0 | const char *setname = GetAffinitySetName(v); |
611 | 0 | if (setname == NULL) { |
612 | 0 | continue; |
613 | 0 | } |
614 | | |
615 | 0 | ThreadsAffinityType *taf = GetOrAllocAffinityTypeForIfaceOfName(setname, NULL); |
616 | 0 | if (taf == NULL) { |
617 | 0 | SCLogError("Failed to allocate CPU affinity type: %s", setname); |
618 | 0 | continue; |
619 | 0 | } |
620 | | |
621 | 0 | SCLogConfig("Found CPU affinity definition for \"%s\"", setname); |
622 | |
|
623 | 0 | SCConfNode *aff_query_node = AffinityConfigIsLegacy() ? affinity->head.tqh_first : affinity; |
624 | 0 | SetupCpuSets(taf, aff_query_node, setname); |
625 | 0 | if (SetupAffinityPriority(taf, aff_query_node, setname) < 0) { |
626 | 0 | SCLogError("Failed to setup priority for CPU affinity type: %s", setname); |
627 | 0 | continue; |
628 | 0 | } |
629 | 0 | if (SetupAffinityMode(taf, aff_query_node) < 0) { |
630 | 0 | SCLogError("Failed to setup mode for CPU affinity type: %s", setname); |
631 | 0 | continue; |
632 | 0 | } |
633 | 0 | if (SetupAffinityThreads(taf, aff_query_node) < 0) { |
634 | 0 | SCLogError("Failed to setup threads for CPU affinity type: %s", setname); |
635 | 0 | continue; |
636 | 0 | } |
637 | | |
638 | 0 | if (!AffinityConfigIsLegacy() && (IsWorkerCpuSet(setname) || IsReceiveCpuSet(setname))) { |
639 | 0 | if (SetupPerIfaceAffinity(taf, affinity) < 0) { |
640 | 0 | SCLogError("Failed to setup per-interface affinity for CPU affinity type: %s", |
641 | 0 | setname); |
642 | 0 | continue; |
643 | 0 | } |
644 | 0 | } |
645 | 0 | } |
646 | 0 | #endif /* OS_WIN32 and __OpenBSD__ */ |
647 | 0 | } |
648 | | |
649 | | #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun |
650 | | #ifdef HAVE_HWLOC |
651 | | static int HwLocDeviceNumaGet(hwloc_topology_t topo, hwloc_obj_t obj) |
652 | | { |
653 | | #if HWLOC_VERSION_MAJOR >= 2 && HWLOC_VERSION_MINOR >= 5 |
654 | | hwloc_obj_t nodes[MAX_NUMA_NODES]; |
655 | | unsigned num_nodes = MAX_NUMA_NODES; |
656 | | struct hwloc_location location; |
657 | | |
658 | | location.type = HWLOC_LOCATION_TYPE_OBJECT; |
659 | | location.location.object = obj; |
660 | | |
661 | | int result = hwloc_get_local_numanode_objs(topo, &location, &num_nodes, nodes, 0); |
662 | | if (result == 0 && num_nodes > 0 && num_nodes <= MAX_NUMA_NODES) { |
663 | | return nodes[0]->logical_index; |
664 | | } |
665 | | return -1; |
666 | | #else |
667 | | hwloc_obj_t non_io_ancestor = hwloc_get_non_io_ancestor_obj(topo, obj); |
668 | | if (non_io_ancestor == NULL) { |
669 | | return -1; |
670 | | } |
671 | | |
672 | | // Iterate over NUMA nodes and check their nodeset |
673 | | hwloc_obj_t numa_node = NULL; |
674 | | while ((numa_node = hwloc_get_next_obj_by_type(topo, HWLOC_OBJ_NUMANODE, numa_node)) != NULL) { |
675 | | if (hwloc_bitmap_isset(non_io_ancestor->nodeset, numa_node->os_index)) { |
676 | | return numa_node->logical_index; |
677 | | } |
678 | | } |
679 | | |
680 | | return -1; |
681 | | #endif /* ! HWLOC_VERSION_MAJOR >= 2 && HWLOC_VERSION_MINOR >= 5 */ |
682 | | } |
683 | | |
684 | | static hwloc_obj_t HwLocDeviceGetByKernelName(hwloc_topology_t topo, const char *interface_name) |
685 | | { |
686 | | hwloc_obj_t obj = NULL; |
687 | | |
688 | | while ((obj = hwloc_get_next_osdev(topo, obj)) != NULL) { |
689 | | if (obj->attr->osdev.type == HWLOC_OBJ_OSDEV_NETWORK && |
690 | | strcmp(obj->name, interface_name) == 0) { |
691 | | hwloc_obj_t parent = obj->parent; |
692 | | while (parent) { |
693 | | if (parent->type == HWLOC_OBJ_PCI_DEVICE) { |
694 | | return parent; |
695 | | } |
696 | | parent = parent->parent; |
697 | | } |
698 | | } |
699 | | } |
700 | | return NULL; |
701 | | } |
702 | | |
703 | | // Static function to deparse PCIe interface string name to individual components |
704 | | /** |
705 | | * \brief Parse PCIe address string to individual components |
706 | | * \param[in] pcie_address PCIe address string |
707 | | * \param[out] domain Domain component |
708 | | * \param[out] bus Bus component |
709 | | * \param[out] device Device component |
710 | | * \param[out] function Function component |
711 | | */ |
712 | | static int PcieAddressToComponents(const char *pcie_address, unsigned int *domain, |
713 | | unsigned int *bus, unsigned int *device, unsigned int *function) |
714 | | { |
715 | | // Handle both full and short PCIe address formats |
716 | | if (sscanf(pcie_address, "%x:%x:%x.%x", domain, bus, device, function) != 4) { |
717 | | if (sscanf(pcie_address, "%x:%x.%x", bus, device, function) != 3) { |
718 | | return -1; |
719 | | } |
720 | | *domain = 0; // Default domain to 0 if not provided |
721 | | } |
722 | | return 0; |
723 | | } |
724 | | |
725 | | // Function to convert PCIe address to hwloc object |
726 | | static hwloc_obj_t HwLocDeviceGetByPcie(hwloc_topology_t topo, const char *pcie_address) |
727 | | { |
728 | | hwloc_obj_t obj = NULL; |
729 | | unsigned int domain, bus, device, function; |
730 | | int r = PcieAddressToComponents(pcie_address, &domain, &bus, &device, &function); |
731 | | if (r == 0) { |
732 | | while ((obj = hwloc_get_next_pcidev(topo, obj)) != NULL) { |
733 | | if (obj->attr->pcidev.domain == domain && obj->attr->pcidev.bus == bus && |
734 | | obj->attr->pcidev.dev == device && obj->attr->pcidev.func == function) { |
735 | | return obj; |
736 | | } |
737 | | } |
738 | | } |
739 | | return NULL; |
740 | | } |
741 | | |
742 | | static void HwlocObjectDump(hwloc_obj_t obj, const char *iface_name) |
743 | | { |
744 | | if (!obj) { |
745 | | SCLogDebug("No object found for the given PCIe address.\n"); |
746 | | return; |
747 | | } |
748 | | |
749 | | static char pcie_address[32]; |
750 | | snprintf(pcie_address, sizeof(pcie_address), "%04x:%02x:%02x.%x", obj->attr->pcidev.domain, |
751 | | obj->attr->pcidev.bus, obj->attr->pcidev.dev, obj->attr->pcidev.func); |
752 | | SCLogDebug("Interface (%s / %s) has NUMA ID %d", iface_name, pcie_address, |
753 | | HwLocDeviceNumaGet(topology, obj)); |
754 | | |
755 | | SCLogDebug("Object type: %s\n", hwloc_obj_type_string(obj->type)); |
756 | | SCLogDebug("Logical index: %u\n", obj->logical_index); |
757 | | SCLogDebug("Depth: %u\n", obj->depth); |
758 | | SCLogDebug("Attributes:\n"); |
759 | | if (obj->type == HWLOC_OBJ_PCI_DEVICE) { |
760 | | SCLogDebug(" Domain: %04x\n", obj->attr->pcidev.domain); |
761 | | SCLogDebug(" Bus: %02x\n", obj->attr->pcidev.bus); |
762 | | SCLogDebug(" Device: %02x\n", obj->attr->pcidev.dev); |
763 | | SCLogDebug(" Function: %01x\n", obj->attr->pcidev.func); |
764 | | SCLogDebug(" Class ID: %04x\n", obj->attr->pcidev.class_id); |
765 | | SCLogDebug(" Vendor ID: %04x\n", obj->attr->pcidev.vendor_id); |
766 | | SCLogDebug(" Device ID: %04x\n", obj->attr->pcidev.device_id); |
767 | | SCLogDebug(" Subvendor ID: %04x\n", obj->attr->pcidev.subvendor_id); |
768 | | SCLogDebug(" Subdevice ID: %04x\n", obj->attr->pcidev.subdevice_id); |
769 | | SCLogDebug(" Revision: %02x\n", obj->attr->pcidev.revision); |
770 | | SCLogDebug(" Link speed: %f GB/s\n", obj->attr->pcidev.linkspeed); |
771 | | } else { |
772 | | SCLogDebug(" No PCI device attributes available.\n"); |
773 | | } |
774 | | } |
775 | | |
776 | | static bool TopologyShouldAutopin(ThreadVars *tv, ThreadsAffinityType *taf) |
777 | | { |
778 | | bool cond; |
779 | | SCMutexLock(&taf->taf_mutex); |
780 | | cond = tv->type == TVT_PPT && tv->iface_name && |
781 | | (strcmp(tv->iface_name, taf->name) == 0 || |
782 | | (strcmp("worker-cpu-set", taf->name) == 0 && RunmodeIsWorkers()) || |
783 | | (strcmp("receive-cpu-set", taf->name) == 0 && RunmodeIsAutofp())); |
784 | | SCMutexUnlock(&taf->taf_mutex); |
785 | | return cond; |
786 | | } |
787 | | |
788 | | /** |
789 | | * \brief Initialize the hardware topology. |
790 | | * \retval 0 on success, -1 on error |
791 | | */ |
792 | | static int TopologyInitialize(void) |
793 | | { |
794 | | if (topology == NULL) { |
795 | | if (hwloc_topology_init(&topology) == -1) { |
796 | | SCLogError("Failed to initialize topology"); |
797 | | return -1; |
798 | | } |
799 | | |
800 | | if (hwloc_topology_set_flags(topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) == -1 || |
801 | | hwloc_topology_set_io_types_filter(topology, HWLOC_TYPE_FILTER_KEEP_ALL) == -1 || |
802 | | hwloc_topology_load(topology) == -1) { |
803 | | SCLogError("Failed to set/load topology"); |
804 | | hwloc_topology_destroy(topology); |
805 | | topology = NULL; |
806 | | return -1; |
807 | | } |
808 | | } |
809 | | return 0; |
810 | | } |
811 | | |
812 | | void TopologyDestroy(void) |
813 | | { |
814 | | if (topology != NULL) { |
815 | | hwloc_topology_destroy(topology); |
816 | | topology = NULL; |
817 | | } |
818 | | } |
819 | | |
820 | | static int InterfaceGetNumaNode(ThreadVars *tv) |
821 | | { |
822 | | hwloc_obj_t if_obj = HwLocDeviceGetByKernelName(topology, tv->iface_name); |
823 | | if (if_obj == NULL) { |
824 | | if_obj = HwLocDeviceGetByPcie(topology, tv->iface_name); |
825 | | } |
826 | | |
827 | | if (if_obj != NULL && SCLogGetLogLevel() == SC_LOG_DEBUG) { |
828 | | HwlocObjectDump(if_obj, tv->iface_name); |
829 | | } |
830 | | |
831 | | int32_t numa_id = HwLocDeviceNumaGet(topology, if_obj); |
832 | | if (numa_id < 0 && SCRunmodeGet() == RUNMODE_DPDK) { |
833 | | // DPDK fallback for e.g. net_bonding (vdev) PMDs |
834 | | int32_t r = DPDKDeviceNameSetSocketID(tv->iface_name, &numa_id); |
835 | | if (r < 0) { |
836 | | numa_id = -1; |
837 | | } |
838 | | } |
839 | | |
840 | | if (numa_id < 0) { |
841 | | SCLogDebug("Unable to find NUMA node for interface %s", tv->iface_name); |
842 | | } |
843 | | |
844 | | return numa_id; |
845 | | } |
846 | | #endif /* HAVE_HWLOC */ |
847 | | |
848 | | static bool CPUIsFromNuma(uint16_t ncpu, uint16_t numa) |
849 | 0 | { |
850 | | #ifdef HAVE_HWLOC |
851 | | int core_id = ncpu; |
852 | | int depth = hwloc_get_type_depth(topology, HWLOC_OBJ_NUMANODE); |
853 | | hwloc_obj_t numa_node = NULL; |
854 | | bool found = false; |
855 | | uint16_t found_numa = 0; |
856 | | |
857 | | // Invalid depth or no NUMA nodes available |
858 | | if (depth == HWLOC_TYPE_DEPTH_UNKNOWN) { |
859 | | return false; |
860 | | } |
861 | | |
862 | | while ((numa_node = hwloc_get_next_obj_by_depth(topology, depth, numa_node)) != NULL) { |
863 | | hwloc_cpuset_t cpuset = hwloc_bitmap_alloc(); |
864 | | if (cpuset == NULL) { |
865 | | SCLogDebug("Failed to allocate cpuset"); |
866 | | continue; |
867 | | } |
868 | | hwloc_bitmap_copy(cpuset, numa_node->cpuset); |
869 | | |
870 | | if (hwloc_bitmap_isset(cpuset, core_id)) { |
871 | | SCLogDebug("Core %d - NUMA %d", core_id, numa_node->logical_index); |
872 | | found = true; |
873 | | found_numa = numa_node->logical_index; |
874 | | hwloc_bitmap_free(cpuset); |
875 | | break; |
876 | | } |
877 | | hwloc_bitmap_free(cpuset); |
878 | | } |
879 | | |
880 | | // After loop, check if we found the CPU and match the requested NUMA node |
881 | | if (found && numa == found_numa) { |
882 | | return true; |
883 | | } |
884 | | |
885 | | // CPU was not found in any NUMA node or did not match requested NUMA |
886 | | #endif /* HAVE_HWLOC */ |
887 | |
|
888 | 0 | return false; |
889 | 0 | } |
890 | | |
891 | | static int16_t FindCPUInNumaNode(int numa_node, ThreadsAffinityType *taf) |
892 | 0 | { |
893 | 0 | if (numa_node < 0) { |
894 | 0 | return -1; |
895 | 0 | } |
896 | | |
897 | 0 | if (taf->lcpu[numa_node] >= UtilCpuGetNumProcessorsOnline()) { |
898 | 0 | return -1; |
899 | 0 | } |
900 | | |
901 | 0 | uint16_t cpu = taf->lcpu[numa_node]; |
902 | 0 | while (cpu < UtilCpuGetNumProcessorsOnline() && |
903 | 0 | (!CPU_ISSET(cpu, &taf->cpu_set) || !CPUIsFromNuma(cpu, (uint16_t)numa_node))) { |
904 | 0 | cpu++; |
905 | 0 | } |
906 | |
|
907 | 0 | taf->lcpu[numa_node] = |
908 | 0 | (CPU_ISSET(cpu, &taf->cpu_set) && CPUIsFromNuma(cpu, (uint16_t)numa_node)) |
909 | 0 | ? cpu + 1 |
910 | 0 | : UtilCpuGetNumProcessorsOnline(); |
911 | 0 | return (CPU_ISSET(cpu, &taf->cpu_set) && CPUIsFromNuma(cpu, (uint16_t)numa_node)) ? (int16_t)cpu |
912 | 0 | : -1; |
913 | 0 | } |
914 | | |
915 | | static int16_t CPUSelectFromNuma(int iface_numa, ThreadsAffinityType *taf) |
916 | 0 | { |
917 | 0 | if (iface_numa != -1) { |
918 | 0 | return FindCPUInNumaNode(iface_numa, taf); |
919 | 0 | } |
920 | 0 | return -1; |
921 | 0 | } |
922 | | |
923 | | static int16_t CPUSelectAlternative(int iface_numa, ThreadsAffinityType *taf) |
924 | 0 | { |
925 | 0 | for (int nid = 0; nid < MAX_NUMA_NODES; nid++) { |
926 | 0 | if (iface_numa == nid) { |
927 | 0 | continue; |
928 | 0 | } |
929 | | |
930 | 0 | int16_t cpu = FindCPUInNumaNode(nid, taf); |
931 | 0 | if (cpu != -1) { |
932 | 0 | SCLogPerf("CPU %d from NUMA %d assigned to a network interface located on NUMA %d", cpu, |
933 | 0 | nid, iface_numa); |
934 | 0 | return cpu; |
935 | 0 | } |
936 | 0 | } |
937 | 0 | return -1; |
938 | 0 | } |
939 | | |
940 | | /** |
941 | | * \brief Select the next available CPU for the given affinity type. |
942 | | * taf->cpu_set is a bit array where each bit represents a CPU core. |
943 | | * The function iterates over the bit array and returns the first available CPU. |
944 | | * If last used CPU core index is higher than the indexes of available cores, |
945 | | * we reach the end of the array, and we reset the CPU selection. |
946 | | * On the second reset attempt, the function bails out with a default value. |
947 | | * The second attempt should only happen with an empty CPU set. |
948 | | */ |
949 | | static uint16_t CPUSelectDefault(ThreadsAffinityType *taf) |
950 | 0 | { |
951 | 0 | uint16_t cpu = taf->lcpu[0]; |
952 | 0 | int attempts = 0; |
953 | 0 | uint16_t num_procs = UtilCpuGetNumProcessorsOnline(); |
954 | 0 | if (num_procs > 0) { |
955 | 0 | while (!CPU_ISSET(cpu, &taf->cpu_set) && attempts < 2) { |
956 | 0 | cpu = (cpu + 1) % num_procs; |
957 | 0 | if (cpu == 0) { |
958 | 0 | attempts++; |
959 | 0 | } |
960 | 0 | } |
961 | 0 | } |
962 | |
|
963 | 0 | taf->lcpu[0] = cpu + 1; |
964 | 0 | return cpu; |
965 | 0 | } |
966 | | |
967 | | static uint16_t CPUSelectFromNumaOrDefault(int iface_numa, ThreadsAffinityType *taf) |
968 | 0 | { |
969 | 0 | uint16_t attempts = 0; |
970 | 0 | int16_t cpu = -1; |
971 | 0 | while (attempts < 2) { |
972 | 0 | cpu = CPUSelectFromNuma(iface_numa, taf); |
973 | 0 | if (cpu == -1) { |
974 | 0 | cpu = CPUSelectAlternative(iface_numa, taf); |
975 | 0 | if (cpu == -1) { |
976 | | // All CPUs from all NUMAs are used at this point |
977 | 0 | ResetCPUs(taf); |
978 | 0 | attempts++; |
979 | 0 | } |
980 | 0 | } |
981 | |
|
982 | 0 | if (cpu >= 0) { |
983 | 0 | return (uint16_t)cpu; |
984 | 0 | } |
985 | 0 | } |
986 | 0 | return CPUSelectDefault(taf); |
987 | 0 | } |
988 | | |
989 | | static uint16_t GetNextAvailableCPU(int iface_numa, ThreadsAffinityType *taf) |
990 | 0 | { |
991 | 0 | if (iface_numa < 0) { |
992 | 0 | return CPUSelectDefault(taf); |
993 | 0 | } |
994 | | |
995 | 0 | return CPUSelectFromNumaOrDefault(iface_numa, taf); |
996 | 0 | } |
997 | | |
998 | | static bool AutopinEnabled(void) |
999 | 0 | { |
1000 | 0 | int autopin = 0; |
1001 | 0 | if (SCConfGetBool("threading.autopin", &autopin) != 1) { |
1002 | 0 | return false; |
1003 | 0 | } |
1004 | 0 | return (bool)autopin; |
1005 | 0 | } |
1006 | | |
1007 | | #endif /* OS_WIN32 and __OpenBSD__ */ |
1008 | | |
1009 | | uint16_t AffinityGetNextCPU(ThreadVars *tv, ThreadsAffinityType *taf) |
1010 | 0 | { |
1011 | 0 | uint16_t ncpu = 0; |
1012 | 0 | #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun |
1013 | 0 | int iface_numa = -1; |
1014 | 0 | if (AutopinEnabled()) { |
1015 | | #ifdef HAVE_HWLOC |
1016 | | if (TopologyShouldAutopin(tv, taf)) { |
1017 | | if (TopologyInitialize() < 0) { |
1018 | | SCLogError("Failed to initialize topology for CPU affinity"); |
1019 | | return ncpu; |
1020 | | } |
1021 | | iface_numa = InterfaceGetNumaNode(tv); |
1022 | | } |
1023 | | #else |
1024 | 0 | static bool printed = false; |
1025 | 0 | if (!printed) { |
1026 | 0 | printed = true; |
1027 | 0 | SCLogWarning( |
1028 | 0 | "threading.autopin option is enabled but hwloc support is not compiled in. " |
1029 | 0 | "Make sure to pass --enable-hwloc to configure when building Suricata."); |
1030 | 0 | } |
1031 | 0 | #endif /* HAVE_HWLOC */ |
1032 | 0 | } |
1033 | |
|
1034 | 0 | SCMutexLock(&taf->taf_mutex); |
1035 | 0 | ncpu = GetNextAvailableCPU(iface_numa, taf); |
1036 | 0 | SCLogDebug("Setting affinity on CPU %d", ncpu); |
1037 | 0 | SCMutexUnlock(&taf->taf_mutex); |
1038 | 0 | #endif /* OS_WIN32 and __OpenBSD__ */ |
1039 | 0 | return ncpu; |
1040 | 0 | } |
1041 | | |
1042 | | /** |
1043 | | * \brief Return the total number of CPUs in a given affinity |
1044 | | * \retval the number of affined CPUs |
1045 | | */ |
1046 | | uint16_t UtilAffinityGetAffinedCPUNum(ThreadsAffinityType *taf) |
1047 | 0 | { |
1048 | 0 | uint16_t ncpu = 0; |
1049 | 0 | #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun |
1050 | 0 | SCMutexLock(&taf->taf_mutex); |
1051 | 0 | for (int i = UtilCpuGetNumProcessorsOnline(); i >= 0; i--) |
1052 | 0 | if (CPU_ISSET(i, &taf->cpu_set)) { |
1053 | 0 | ncpu++; |
1054 | 0 | } |
1055 | 0 | SCMutexUnlock(&taf->taf_mutex); |
1056 | 0 | #endif |
1057 | 0 | return ncpu; |
1058 | 0 | } |
1059 | | |
1060 | | #ifdef HAVE_DPDK |
1061 | | /** |
1062 | | * Find if CPU sets overlap |
1063 | | * \return 1 if CPUs overlap, 0 otherwise |
1064 | | */ |
1065 | | uint16_t UtilAffinityCpusOverlap(ThreadsAffinityType *taf1, ThreadsAffinityType *taf2) |
1066 | | { |
1067 | | ThreadsAffinityType tmptaf; |
1068 | | CPU_ZERO(&tmptaf); |
1069 | | SCMutexInit(&tmptaf.taf_mutex, NULL); |
1070 | | |
1071 | | cpu_set_t tmpcset; |
1072 | | |
1073 | | SCMutexLock(&taf1->taf_mutex); |
1074 | | SCMutexLock(&taf2->taf_mutex); |
1075 | | CPU_AND(&tmpcset, &taf1->cpu_set, &taf2->cpu_set); |
1076 | | SCMutexUnlock(&taf2->taf_mutex); |
1077 | | SCMutexUnlock(&taf1->taf_mutex); |
1078 | | |
1079 | | for (int i = UtilCpuGetNumProcessorsOnline(); i >= 0; i--) |
1080 | | if (CPU_ISSET(i, &tmpcset)) { |
1081 | | return 1; |
1082 | | } |
1083 | | return 0; |
1084 | | } |
1085 | | |
1086 | | /** |
1087 | | * Function makes sure that CPUs of different types don't overlap by excluding |
1088 | | * one affinity type from the other |
1089 | | * \param mod_taf - CPU set to be modified |
1090 | | * \param static_taf - static CPU set to be used only for evaluation |
1091 | | */ |
1092 | | void UtilAffinityCpusExclude(ThreadsAffinityType *mod_taf, ThreadsAffinityType *static_taf) |
1093 | | { |
1094 | | SCMutexLock(&mod_taf->taf_mutex); |
1095 | | SCMutexLock(&static_taf->taf_mutex); |
1096 | | int max_cpus = UtilCpuGetNumProcessorsOnline(); |
1097 | | for (int cpu = 0; cpu < max_cpus; cpu++) { |
1098 | | if (CPU_ISSET(cpu, &mod_taf->cpu_set) && CPU_ISSET(cpu, &static_taf->cpu_set)) { |
1099 | | CPU_CLR(cpu, &mod_taf->cpu_set); |
1100 | | } |
1101 | | } |
1102 | | SCMutexUnlock(&static_taf->taf_mutex); |
1103 | | SCMutexUnlock(&mod_taf->taf_mutex); |
1104 | | } |
1105 | | #endif /* HAVE_DPDK */ |
1106 | | |
1107 | | #ifdef UNITTESTS |
1108 | | // avoiding Darwin/MacOS as it does not support bitwise CPU affinity |
1109 | | #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) |
1110 | | |
1111 | | /** |
1112 | | * \brief Helper function to reset affinity state for unit tests |
1113 | | * This properly clears CPU sets without destroying initialized mutexes |
1114 | | */ |
1115 | | static void ResetAffinityForTest(void) |
1116 | | { |
1117 | | thread_affinity_init_done = 0; |
1118 | | for (int i = 0; i < MAX_CPU_SET; i++) { |
1119 | | ThreadsAffinityType *taf = &thread_affinity[i]; |
1120 | | CPU_ZERO(&taf->cpu_set); |
1121 | | CPU_ZERO(&taf->lowprio_cpu); |
1122 | | CPU_ZERO(&taf->medprio_cpu); |
1123 | | CPU_ZERO(&taf->hiprio_cpu); |
1124 | | taf->nb_threads = 0; |
1125 | | taf->prio = PRIO_LOW; |
1126 | | taf->mode_flag = BALANCED_AFFINITY; |
1127 | | |
1128 | | for (int j = 0; j < MAX_NUMA_NODES; j++) { |
1129 | | taf->lcpu[j] = 0; |
1130 | | } |
1131 | | |
1132 | | if (taf->children) { |
1133 | | for (uint32_t j = 0; j < taf->nb_children; j++) { |
1134 | | if (taf->children[j]) { |
1135 | | SCFree((void *)taf->children[j]->name); |
1136 | | SCFree(taf->children[j]); |
1137 | | } |
1138 | | } |
1139 | | SCFree(taf->children); |
1140 | | taf->children = NULL; |
1141 | | } |
1142 | | taf->nb_children = 0; |
1143 | | taf->nb_children_capacity = 0; |
1144 | | |
1145 | | if (i == MANAGEMENT_CPU_SET) { |
1146 | | taf->name = "management-cpu-set"; |
1147 | | } else if (i == WORKER_CPU_SET) { |
1148 | | taf->name = "worker-cpu-set"; |
1149 | | } else if (i == VERDICT_CPU_SET) { |
1150 | | taf->name = "verdict-cpu-set"; |
1151 | | } else if (i == RECEIVE_CPU_SET) { |
1152 | | taf->name = "receive-cpu-set"; |
1153 | | } else { |
1154 | | taf->name = NULL; |
1155 | | } |
1156 | | |
1157 | | // Don't touch the mutex - it should remain initialized |
1158 | | } |
1159 | | } |
1160 | | |
1161 | | /** |
1162 | | * \brief Test basic CPU affinity parsing in new format |
1163 | | */ |
1164 | | static int ThreadingAffinityTest01(void) |
1165 | | { |
1166 | | SCConfCreateContextBackup(); |
1167 | | SCConfInit(); |
1168 | | ResetAffinityForTest(); |
1169 | | const char *config = "%YAML 1.1\n" |
1170 | | "---\n" |
1171 | | "threading:\n" |
1172 | | " cpu-affinity:\n" |
1173 | | " management-cpu-set:\n" |
1174 | | " cpu: [ 0 ]\n" |
1175 | | " worker-cpu-set:\n" |
1176 | | " cpu: [ 1, 2, 3 ]\n"; |
1177 | | |
1178 | | SCConfYamlLoadString(config, strlen(config)); |
1179 | | |
1180 | | AffinitySetupLoadFromConfig(); |
1181 | | FAIL_IF_NOT(AffinityConfigIsLegacy() == false); |
1182 | | |
1183 | | ThreadsAffinityType *mgmt_taf = &thread_affinity[MANAGEMENT_CPU_SET]; |
1184 | | FAIL_IF_NOT(CPU_ISSET(0, &mgmt_taf->cpu_set)); |
1185 | | FAIL_IF_NOT(CPU_COUNT(&mgmt_taf->cpu_set) == 1); |
1186 | | |
1187 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1188 | | FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->cpu_set)); |
1189 | | FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->cpu_set)); |
1190 | | FAIL_IF_NOT(CPU_ISSET(3, &worker_taf->cpu_set)); |
1191 | | FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set)); |
1192 | | |
1193 | | SCConfRestoreContextBackup(); |
1194 | | PASS; |
1195 | | } |
1196 | | |
1197 | | /** |
1198 | | * \brief Test deprecated CPU affinity format parsing |
1199 | | */ |
1200 | | static int ThreadingAffinityTest02(void) |
1201 | | { |
1202 | | SCConfCreateContextBackup(); |
1203 | | SCConfInit(); |
1204 | | ResetAffinityForTest(); |
1205 | | |
1206 | | const char *config = "%YAML 1.1\n" |
1207 | | "---\n" |
1208 | | "threading:\n" |
1209 | | " cpu-affinity:\n" |
1210 | | " - worker-cpu-set:\n" |
1211 | | " cpu: [ 1, 2 ]\n"; |
1212 | | |
1213 | | SCConfYamlLoadString(config, strlen(config)); |
1214 | | AffinitySetupLoadFromConfig(); |
1215 | | FAIL_IF_NOT(AffinityConfigIsLegacy() == true); |
1216 | | |
1217 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1218 | | FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->cpu_set)); |
1219 | | FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->cpu_set)); |
1220 | | FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == 2); |
1221 | | |
1222 | | SCConfRestoreContextBackup(); |
1223 | | PASS; |
1224 | | } |
1225 | | |
1226 | | /** |
1227 | | * \brief Test CPU range parsing ("0-3") |
1228 | | */ |
1229 | | static int ThreadingAffinityTest03(void) |
1230 | | { |
1231 | | SCConfCreateContextBackup(); |
1232 | | SCConfInit(); |
1233 | | ResetAffinityForTest(); |
1234 | | |
1235 | | const char *config = "%YAML 1.1\n" |
1236 | | "---\n" |
1237 | | "threading:\n" |
1238 | | " cpu-affinity:\n" |
1239 | | " worker-cpu-set:\n" |
1240 | | " cpu: [ \"0-3\" ]\n"; |
1241 | | |
1242 | | SCConfYamlLoadString(config, strlen(config)); |
1243 | | AffinitySetupLoadFromConfig(); |
1244 | | |
1245 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1246 | | FAIL_IF_NOT(CPU_ISSET(0, &worker_taf->cpu_set)); |
1247 | | FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->cpu_set)); |
1248 | | FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->cpu_set)); |
1249 | | FAIL_IF_NOT(CPU_ISSET(3, &worker_taf->cpu_set)); |
1250 | | FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == 4); |
1251 | | |
1252 | | SCConfRestoreContextBackup(); |
1253 | | PASS; |
1254 | | } |
1255 | | |
1256 | | /** |
1257 | | * \brief Test mixed CPU specification parsing (individual CPUs in list) |
1258 | | */ |
1259 | | static int ThreadingAffinityTest04(void) |
1260 | | { |
1261 | | SCConfCreateContextBackup(); |
1262 | | SCConfInit(); |
1263 | | ResetAffinityForTest(); |
1264 | | |
1265 | | const char *config = "%YAML 1.1\n" |
1266 | | "---\n" |
1267 | | "threading:\n" |
1268 | | " cpu-affinity:\n" |
1269 | | " worker-cpu-set:\n" |
1270 | | " cpu: [ 1, 3, 5 ]\n"; |
1271 | | |
1272 | | SCConfYamlLoadString(config, strlen(config)); |
1273 | | AffinitySetupLoadFromConfig(); |
1274 | | |
1275 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1276 | | FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->cpu_set)); |
1277 | | FAIL_IF_NOT(CPU_ISSET(3, &worker_taf->cpu_set)); |
1278 | | FAIL_IF_NOT(CPU_ISSET(5, &worker_taf->cpu_set)); |
1279 | | FAIL_IF(CPU_ISSET(0, &worker_taf->cpu_set)); |
1280 | | FAIL_IF(CPU_ISSET(2, &worker_taf->cpu_set)); |
1281 | | FAIL_IF(CPU_ISSET(4, &worker_taf->cpu_set)); |
1282 | | FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == 3); |
1283 | | |
1284 | | SCConfRestoreContextBackup(); |
1285 | | PASS; |
1286 | | } |
1287 | | |
1288 | | /** |
1289 | | * \brief Test "all" CPU specification |
1290 | | */ |
1291 | | static int ThreadingAffinityTest05(void) |
1292 | | { |
1293 | | SCConfCreateContextBackup(); |
1294 | | SCConfInit(); |
1295 | | ResetAffinityForTest(); |
1296 | | |
1297 | | const char *config = "%YAML 1.1\n" |
1298 | | "---\n" |
1299 | | "threading:\n" |
1300 | | " cpu-affinity:\n" |
1301 | | " worker-cpu-set:\n" |
1302 | | " cpu: [ \"all\" ]\n"; |
1303 | | |
1304 | | SCConfYamlLoadString(config, strlen(config)); |
1305 | | AffinitySetupLoadFromConfig(); |
1306 | | |
1307 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1308 | | FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == UtilCpuGetNumProcessorsOnline()); |
1309 | | |
1310 | | SCConfRestoreContextBackup(); |
1311 | | PASS; |
1312 | | } |
1313 | | |
1314 | | /** |
1315 | | * \brief Test priority settings parsing |
1316 | | */ |
1317 | | static int ThreadingAffinityTest06(void) |
1318 | | { |
1319 | | SCConfCreateContextBackup(); |
1320 | | SCConfInit(); |
1321 | | ResetAffinityForTest(); |
1322 | | |
1323 | | const char *config = "%YAML 1.1\n" |
1324 | | "---\n" |
1325 | | "threading:\n" |
1326 | | " cpu-affinity:\n" |
1327 | | " worker-cpu-set:\n" |
1328 | | " cpu: [ 0, 1, 2, 3 ]\n" |
1329 | | " prio:\n" |
1330 | | " low: [ 0 ]\n" |
1331 | | " medium: [ \"1-2\" ]\n" |
1332 | | " high: [ 3 ]\n" |
1333 | | " default: \"medium\"\n"; |
1334 | | |
1335 | | SCConfYamlLoadString(config, strlen(config)); |
1336 | | AffinitySetupLoadFromConfig(); |
1337 | | |
1338 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1339 | | FAIL_IF_NOT(CPU_ISSET(0, &worker_taf->lowprio_cpu)); |
1340 | | FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->medprio_cpu)); |
1341 | | FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->medprio_cpu)); |
1342 | | FAIL_IF_NOT(CPU_ISSET(3, &worker_taf->hiprio_cpu)); |
1343 | | FAIL_IF_NOT(worker_taf->prio == PRIO_MEDIUM); |
1344 | | |
1345 | | SCConfRestoreContextBackup(); |
1346 | | PASS; |
1347 | | } |
1348 | | |
1349 | | /** |
1350 | | * \brief Test mode settings (exclusive/balanced) |
1351 | | */ |
1352 | | static int ThreadingAffinityTest07(void) |
1353 | | { |
1354 | | SCConfCreateContextBackup(); |
1355 | | SCConfInit(); |
1356 | | ResetAffinityForTest(); |
1357 | | |
1358 | | const char *config = "%YAML 1.1\n" |
1359 | | "---\n" |
1360 | | "threading:\n" |
1361 | | " cpu-affinity:\n" |
1362 | | " worker-cpu-set:\n" |
1363 | | " cpu: [ 0, 1 ]\n" |
1364 | | " mode: \"exclusive\"\n"; |
1365 | | |
1366 | | SCConfYamlLoadString(config, strlen(config)); |
1367 | | AffinitySetupLoadFromConfig(); |
1368 | | |
1369 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1370 | | FAIL_IF_NOT(worker_taf->mode_flag == EXCLUSIVE_AFFINITY); |
1371 | | |
1372 | | SCConfRestoreContextBackup(); |
1373 | | PASS; |
1374 | | } |
1375 | | |
1376 | | /** |
1377 | | * \brief Test threads count parsing |
1378 | | */ |
1379 | | static int ThreadingAffinityTest08(void) |
1380 | | { |
1381 | | SCConfCreateContextBackup(); |
1382 | | SCConfInit(); |
1383 | | ResetAffinityForTest(); |
1384 | | |
1385 | | const char *config = "%YAML 1.1\n" |
1386 | | "---\n" |
1387 | | "threading:\n" |
1388 | | " cpu-affinity:\n" |
1389 | | " worker-cpu-set:\n" |
1390 | | " cpu: [ 0, 1, 2 ]\n" |
1391 | | " threads: 4\n"; |
1392 | | |
1393 | | SCConfYamlLoadString(config, strlen(config)); |
1394 | | AffinitySetupLoadFromConfig(); |
1395 | | |
1396 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1397 | | FAIL_IF_NOT(worker_taf->nb_threads == 4); |
1398 | | |
1399 | | SCConfRestoreContextBackup(); |
1400 | | PASS; |
1401 | | } |
1402 | | |
1403 | | /** |
1404 | | * \brief Test interface-specific CPU set parsing |
1405 | | */ |
1406 | | static int ThreadingAffinityTest09(void) |
1407 | | { |
1408 | | SCConfCreateContextBackup(); |
1409 | | SCConfInit(); |
1410 | | ResetAffinityForTest(); |
1411 | | |
1412 | | const char *config = "%YAML 1.1\n" |
1413 | | "---\n" |
1414 | | "threading:\n" |
1415 | | " cpu-affinity:\n" |
1416 | | " worker-cpu-set:\n" |
1417 | | " cpu: [ 0, 1 ]\n" |
1418 | | " interface-specific-cpu-set:\n" |
1419 | | " - interface: \"eth0\"\n" |
1420 | | " cpu: [ 2, 3 ]\n" |
1421 | | " mode: \"exclusive\"\n"; |
1422 | | |
1423 | | SCConfYamlLoadString(config, strlen(config)); |
1424 | | AffinitySetupLoadFromConfig(); |
1425 | | |
1426 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1427 | | FAIL_IF_NOT(worker_taf->nb_children == 1); |
1428 | | |
1429 | | ThreadsAffinityType *iface_taf = worker_taf->children[0]; |
1430 | | FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0); |
1431 | | FAIL_IF_NOT(CPU_ISSET(2, &iface_taf->cpu_set)); |
1432 | | FAIL_IF_NOT(CPU_ISSET(3, &iface_taf->cpu_set)); |
1433 | | FAIL_IF_NOT(CPU_COUNT(&iface_taf->cpu_set) == 2); |
1434 | | FAIL_IF_NOT(iface_taf->mode_flag == EXCLUSIVE_AFFINITY); |
1435 | | |
1436 | | SCConfRestoreContextBackup(); |
1437 | | PASS; |
1438 | | } |
1439 | | |
1440 | | /** |
1441 | | * \brief Test multiple interface-specific CPU sets |
1442 | | */ |
1443 | | static int ThreadingAffinityTest10(void) |
1444 | | { |
1445 | | SCConfCreateContextBackup(); |
1446 | | SCConfInit(); |
1447 | | ResetAffinityForTest(); |
1448 | | |
1449 | | const char *config = "%YAML 1.1\n" |
1450 | | "---\n" |
1451 | | "threading:\n" |
1452 | | " cpu-affinity:\n" |
1453 | | " receive-cpu-set:\n" |
1454 | | " cpu: [ 0 ]\n" |
1455 | | " interface-specific-cpu-set:\n" |
1456 | | " - interface: \"eth0\"\n" |
1457 | | " cpu: [ 1, 2 ]\n" |
1458 | | " - interface: \"eth1\"\n" |
1459 | | " cpu: [ 3, 4 ]\n"; |
1460 | | |
1461 | | SCConfYamlLoadString(config, strlen(config)); |
1462 | | AffinitySetupLoadFromConfig(); |
1463 | | |
1464 | | ThreadsAffinityType *receive_taf = &thread_affinity[RECEIVE_CPU_SET]; |
1465 | | FAIL_IF_NOT(receive_taf->nb_children == 2); |
1466 | | |
1467 | | bool eth0_found = false, eth1_found = false; |
1468 | | |
1469 | | for (uint32_t i = 0; i < receive_taf->nb_children; i++) { |
1470 | | ThreadsAffinityType *iface_taf = receive_taf->children[i]; |
1471 | | if (strcmp(iface_taf->name, "eth0") == 0) { |
1472 | | if (CPU_ISSET(1, &iface_taf->cpu_set) && CPU_ISSET(2, &iface_taf->cpu_set) && |
1473 | | CPU_COUNT(&iface_taf->cpu_set) == 2) { |
1474 | | eth0_found = true; |
1475 | | } |
1476 | | } else if (strcmp(iface_taf->name, "eth1") == 0) { |
1477 | | if (CPU_ISSET(3, &iface_taf->cpu_set) && CPU_ISSET(4, &iface_taf->cpu_set) && |
1478 | | CPU_COUNT(&iface_taf->cpu_set) == 2) { |
1479 | | eth1_found = true; |
1480 | | } |
1481 | | } |
1482 | | } |
1483 | | |
1484 | | FAIL_IF_NOT(eth0_found && eth1_found); |
1485 | | |
1486 | | SCConfRestoreContextBackup(); |
1487 | | PASS; |
1488 | | } |
1489 | | |
1490 | | /** |
1491 | | * \brief Test interface-specific priority settings |
1492 | | */ |
1493 | | static int ThreadingAffinityTest11(void) |
1494 | | { |
1495 | | SCConfCreateContextBackup(); |
1496 | | SCConfInit(); |
1497 | | ResetAffinityForTest(); |
1498 | | |
1499 | | const char *config = "%YAML 1.1\n" |
1500 | | "---\n" |
1501 | | "threading:\n" |
1502 | | " cpu-affinity:\n" |
1503 | | " worker-cpu-set:\n" |
1504 | | " cpu: [ 0 ]\n" |
1505 | | " interface-specific-cpu-set:\n" |
1506 | | " - interface: \"eth0\"\n" |
1507 | | " cpu: [ 1, 2, 3 ]\n" |
1508 | | " prio:\n" |
1509 | | " high: [ \"all\" ]\n" |
1510 | | " default: \"high\"\n"; |
1511 | | |
1512 | | SCConfYamlLoadString(config, strlen(config)); |
1513 | | AffinitySetupLoadFromConfig(); |
1514 | | |
1515 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1516 | | FAIL_IF_NOT(worker_taf->nb_children == 1); |
1517 | | |
1518 | | ThreadsAffinityType *iface_taf = worker_taf->children[0]; |
1519 | | FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0); |
1520 | | FAIL_IF_NOT(CPU_ISSET(1, &iface_taf->hiprio_cpu)); |
1521 | | FAIL_IF_NOT(CPU_ISSET(2, &iface_taf->hiprio_cpu)); |
1522 | | FAIL_IF_NOT(CPU_ISSET(3, &iface_taf->hiprio_cpu)); |
1523 | | FAIL_IF_NOT(iface_taf->prio == PRIO_HIGH); |
1524 | | |
1525 | | SCConfRestoreContextBackup(); |
1526 | | PASS; |
1527 | | } |
1528 | | |
1529 | | /** |
1530 | | * \brief Test complete configuration with all CPU sets |
1531 | | */ |
1532 | | static int ThreadingAffinityTest12(void) |
1533 | | { |
1534 | | SCConfCreateContextBackup(); |
1535 | | SCConfInit(); |
1536 | | ResetAffinityForTest(); |
1537 | | |
1538 | | const char *config = "%YAML 1.1\n" |
1539 | | "---\n" |
1540 | | "threading:\n" |
1541 | | " cpu-affinity:\n" |
1542 | | " management-cpu-set:\n" |
1543 | | " cpu: [ 0 ]\n" |
1544 | | " receive-cpu-set:\n" |
1545 | | " cpu: [ 1 ]\n" |
1546 | | " worker-cpu-set:\n" |
1547 | | " cpu: [ 2, 3 ]\n" |
1548 | | " interface-specific-cpu-set:\n" |
1549 | | " - interface: \"eth0\"\n" |
1550 | | " cpu: [ \"5-7\" ]\n" |
1551 | | " prio:\n" |
1552 | | " high: [ \"all\" ]\n" |
1553 | | " default: \"high\"\n" |
1554 | | " verdict-cpu-set:\n" |
1555 | | " cpu: [ 4 ]\n"; |
1556 | | |
1557 | | SCConfYamlLoadString(config, strlen(config)); |
1558 | | AffinitySetupLoadFromConfig(); |
1559 | | |
1560 | | FAIL_IF_NOT(CPU_ISSET(0, &thread_affinity[MANAGEMENT_CPU_SET].cpu_set)); |
1561 | | FAIL_IF_NOT(CPU_COUNT(&thread_affinity[MANAGEMENT_CPU_SET].cpu_set) == 1); |
1562 | | FAIL_IF_NOT(CPU_ISSET(1, &thread_affinity[RECEIVE_CPU_SET].cpu_set)); |
1563 | | FAIL_IF_NOT(CPU_COUNT(&thread_affinity[RECEIVE_CPU_SET].cpu_set) == 1); |
1564 | | FAIL_IF_NOT(CPU_ISSET(4, &thread_affinity[VERDICT_CPU_SET].cpu_set)); |
1565 | | FAIL_IF_NOT(CPU_COUNT(&thread_affinity[VERDICT_CPU_SET].cpu_set) == 1); |
1566 | | FAIL_IF_NOT(CPU_ISSET(2, &thread_affinity[WORKER_CPU_SET].cpu_set)); |
1567 | | FAIL_IF_NOT(CPU_ISSET(3, &thread_affinity[WORKER_CPU_SET].cpu_set)); |
1568 | | |
1569 | | FAIL_IF_NOT(thread_affinity[WORKER_CPU_SET].nb_children == 1); |
1570 | | ThreadsAffinityType *iface_taf = thread_affinity[WORKER_CPU_SET].children[0]; |
1571 | | FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0); |
1572 | | FAIL_IF_NOT(CPU_ISSET(1, &iface_taf->hiprio_cpu)); |
1573 | | FAIL_IF_NOT(CPU_ISSET(2, &iface_taf->hiprio_cpu)); |
1574 | | FAIL_IF_NOT(CPU_ISSET(3, &iface_taf->hiprio_cpu)); |
1575 | | FAIL_IF_NOT(iface_taf->prio == PRIO_HIGH); |
1576 | | FAIL_IF_NOT(CPU_COUNT(&thread_affinity[WORKER_CPU_SET].cpu_set) == 2); |
1577 | | |
1578 | | SCConfRestoreContextBackup(); |
1579 | | PASS; |
1580 | | } |
1581 | | |
1582 | | /** |
1583 | | * \brief Test error handling for malformed CPU specification |
1584 | | */ |
1585 | | static int ThreadingAffinityTest13(void) |
1586 | | { |
1587 | | SCConfCreateContextBackup(); |
1588 | | SCConfInit(); |
1589 | | ResetAffinityForTest(); |
1590 | | |
1591 | | const char *config = "%YAML 1.1\n" |
1592 | | "---\n" |
1593 | | "threading:\n" |
1594 | | " cpu-affinity:\n" |
1595 | | " worker-cpu-set:\n" |
1596 | | " cpu: [ \"invalid-cpu\" ]\n"; |
1597 | | |
1598 | | SCConfYamlLoadString(config, strlen(config)); |
1599 | | AffinitySetupLoadFromConfig(); |
1600 | | |
1601 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1602 | | FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == 0); |
1603 | | |
1604 | | SCConfRestoreContextBackup(); |
1605 | | PASS; |
1606 | | } |
1607 | | |
1608 | | /** |
1609 | | * \brief Test empty configuration handling |
1610 | | */ |
1611 | | static int ThreadingAffinityTest14(void) |
1612 | | { |
1613 | | SCConfCreateContextBackup(); |
1614 | | SCConfInit(); |
1615 | | ResetAffinityForTest(); |
1616 | | |
1617 | | const char *config = "%YAML 1.1\n" |
1618 | | "---\n" |
1619 | | "threading:\n" |
1620 | | " cpu-affinity:\n"; |
1621 | | |
1622 | | SCConfYamlLoadString(config, strlen(config)); |
1623 | | AffinitySetupLoadFromConfig(); |
1624 | | |
1625 | | FAIL_IF_NOT( |
1626 | | CPU_COUNT(&thread_affinity[WORKER_CPU_SET].cpu_set) == UtilCpuGetNumProcessorsOnline()); |
1627 | | |
1628 | | SCConfRestoreContextBackup(); |
1629 | | PASS; |
1630 | | } |
1631 | | |
1632 | | /** |
1633 | | * \brief Test CPU range parsing with invalid order (high-low) |
1634 | | * CPU ranges specified in reverse order should be handled |
1635 | | */ |
1636 | | static int ThreadingAffinityTest15(void) |
1637 | | { |
1638 | | SCConfCreateContextBackup(); |
1639 | | SCConfInit(); |
1640 | | ResetAffinityForTest(); |
1641 | | |
1642 | | const char *config = "%YAML 1.1\n" |
1643 | | "---\n" |
1644 | | "threading:\n" |
1645 | | " cpu-affinity:\n" |
1646 | | " - management-cpu-set:\n" |
1647 | | " cpu: [ \"3-1\" ]\n"; // Invalid reverse range |
1648 | | |
1649 | | SCConfYamlLoadString(config, strlen(config)); |
1650 | | AffinitySetupLoadFromConfig(); |
1651 | | |
1652 | | FAIL_IF_NOT(CPU_COUNT(&thread_affinity[MANAGEMENT_CPU_SET].cpu_set) == 0); |
1653 | | |
1654 | | SCConfRestoreContextBackup(); |
1655 | | PASS; |
1656 | | } |
1657 | | |
1658 | | /** |
1659 | | * \brief Test invalid priority values in SetupDefaultPriority |
1660 | | * Invalid priority strings should return errors but pass |
1661 | | */ |
1662 | | static int ThreadingAffinityTest16(void) |
1663 | | { |
1664 | | SCConfCreateContextBackup(); |
1665 | | SCConfInit(); |
1666 | | ResetAffinityForTest(); |
1667 | | |
1668 | | const char *config = "%YAML 1.1\n" |
1669 | | "---\n" |
1670 | | "threading:\n" |
1671 | | " cpu-affinity:\n" |
1672 | | " - management-cpu-set:\n" |
1673 | | " prio:\n" |
1674 | | " default: invalid_priority\n"; |
1675 | | |
1676 | | SCConfYamlLoadString(config, strlen(config)); |
1677 | | AffinitySetupLoadFromConfig(); |
1678 | | |
1679 | | SCConfRestoreContextBackup(); |
1680 | | PASS; |
1681 | | } |
1682 | | |
1683 | | /** |
1684 | | * \brief Test invalid CPU affinity mode values |
1685 | | * Invalid mode strings should return errors but pass |
1686 | | */ |
1687 | | static int ThreadingAffinityTest17(void) |
1688 | | { |
1689 | | SCConfCreateContextBackup(); |
1690 | | SCConfInit(); |
1691 | | ResetAffinityForTest(); |
1692 | | |
1693 | | const char *config = "%YAML 1.1\n" |
1694 | | "---\n" |
1695 | | "threading:\n" |
1696 | | " cpu-affinity:\n" |
1697 | | " - management-cpu-set:\n" |
1698 | | " mode: invalid_mode\n"; |
1699 | | |
1700 | | SCConfYamlLoadString(config, strlen(config)); |
1701 | | AffinitySetupLoadFromConfig(); |
1702 | | |
1703 | | SCConfRestoreContextBackup(); |
1704 | | PASS; |
1705 | | } |
1706 | | |
1707 | | /** |
1708 | | * \brief Test invalid thread count values |
1709 | | * Invalid thread counts |
1710 | | */ |
1711 | | static int ThreadingAffinityTest18(void) |
1712 | | { |
1713 | | SCConfCreateContextBackup(); |
1714 | | SCConfInit(); |
1715 | | ResetAffinityForTest(); |
1716 | | |
1717 | | const char *config = "%YAML 1.1\n" |
1718 | | "---\n" |
1719 | | "threading:\n" |
1720 | | " cpu-affinity:\n" |
1721 | | " - management-cpu-set:\n" |
1722 | | " threads: 0\n"; |
1723 | | |
1724 | | SCConfYamlLoadString(config, strlen(config)); |
1725 | | AffinitySetupLoadFromConfig(); |
1726 | | |
1727 | | SCConfRestoreContextBackup(); |
1728 | | PASS; |
1729 | | } |
1730 | | |
1731 | | /** |
1732 | | * \brief Test CPU specification with negative numbers |
1733 | | * Negative CPU numbers should be rejected |
1734 | | */ |
1735 | | static int ThreadingAffinityTest19(void) |
1736 | | { |
1737 | | SCConfCreateContextBackup(); |
1738 | | SCConfInit(); |
1739 | | ResetAffinityForTest(); |
1740 | | |
1741 | | const char *config = "%YAML 1.1\n" |
1742 | | "---\n" |
1743 | | "threading:\n" |
1744 | | " cpu-affinity:\n" |
1745 | | " - management-cpu-set:\n" |
1746 | | " cpu: [ -1 ]\n"; |
1747 | | |
1748 | | SCConfYamlLoadString(config, strlen(config)); |
1749 | | AffinitySetupLoadFromConfig(); |
1750 | | |
1751 | | SCConfRestoreContextBackup(); |
1752 | | PASS; |
1753 | | } |
1754 | | |
1755 | | /** |
1756 | | * \brief Test invalid thread count with non-numeric values |
1757 | | * Non-numeric thread counts should be handled |
1758 | | */ |
1759 | | static int ThreadingAffinityTest20(void) |
1760 | | { |
1761 | | SCConfCreateContextBackup(); |
1762 | | SCConfInit(); |
1763 | | ResetAffinityForTest(); |
1764 | | |
1765 | | const char *config = "%YAML 1.1\n" |
1766 | | "---\n" |
1767 | | "threading:\n" |
1768 | | " cpu-affinity:\n" |
1769 | | " - management-cpu-set:\n" |
1770 | | " threads: invalid_number\n"; |
1771 | | |
1772 | | SCConfYamlLoadString(config, strlen(config)); |
1773 | | AffinitySetupLoadFromConfig(); |
1774 | | |
1775 | | SCConfRestoreContextBackup(); |
1776 | | PASS; |
1777 | | } |
1778 | | |
1779 | | /** |
1780 | | * \brief Test extremely large CPU ranges |
1781 | | * Very large CPU range specifications should be handled |
1782 | | */ |
1783 | | static int ThreadingAffinityTest21(void) |
1784 | | { |
1785 | | SCConfCreateContextBackup(); |
1786 | | SCConfInit(); |
1787 | | ResetAffinityForTest(); |
1788 | | |
1789 | | const char *config = "%YAML 1.1\n" |
1790 | | "---\n" |
1791 | | "threading:\n" |
1792 | | " cpu-affinity:\n" |
1793 | | " - management-cpu-set:\n" |
1794 | | " cpu: [ 0-99999 ]\n"; |
1795 | | |
1796 | | SCConfYamlLoadString(config, strlen(config)); |
1797 | | AffinitySetupLoadFromConfig(); |
1798 | | |
1799 | | SCConfRestoreContextBackup(); |
1800 | | PASS; |
1801 | | } |
1802 | | |
1803 | | /** |
1804 | | * \brief Test deeply nested interface configurations |
1805 | | * Prevent infinite loops in configuration parsing |
1806 | | */ |
1807 | | static int ThreadingAffinityTest22(void) |
1808 | | { |
1809 | | SCConfCreateContextBackup(); |
1810 | | SCConfInit(); |
1811 | | ResetAffinityForTest(); |
1812 | | |
1813 | | const char *config = "%YAML 1.1\n" |
1814 | | "---\n" |
1815 | | "threading:\n" |
1816 | | " cpu-affinity:\n" |
1817 | | " worker-cpu-set:\n" |
1818 | | " interface-specific-cpu-set:\n" |
1819 | | " - interface: eth0\n" |
1820 | | " cpu: [ 1 ]\n" |
1821 | | " interface-specific-cpu-set:\n" // Nested interface-specific |
1822 | | " - interface: eth1\n" |
1823 | | " cpu: [ 2 ]\n"; |
1824 | | |
1825 | | SCConfYamlLoadString(config, strlen(config)); |
1826 | | AffinitySetupLoadFromConfig(); |
1827 | | |
1828 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1829 | | FAIL_IF_NOT(worker_taf->nb_children == 1); |
1830 | | ThreadsAffinityType *iface_taf = worker_taf->children[0]; |
1831 | | FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0); |
1832 | | FAIL_IF_NOT(CPU_ISSET(1, &iface_taf->cpu_set)); |
1833 | | FAIL_IF_NOT(iface_taf->nb_children == 0); |
1834 | | |
1835 | | SCConfRestoreContextBackup(); |
1836 | | PASS; |
1837 | | } |
1838 | | |
1839 | | /** |
1840 | | * \brief Test GetAffinityTypeForNameAndIface with NULL and empty string parameters |
1841 | | * Comprehensive NULL parameter testing |
1842 | | */ |
1843 | | static int ThreadingAffinityTest23(void) |
1844 | | { |
1845 | | SCConfCreateContextBackup(); |
1846 | | SCConfInit(); |
1847 | | ResetAffinityForTest(); |
1848 | | |
1849 | | const char *config = "%YAML 1.1\n" |
1850 | | "---\n" |
1851 | | "threading:\n" |
1852 | | " cpu-affinity:\n" |
1853 | | " worker-cpu-set:\n" |
1854 | | " cpu: [ 1, 2, 3 ]\n"; |
1855 | | |
1856 | | SCConfYamlLoadString(config, strlen(config)); |
1857 | | AffinitySetupLoadFromConfig(); |
1858 | | |
1859 | | ThreadsAffinityType *result = GetAffinityTypeForNameAndIface(NULL, "eth0"); |
1860 | | FAIL_IF_NOT(result == NULL); |
1861 | | |
1862 | | result = GetAffinityTypeForNameAndIface("", "eth0"); |
1863 | | FAIL_IF_NOT(result == NULL); |
1864 | | |
1865 | | result = GetAffinityTypeForNameAndIface("worker-cpu-set", NULL); |
1866 | | FAIL_IF(result == NULL); |
1867 | | FAIL_IF_NOT(strcmp(result->name, "worker-cpu-set") == 0); |
1868 | | |
1869 | | result = GetAffinityTypeForNameAndIface("worker-cpu-set", ""); |
1870 | | FAIL_IF_NOT(result == NULL); // Returns NULL as no child with an empty name exists |
1871 | | |
1872 | | SCConfRestoreContextBackup(); |
1873 | | PASS; |
1874 | | } |
1875 | | |
1876 | | /** |
1877 | | * \brief Test interface-specific configuration with missing interface field |
1878 | | * Interface-specific configs with malformed structure |
1879 | | */ |
1880 | | static int ThreadingAffinityTest24(void) |
1881 | | { |
1882 | | SCConfCreateContextBackup(); |
1883 | | SCConfInit(); |
1884 | | ResetAffinityForTest(); |
1885 | | |
1886 | | const char *config = "%YAML 1.1\n" |
1887 | | "---\n" |
1888 | | "threading:\n" |
1889 | | " cpu-affinity:\n" |
1890 | | " - worker-cpu-set:\n" |
1891 | | " interface-specific-cpu-set:\n" |
1892 | | " - cpu: [ 1 ]\n" // Missing interface field |
1893 | | " mode: exclusive\n" |
1894 | | " - interface_name: eth0\n" // Wrong field name |
1895 | | " cpu: [ 2 ]\n"; |
1896 | | |
1897 | | SCConfYamlLoadString(config, strlen(config)); |
1898 | | AffinitySetupLoadFromConfig(); |
1899 | | |
1900 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1901 | | FAIL_IF_NOT(worker_taf->nb_children == 0); |
1902 | | |
1903 | | SCConfRestoreContextBackup(); |
1904 | | PASS; |
1905 | | } |
1906 | | |
1907 | | /** |
1908 | | * \brief Test AllocAndInitAffinityType with multiple allocations to test realloc paths |
1909 | | * Test dynamic array reallocation in parent-child relationships |
1910 | | */ |
1911 | | static int ThreadingAffinityTest25(void) |
1912 | | { |
1913 | | ResetAffinityForTest(); |
1914 | | |
1915 | | ThreadsAffinityType *parent = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", NULL); |
1916 | | FAIL_IF(parent == NULL); |
1917 | | |
1918 | | ThreadsAffinityType *child1 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface1"); |
1919 | | ThreadsAffinityType *child2 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface2"); |
1920 | | ThreadsAffinityType *child3 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface3"); |
1921 | | ThreadsAffinityType *child4 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface4"); |
1922 | | |
1923 | | FAIL_IF_NOT(child1 && child2 && child3 && child4); |
1924 | | FAIL_IF_NOT(parent->nb_children == 4); |
1925 | | |
1926 | | ThreadsAffinityType *found = FindAffinityByInterface(parent, "iface2"); |
1927 | | FAIL_IF_NOT(found == child2); |
1928 | | |
1929 | | found = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface2"); |
1930 | | FAIL_IF_NOT(found == child2); |
1931 | | |
1932 | | PASS; |
1933 | | } |
1934 | | |
1935 | | /** |
1936 | | * \brief Test AffinityGetYamlPath with very long name |
1937 | | * Path building with long string lengths to test for buffer overflows |
1938 | | */ |
1939 | | static int ThreadingAffinityTest26(void) |
1940 | | { |
1941 | | ResetAffinityForTest(); |
1942 | | |
1943 | | ThreadsAffinityType test_taf; |
1944 | | memset(&test_taf, 0, sizeof(test_taf)); |
1945 | | |
1946 | | char long_name[1024]; |
1947 | | memset(long_name, 'a', sizeof(long_name) - 1); |
1948 | | long_name[sizeof(long_name) - 1] = '\0'; |
1949 | | test_taf.name = long_name; |
1950 | | |
1951 | | char *path = AffinityGetYamlPath(&test_taf); |
1952 | | FAIL_IF_NOT(path == NULL); // overflows the internal buffer and return NULL |
1953 | | |
1954 | | path = AffinityGetYamlPath(NULL); // returns root path |
1955 | | FAIL_IF(path == NULL || strcmp(path, "threading.cpu-affinity") != 0); |
1956 | | |
1957 | | PASS; |
1958 | | } |
1959 | | |
1960 | | /** |
1961 | | * \brief Test mixed format configurations in same file |
1962 | | * Combination of new and deprecated formats |
1963 | | */ |
1964 | | static int ThreadingAffinityTest27(void) |
1965 | | { |
1966 | | SCConfCreateContextBackup(); |
1967 | | SCConfInit(); |
1968 | | ResetAffinityForTest(); |
1969 | | |
1970 | | const char *config = "%YAML 1.1\n" |
1971 | | "---\n" |
1972 | | "threading:\n" |
1973 | | " cpu-affinity:\n" |
1974 | | " management-cpu-set:\n" // New format |
1975 | | " cpu: [ 0 ]\n" |
1976 | | " - worker-cpu-set:\n" // Deprecated format |
1977 | | " cpu: [ 1, 2 ]\n"; |
1978 | | |
1979 | | SCConfYamlLoadString(config, strlen(config)); |
1980 | | AffinitySetupLoadFromConfig(); |
1981 | | |
1982 | | ThreadsAffinityType *mgmt_taf = &thread_affinity[MANAGEMENT_CPU_SET]; |
1983 | | ThreadsAffinityType *worker_taf = &thread_affinity[WORKER_CPU_SET]; |
1984 | | // The first format should be picked-up and the other should be ignored |
1985 | | // For ignored formats, CPU_SET is initliazed as all cores |
1986 | | FAIL_IF(CPU_COUNT(&mgmt_taf->cpu_set) != 1 || |
1987 | | CPU_COUNT(&worker_taf->cpu_set) != UtilCpuGetNumProcessorsOnline()); |
1988 | | |
1989 | | SCConfRestoreContextBackup(); |
1990 | | PASS; |
1991 | | } |
1992 | | |
1993 | | #endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) */ |
1994 | | |
1995 | | /** |
1996 | | * \brief Register all threading affinity unit tests |
1997 | | */ |
1998 | | void ThreadingAffinityRegisterTests(void) |
1999 | | { |
2000 | | #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) |
2001 | | UtRegisterTest("ThreadingAffinityTest01", ThreadingAffinityTest01); |
2002 | | UtRegisterTest("ThreadingAffinityTest02", ThreadingAffinityTest02); |
2003 | | UtRegisterTest("ThreadingAffinityTest03", ThreadingAffinityTest03); |
2004 | | UtRegisterTest("ThreadingAffinityTest04", ThreadingAffinityTest04); |
2005 | | UtRegisterTest("ThreadingAffinityTest05", ThreadingAffinityTest05); |
2006 | | UtRegisterTest("ThreadingAffinityTest06", ThreadingAffinityTest06); |
2007 | | UtRegisterTest("ThreadingAffinityTest07", ThreadingAffinityTest07); |
2008 | | UtRegisterTest("ThreadingAffinityTest08", ThreadingAffinityTest08); |
2009 | | UtRegisterTest("ThreadingAffinityTest09", ThreadingAffinityTest09); |
2010 | | UtRegisterTest("ThreadingAffinityTest10", ThreadingAffinityTest10); |
2011 | | UtRegisterTest("ThreadingAffinityTest11", ThreadingAffinityTest11); |
2012 | | UtRegisterTest("ThreadingAffinityTest12", ThreadingAffinityTest12); |
2013 | | UtRegisterTest("ThreadingAffinityTest13", ThreadingAffinityTest13); |
2014 | | UtRegisterTest("ThreadingAffinityTest14", ThreadingAffinityTest14); |
2015 | | UtRegisterTest("ThreadingAffinityTest15", ThreadingAffinityTest15); |
2016 | | UtRegisterTest("ThreadingAffinityTest16", ThreadingAffinityTest16); |
2017 | | UtRegisterTest("ThreadingAffinityTest17", ThreadingAffinityTest17); |
2018 | | UtRegisterTest("ThreadingAffinityTest18", ThreadingAffinityTest18); |
2019 | | UtRegisterTest("ThreadingAffinityTest19", ThreadingAffinityTest19); |
2020 | | UtRegisterTest("ThreadingAffinityTest20", ThreadingAffinityTest20); |
2021 | | UtRegisterTest("ThreadingAffinityTest21", ThreadingAffinityTest21); |
2022 | | UtRegisterTest("ThreadingAffinityTest22", ThreadingAffinityTest22); |
2023 | | UtRegisterTest("ThreadingAffinityTest23", ThreadingAffinityTest23); |
2024 | | UtRegisterTest("ThreadingAffinityTest24", ThreadingAffinityTest24); |
2025 | | UtRegisterTest("ThreadingAffinityTest25", ThreadingAffinityTest25); |
2026 | | UtRegisterTest("ThreadingAffinityTest26", ThreadingAffinityTest26); |
2027 | | UtRegisterTest("ThreadingAffinityTest27", ThreadingAffinityTest27); |
2028 | | #endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) */ |
2029 | | } |
2030 | | |
2031 | | #endif /* UNITTESTS */ |