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