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
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 {
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  */
138 ThreadsAffinityType *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
210 static void AffinitySetupInit(void)
211 {
212  int i, j;
213  int ncpu = UtilCpuGetNumProcessorsOnline();
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 
279 static void AffinityCallback(int i, void *data)
280 {
281  CPU_SET(i, (cpu_set_t *)data);
282 }
283 
284 static 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  */
292 static 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  */
305 static 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  */
321 static 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  */
338 static 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  */
365 static 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  */
386 static 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  */
408 static 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 
460 static 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  */
470 static 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  */
478 static 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  */
490 static 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  */
529 static 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  */
555 static 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
648 static 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 
681 static 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  */
709 static 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
723 static 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 
739 static 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 
773 static 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()));
781  SCMutexUnlock(&taf->taf_mutex);
782  return cond;
783 }
784 
785 /**
786  * \brief Initialize the hardware topology.
787  * \retval 0 on success, -1 on error
788  */
789 static 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 
809 void TopologyDestroy(void)
810 {
811  if (topology != NULL) {
812  hwloc_topology_destroy(topology);
813  topology = NULL;
814  }
815 }
816 
817 static 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 
845 static 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 
888 static 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 
912 static 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 
920 static 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  */
946 static 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 
964 static 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 
986 static 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 
995 static 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;
1024  SCLogWarning(
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  */
1062 uint16_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  */
1089 void UtilAffinityCpusExclude(ThreadsAffinityType *mod_taf, ThreadsAffinityType *static_taf)
1090 {
1091  SCMutexLock(&mod_taf->taf_mutex);
1092  SCMutexLock(&static_taf->taf_mutex);
1093  int max_cpus = UtilCpuGetNumProcessorsOnline();
1094  for (int cpu = 0; cpu < max_cpus; cpu++) {
1095  if (CPU_ISSET(cpu, &mod_taf->cpu_set) && CPU_ISSET(cpu, &static_taf->cpu_set)) {
1096  CPU_CLR(cpu, &mod_taf->cpu_set);
1097  }
1098  }
1099  SCMutexUnlock(&static_taf->taf_mutex);
1100  SCMutexUnlock(&mod_taf->taf_mutex);
1101 }
1102 #endif /* HAVE_DPDK */
1103 
1104 #ifdef UNITTESTS
1105 // avoiding Darwin/MacOS as it does not support bitwise CPU affinity
1106 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
1107 
1108 /**
1109  * \brief Helper function to reset affinity state for unit tests
1110  * This properly clears CPU sets without destroying initialized mutexes
1111  */
1112 static void ResetAffinityForTest(void)
1113 {
1115  for (int i = 0; i < MAX_CPU_SET; i++) {
1117  CPU_ZERO(&taf->cpu_set);
1118  CPU_ZERO(&taf->lowprio_cpu);
1119  CPU_ZERO(&taf->medprio_cpu);
1120  CPU_ZERO(&taf->hiprio_cpu);
1121  taf->nb_threads = 0;
1122  taf->prio = PRIO_LOW;
1124 
1125  for (int j = 0; j < MAX_NUMA_NODES; j++) {
1126  taf->lcpu[j] = 0;
1127  }
1128 
1129  if (taf->children) {
1130  for (uint32_t j = 0; j < taf->nb_children; j++) {
1131  if (taf->children[j]) {
1132  SCFree((void *)taf->children[j]->name);
1133  SCFree(taf->children[j]);
1134  }
1135  }
1136  SCFree(taf->children);
1137  taf->children = NULL;
1138  }
1139  taf->nb_children = 0;
1140  taf->nb_children_capacity = 0;
1141 
1142  if (i == MANAGEMENT_CPU_SET) {
1143  taf->name = "management-cpu-set";
1144  } else if (i == WORKER_CPU_SET) {
1145  taf->name = "worker-cpu-set";
1146  } else if (i == VERDICT_CPU_SET) {
1147  taf->name = "verdict-cpu-set";
1148  } else if (i == RECEIVE_CPU_SET) {
1149  taf->name = "receive-cpu-set";
1150  } else {
1151  taf->name = NULL;
1152  }
1153 
1154  // Don't touch the mutex - it should remain initialized
1155  }
1156 }
1157 
1158 /**
1159  * \brief Test basic CPU affinity parsing in new format
1160  */
1161 static int ThreadingAffinityTest01(void)
1162 {
1164  SCConfInit();
1165  ResetAffinityForTest();
1166  const char *config = "%YAML 1.1\n"
1167  "---\n"
1168  "threading:\n"
1169  " cpu-affinity:\n"
1170  " management-cpu-set:\n"
1171  " cpu: [ 0 ]\n"
1172  " worker-cpu-set:\n"
1173  " cpu: [ 1, 2, 3 ]\n";
1174 
1175  SCConfYamlLoadString(config, strlen(config));
1176 
1178  FAIL_IF_NOT(AffinityConfigIsLegacy() == false);
1179 
1181  FAIL_IF_NOT(CPU_ISSET(0, &mgmt_taf->cpu_set));
1182  FAIL_IF_NOT(CPU_COUNT(&mgmt_taf->cpu_set) == 1);
1183 
1185  FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->cpu_set));
1186  FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->cpu_set));
1187  FAIL_IF_NOT(CPU_ISSET(3, &worker_taf->cpu_set));
1188  FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set));
1189 
1190  SCConfDeInit();
1192  PASS;
1193 }
1194 
1195 /**
1196  * \brief Test deprecated CPU affinity format parsing
1197  */
1198 static int ThreadingAffinityTest02(void)
1199 {
1201  SCConfInit();
1202  ResetAffinityForTest();
1203 
1204  const char *config = "%YAML 1.1\n"
1205  "---\n"
1206  "threading:\n"
1207  " cpu-affinity:\n"
1208  " - worker-cpu-set:\n"
1209  " cpu: [ 1, 2 ]\n";
1210 
1211  SCConfYamlLoadString(config, strlen(config));
1213  FAIL_IF_NOT(AffinityConfigIsLegacy() == true);
1214 
1216  FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->cpu_set));
1217  FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->cpu_set));
1218  FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == 2);
1219 
1220  SCConfDeInit();
1222  PASS;
1223 }
1224 
1225 /**
1226  * \brief Test CPU range parsing ("0-3")
1227  */
1228 static int ThreadingAffinityTest03(void)
1229 {
1231  SCConfInit();
1232  ResetAffinityForTest();
1233 
1234  const char *config = "%YAML 1.1\n"
1235  "---\n"
1236  "threading:\n"
1237  " cpu-affinity:\n"
1238  " worker-cpu-set:\n"
1239  " cpu: [ \"0-3\" ]\n";
1240 
1241  SCConfYamlLoadString(config, strlen(config));
1243 
1245  FAIL_IF_NOT(CPU_ISSET(0, &worker_taf->cpu_set));
1246  FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->cpu_set));
1247  FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->cpu_set));
1248  FAIL_IF_NOT(CPU_ISSET(3, &worker_taf->cpu_set));
1249  FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == 4);
1250 
1251  SCConfDeInit();
1253  PASS;
1254 }
1255 
1256 /**
1257  * \brief Test mixed CPU specification parsing (individual CPUs in list)
1258  */
1259 static int ThreadingAffinityTest04(void)
1260 {
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));
1274 
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  SCConfDeInit();
1286  PASS;
1287 }
1288 
1289 /**
1290  * \brief Test "all" CPU specification
1291  */
1292 static int ThreadingAffinityTest05(void)
1293 {
1295  SCConfInit();
1296  ResetAffinityForTest();
1297 
1298  const char *config = "%YAML 1.1\n"
1299  "---\n"
1300  "threading:\n"
1301  " cpu-affinity:\n"
1302  " worker-cpu-set:\n"
1303  " cpu: [ \"all\" ]\n";
1304 
1305  SCConfYamlLoadString(config, strlen(config));
1307 
1309  FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == UtilCpuGetNumProcessorsOnline());
1310 
1311  SCConfDeInit();
1313  PASS;
1314 }
1315 
1316 /**
1317  * \brief Test priority settings parsing
1318  */
1319 static int ThreadingAffinityTest06(void)
1320 {
1322  SCConfInit();
1323  ResetAffinityForTest();
1324 
1325  const char *config = "%YAML 1.1\n"
1326  "---\n"
1327  "threading:\n"
1328  " cpu-affinity:\n"
1329  " worker-cpu-set:\n"
1330  " cpu: [ 0, 1, 2, 3 ]\n"
1331  " prio:\n"
1332  " low: [ 0 ]\n"
1333  " medium: [ \"1-2\" ]\n"
1334  " high: [ 3 ]\n"
1335  " default: \"medium\"\n";
1336 
1337  SCConfYamlLoadString(config, strlen(config));
1339 
1341  FAIL_IF_NOT(CPU_ISSET(0, &worker_taf->lowprio_cpu));
1342  FAIL_IF_NOT(CPU_ISSET(1, &worker_taf->medprio_cpu));
1343  FAIL_IF_NOT(CPU_ISSET(2, &worker_taf->medprio_cpu));
1344  FAIL_IF_NOT(CPU_ISSET(3, &worker_taf->hiprio_cpu));
1345  FAIL_IF_NOT(worker_taf->prio == PRIO_MEDIUM);
1346 
1347  SCConfDeInit();
1349  PASS;
1350 }
1351 
1352 /**
1353  * \brief Test mode settings (exclusive/balanced)
1354  */
1355 static int ThreadingAffinityTest07(void)
1356 {
1358  SCConfInit();
1359  ResetAffinityForTest();
1360 
1361  const char *config = "%YAML 1.1\n"
1362  "---\n"
1363  "threading:\n"
1364  " cpu-affinity:\n"
1365  " worker-cpu-set:\n"
1366  " cpu: [ 0, 1 ]\n"
1367  " mode: \"exclusive\"\n";
1368 
1369  SCConfYamlLoadString(config, strlen(config));
1371 
1373  FAIL_IF_NOT(worker_taf->mode_flag == EXCLUSIVE_AFFINITY);
1374 
1375  SCConfDeInit();
1377  PASS;
1378 }
1379 
1380 /**
1381  * \brief Test threads count parsing
1382  */
1383 static int ThreadingAffinityTest08(void)
1384 {
1386  SCConfInit();
1387  ResetAffinityForTest();
1388 
1389  const char *config = "%YAML 1.1\n"
1390  "---\n"
1391  "threading:\n"
1392  " cpu-affinity:\n"
1393  " worker-cpu-set:\n"
1394  " cpu: [ 0, 1, 2 ]\n"
1395  " threads: 4\n";
1396 
1397  SCConfYamlLoadString(config, strlen(config));
1399 
1401  FAIL_IF_NOT(worker_taf->nb_threads == 4);
1402 
1403  SCConfDeInit();
1405  PASS;
1406 }
1407 
1408 /**
1409  * \brief Test interface-specific CPU set parsing
1410  */
1411 static int ThreadingAffinityTest09(void)
1412 {
1414  SCConfInit();
1415  ResetAffinityForTest();
1416 
1417  const char *config = "%YAML 1.1\n"
1418  "---\n"
1419  "threading:\n"
1420  " cpu-affinity:\n"
1421  " worker-cpu-set:\n"
1422  " cpu: [ 0, 1 ]\n"
1423  " interface-specific-cpu-set:\n"
1424  " - interface: \"eth0\"\n"
1425  " cpu: [ 2, 3 ]\n"
1426  " mode: \"exclusive\"\n";
1427 
1428  SCConfYamlLoadString(config, strlen(config));
1430 
1432  FAIL_IF_NOT(worker_taf->nb_children == 1);
1433 
1434  ThreadsAffinityType *iface_taf = worker_taf->children[0];
1435  FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0);
1436  FAIL_IF_NOT(CPU_ISSET(2, &iface_taf->cpu_set));
1437  FAIL_IF_NOT(CPU_ISSET(3, &iface_taf->cpu_set));
1438  FAIL_IF_NOT(CPU_COUNT(&iface_taf->cpu_set) == 2);
1439  FAIL_IF_NOT(iface_taf->mode_flag == EXCLUSIVE_AFFINITY);
1440 
1441  SCConfDeInit();
1443  PASS;
1444 }
1445 
1446 /**
1447  * \brief Test multiple interface-specific CPU sets
1448  */
1449 static int ThreadingAffinityTest10(void)
1450 {
1452  SCConfInit();
1453  ResetAffinityForTest();
1454 
1455  const char *config = "%YAML 1.1\n"
1456  "---\n"
1457  "threading:\n"
1458  " cpu-affinity:\n"
1459  " receive-cpu-set:\n"
1460  " cpu: [ 0 ]\n"
1461  " interface-specific-cpu-set:\n"
1462  " - interface: \"eth0\"\n"
1463  " cpu: [ 1, 2 ]\n"
1464  " - interface: \"eth1\"\n"
1465  " cpu: [ 3, 4 ]\n";
1466 
1467  SCConfYamlLoadString(config, strlen(config));
1469 
1471  FAIL_IF_NOT(receive_taf->nb_children == 2);
1472 
1473  bool eth0_found = false, eth1_found = false;
1474 
1475  for (uint32_t i = 0; i < receive_taf->nb_children; i++) {
1476  ThreadsAffinityType *iface_taf = receive_taf->children[i];
1477  if (strcmp(iface_taf->name, "eth0") == 0) {
1478  if (CPU_ISSET(1, &iface_taf->cpu_set) && CPU_ISSET(2, &iface_taf->cpu_set) &&
1479  CPU_COUNT(&iface_taf->cpu_set) == 2) {
1480  eth0_found = true;
1481  }
1482  } else if (strcmp(iface_taf->name, "eth1") == 0) {
1483  if (CPU_ISSET(3, &iface_taf->cpu_set) && CPU_ISSET(4, &iface_taf->cpu_set) &&
1484  CPU_COUNT(&iface_taf->cpu_set) == 2) {
1485  eth1_found = true;
1486  }
1487  }
1488  }
1489 
1490  FAIL_IF_NOT(eth0_found && eth1_found);
1491 
1492  SCConfDeInit();
1494  PASS;
1495 }
1496 
1497 /**
1498  * \brief Test interface-specific priority settings
1499  */
1500 static int ThreadingAffinityTest11(void)
1501 {
1503  SCConfInit();
1504  ResetAffinityForTest();
1505 
1506  const char *config = "%YAML 1.1\n"
1507  "---\n"
1508  "threading:\n"
1509  " cpu-affinity:\n"
1510  " worker-cpu-set:\n"
1511  " cpu: [ 0 ]\n"
1512  " interface-specific-cpu-set:\n"
1513  " - interface: \"eth0\"\n"
1514  " cpu: [ 1, 2, 3 ]\n"
1515  " prio:\n"
1516  " high: [ \"all\" ]\n"
1517  " default: \"high\"\n";
1518 
1519  SCConfYamlLoadString(config, strlen(config));
1521 
1523  FAIL_IF_NOT(worker_taf->nb_children == 1);
1524 
1525  ThreadsAffinityType *iface_taf = worker_taf->children[0];
1526  FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0);
1527  FAIL_IF_NOT(CPU_ISSET(1, &iface_taf->hiprio_cpu));
1528  FAIL_IF_NOT(CPU_ISSET(2, &iface_taf->hiprio_cpu));
1529  FAIL_IF_NOT(CPU_ISSET(3, &iface_taf->hiprio_cpu));
1530  FAIL_IF_NOT(iface_taf->prio == PRIO_HIGH);
1531 
1532  SCConfDeInit();
1534  PASS;
1535 }
1536 
1537 /**
1538  * \brief Test complete configuration with all CPU sets
1539  */
1540 static int ThreadingAffinityTest12(void)
1541 {
1543  SCConfInit();
1544  ResetAffinityForTest();
1545 
1546  const char *config = "%YAML 1.1\n"
1547  "---\n"
1548  "threading:\n"
1549  " cpu-affinity:\n"
1550  " management-cpu-set:\n"
1551  " cpu: [ 0 ]\n"
1552  " receive-cpu-set:\n"
1553  " cpu: [ 1 ]\n"
1554  " worker-cpu-set:\n"
1555  " cpu: [ 2, 3 ]\n"
1556  " interface-specific-cpu-set:\n"
1557  " - interface: \"eth0\"\n"
1558  " cpu: [ \"5-7\" ]\n"
1559  " prio:\n"
1560  " high: [ \"all\" ]\n"
1561  " default: \"high\"\n"
1562  " verdict-cpu-set:\n"
1563  " cpu: [ 4 ]\n";
1564 
1565  SCConfYamlLoadString(config, strlen(config));
1567 
1568  FAIL_IF_NOT(CPU_ISSET(0, &thread_affinity[MANAGEMENT_CPU_SET].cpu_set));
1569  FAIL_IF_NOT(CPU_COUNT(&thread_affinity[MANAGEMENT_CPU_SET].cpu_set) == 1);
1570  FAIL_IF_NOT(CPU_ISSET(1, &thread_affinity[RECEIVE_CPU_SET].cpu_set));
1571  FAIL_IF_NOT(CPU_COUNT(&thread_affinity[RECEIVE_CPU_SET].cpu_set) == 1);
1572  FAIL_IF_NOT(CPU_ISSET(4, &thread_affinity[VERDICT_CPU_SET].cpu_set));
1573  FAIL_IF_NOT(CPU_COUNT(&thread_affinity[VERDICT_CPU_SET].cpu_set) == 1);
1574  FAIL_IF_NOT(CPU_ISSET(2, &thread_affinity[WORKER_CPU_SET].cpu_set));
1575  FAIL_IF_NOT(CPU_ISSET(3, &thread_affinity[WORKER_CPU_SET].cpu_set));
1576 
1577  FAIL_IF_NOT(thread_affinity[WORKER_CPU_SET].nb_children == 1);
1579  FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0);
1580  FAIL_IF_NOT(CPU_ISSET(1, &iface_taf->hiprio_cpu));
1581  FAIL_IF_NOT(CPU_ISSET(2, &iface_taf->hiprio_cpu));
1582  FAIL_IF_NOT(CPU_ISSET(3, &iface_taf->hiprio_cpu));
1583  FAIL_IF_NOT(iface_taf->prio == PRIO_HIGH);
1584  FAIL_IF_NOT(CPU_COUNT(&thread_affinity[WORKER_CPU_SET].cpu_set) == 2);
1585 
1586  SCConfDeInit();
1588  PASS;
1589 }
1590 
1591 /**
1592  * \brief Test error handling for malformed CPU specification
1593  */
1594 static int ThreadingAffinityTest13(void)
1595 {
1597  SCConfInit();
1598  ResetAffinityForTest();
1599 
1600  const char *config = "%YAML 1.1\n"
1601  "---\n"
1602  "threading:\n"
1603  " cpu-affinity:\n"
1604  " worker-cpu-set:\n"
1605  " cpu: [ \"invalid-cpu\" ]\n";
1606 
1607  SCConfYamlLoadString(config, strlen(config));
1609 
1611  FAIL_IF_NOT(CPU_COUNT(&worker_taf->cpu_set) == 0);
1612 
1613  SCConfDeInit();
1615  PASS;
1616 }
1617 
1618 /**
1619  * \brief Test empty configuration handling
1620  */
1621 static int ThreadingAffinityTest14(void)
1622 {
1624  SCConfInit();
1625  ResetAffinityForTest();
1626 
1627  const char *config = "%YAML 1.1\n"
1628  "---\n"
1629  "threading:\n"
1630  " cpu-affinity:\n";
1631 
1632  SCConfYamlLoadString(config, strlen(config));
1634 
1635  FAIL_IF_NOT(
1637 
1638  SCConfDeInit();
1640  PASS;
1641 }
1642 
1643 /**
1644  * \brief Test CPU range parsing with invalid order (high-low)
1645  * CPU ranges specified in reverse order should be handled
1646  */
1647 static int ThreadingAffinityTest15(void)
1648 {
1650  SCConfInit();
1651  ResetAffinityForTest();
1652 
1653  const char *config = "%YAML 1.1\n"
1654  "---\n"
1655  "threading:\n"
1656  " cpu-affinity:\n"
1657  " - management-cpu-set:\n"
1658  " cpu: [ \"3-1\" ]\n"; // Invalid reverse range
1659 
1660  SCConfYamlLoadString(config, strlen(config));
1662 
1663  FAIL_IF_NOT(CPU_COUNT(&thread_affinity[MANAGEMENT_CPU_SET].cpu_set) == 0);
1664 
1665  SCConfDeInit();
1667  PASS;
1668 }
1669 
1670 /**
1671  * \brief Test invalid priority values in SetupDefaultPriority
1672  * Invalid priority strings should return errors but pass
1673  */
1674 static int ThreadingAffinityTest16(void)
1675 {
1677  SCConfInit();
1678  ResetAffinityForTest();
1679 
1680  const char *config = "%YAML 1.1\n"
1681  "---\n"
1682  "threading:\n"
1683  " cpu-affinity:\n"
1684  " - management-cpu-set:\n"
1685  " prio:\n"
1686  " default: invalid_priority\n";
1687 
1688  SCConfYamlLoadString(config, strlen(config));
1690 
1691  SCConfDeInit();
1693  PASS;
1694 }
1695 
1696 /**
1697  * \brief Test invalid CPU affinity mode values
1698  * Invalid mode strings should return errors but pass
1699  */
1700 static int ThreadingAffinityTest17(void)
1701 {
1703  SCConfInit();
1704  ResetAffinityForTest();
1705 
1706  const char *config = "%YAML 1.1\n"
1707  "---\n"
1708  "threading:\n"
1709  " cpu-affinity:\n"
1710  " - management-cpu-set:\n"
1711  " mode: invalid_mode\n";
1712 
1713  SCConfYamlLoadString(config, strlen(config));
1715 
1716  SCConfDeInit();
1718  PASS;
1719 }
1720 
1721 /**
1722  * \brief Test invalid thread count values
1723  * Invalid thread counts
1724  */
1725 static int ThreadingAffinityTest18(void)
1726 {
1728  SCConfInit();
1729  ResetAffinityForTest();
1730 
1731  const char *config = "%YAML 1.1\n"
1732  "---\n"
1733  "threading:\n"
1734  " cpu-affinity:\n"
1735  " - management-cpu-set:\n"
1736  " threads: 0\n";
1737 
1738  SCConfYamlLoadString(config, strlen(config));
1740 
1741  SCConfDeInit();
1743  PASS;
1744 }
1745 
1746 /**
1747  * \brief Test CPU specification with negative numbers
1748  * Negative CPU numbers should be rejected
1749  */
1750 static int ThreadingAffinityTest19(void)
1751 {
1753  SCConfInit();
1754  ResetAffinityForTest();
1755 
1756  const char *config = "%YAML 1.1\n"
1757  "---\n"
1758  "threading:\n"
1759  " cpu-affinity:\n"
1760  " - management-cpu-set:\n"
1761  " cpu: [ -1 ]\n";
1762 
1763  SCConfYamlLoadString(config, strlen(config));
1765 
1766  SCConfDeInit();
1768  PASS;
1769 }
1770 
1771 /**
1772  * \brief Test invalid thread count with non-numeric values
1773  * Non-numeric thread counts should be handled
1774  */
1775 static int ThreadingAffinityTest20(void)
1776 {
1778  SCConfInit();
1779  ResetAffinityForTest();
1780 
1781  const char *config = "%YAML 1.1\n"
1782  "---\n"
1783  "threading:\n"
1784  " cpu-affinity:\n"
1785  " - management-cpu-set:\n"
1786  " threads: invalid_number\n";
1787 
1788  SCConfYamlLoadString(config, strlen(config));
1790 
1791  SCConfDeInit();
1793  PASS;
1794 }
1795 
1796 /**
1797  * \brief Test extremely large CPU ranges
1798  * Very large CPU range specifications should be handled
1799  */
1800 static int ThreadingAffinityTest21(void)
1801 {
1803  SCConfInit();
1804  ResetAffinityForTest();
1805 
1806  const char *config = "%YAML 1.1\n"
1807  "---\n"
1808  "threading:\n"
1809  " cpu-affinity:\n"
1810  " - management-cpu-set:\n"
1811  " cpu: [ 0-99999 ]\n";
1812 
1813  SCConfYamlLoadString(config, strlen(config));
1815 
1816  SCConfDeInit();
1818  PASS;
1819 }
1820 
1821 /**
1822  * \brief Test deeply nested interface configurations
1823  * Prevent infinite loops in configuration parsing
1824  */
1825 static int ThreadingAffinityTest22(void)
1826 {
1828  SCConfInit();
1829  ResetAffinityForTest();
1830 
1831  const char *config = "%YAML 1.1\n"
1832  "---\n"
1833  "threading:\n"
1834  " cpu-affinity:\n"
1835  " worker-cpu-set:\n"
1836  " interface-specific-cpu-set:\n"
1837  " - interface: eth0\n"
1838  " cpu: [ 1 ]\n"
1839  " interface-specific-cpu-set:\n" // Nested interface-specific
1840  " - interface: eth1\n"
1841  " cpu: [ 2 ]\n";
1842 
1843  SCConfYamlLoadString(config, strlen(config));
1845 
1847  FAIL_IF_NOT(worker_taf->nb_children == 1);
1848  ThreadsAffinityType *iface_taf = worker_taf->children[0];
1849  FAIL_IF_NOT(strcmp(iface_taf->name, "eth0") == 0);
1850  FAIL_IF_NOT(CPU_ISSET(1, &iface_taf->cpu_set));
1851  FAIL_IF_NOT(iface_taf->nb_children == 0);
1852 
1853  SCConfDeInit();
1855  PASS;
1856 }
1857 
1858 /**
1859  * \brief Test GetAffinityTypeForNameAndIface with NULL and empty string parameters
1860  * Comprehensive NULL parameter testing
1861  */
1862 static int ThreadingAffinityTest23(void)
1863 {
1865  SCConfInit();
1866  ResetAffinityForTest();
1867 
1868  const char *config = "%YAML 1.1\n"
1869  "---\n"
1870  "threading:\n"
1871  " cpu-affinity:\n"
1872  " worker-cpu-set:\n"
1873  " cpu: [ 1, 2, 3 ]\n";
1874 
1875  SCConfYamlLoadString(config, strlen(config));
1877 
1878  ThreadsAffinityType *result = GetAffinityTypeForNameAndIface(NULL, "eth0");
1879  FAIL_IF_NOT(result == NULL);
1880 
1881  result = GetAffinityTypeForNameAndIface("", "eth0");
1882  FAIL_IF_NOT(result == NULL);
1883 
1884  result = GetAffinityTypeForNameAndIface("worker-cpu-set", NULL);
1885  FAIL_IF(result == NULL);
1886  FAIL_IF_NOT(strcmp(result->name, "worker-cpu-set") == 0);
1887 
1888  result = GetAffinityTypeForNameAndIface("worker-cpu-set", "");
1889  FAIL_IF_NOT(result == NULL); // Returns NULL as no child with an empty name exists
1890 
1891  SCConfDeInit();
1893  PASS;
1894 }
1895 
1896 /**
1897  * \brief Test interface-specific configuration with missing interface field
1898  * Interface-specific configs with malformed structure
1899  */
1900 static int ThreadingAffinityTest24(void)
1901 {
1903  SCConfInit();
1904  ResetAffinityForTest();
1905 
1906  const char *config = "%YAML 1.1\n"
1907  "---\n"
1908  "threading:\n"
1909  " cpu-affinity:\n"
1910  " - worker-cpu-set:\n"
1911  " interface-specific-cpu-set:\n"
1912  " - cpu: [ 1 ]\n" // Missing interface field
1913  " mode: exclusive\n"
1914  " - interface_name: eth0\n" // Wrong field name
1915  " cpu: [ 2 ]\n";
1916 
1917  SCConfYamlLoadString(config, strlen(config));
1919 
1921  FAIL_IF_NOT(worker_taf->nb_children == 0);
1922 
1923  SCConfDeInit();
1925  PASS;
1926 }
1927 
1928 /**
1929  * \brief Test AllocAndInitAffinityType with multiple allocations to test realloc paths
1930  * Test dynamic array reallocation in parent-child relationships
1931  */
1932 static int ThreadingAffinityTest25(void)
1933 {
1934  ResetAffinityForTest();
1935 
1936  ThreadsAffinityType *parent = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", NULL);
1937  FAIL_IF(parent == NULL);
1938 
1939  ThreadsAffinityType *child1 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface1");
1940  ThreadsAffinityType *child2 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface2");
1941  ThreadsAffinityType *child3 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface3");
1942  ThreadsAffinityType *child4 = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface4");
1943 
1944  FAIL_IF_NOT(child1 && child2 && child3 && child4);
1945  FAIL_IF_NOT(parent->nb_children == 4);
1946 
1947  ThreadsAffinityType *found = FindAffinityByInterface(parent, "iface2");
1948  FAIL_IF_NOT(found == child2);
1949 
1950  found = GetOrAllocAffinityTypeForIfaceOfName("worker-cpu-set", "iface2");
1951  FAIL_IF_NOT(found == child2);
1952 
1953  PASS;
1954 }
1955 
1956 /**
1957  * \brief Test AffinityGetYamlPath with very long name
1958  * Path building with long string lengths to test for buffer overflows
1959  */
1960 static int ThreadingAffinityTest26(void)
1961 {
1962  ResetAffinityForTest();
1963 
1964  ThreadsAffinityType test_taf;
1965  memset(&test_taf, 0, sizeof(test_taf));
1966 
1967  char long_name[1024];
1968  memset(long_name, 'a', sizeof(long_name) - 1);
1969  long_name[sizeof(long_name) - 1] = '\0';
1970  test_taf.name = long_name;
1971 
1972  char *path = AffinityGetYamlPath(&test_taf);
1973  FAIL_IF_NOT(path == NULL); // overflows the internal buffer and return NULL
1974 
1975  path = AffinityGetYamlPath(NULL); // returns root path
1976  FAIL_IF(path == NULL || strcmp(path, "threading.cpu-affinity") != 0);
1977 
1978  PASS;
1979 }
1980 
1981 /**
1982  * \brief Test mixed format configurations in same file
1983  * Combination of new and deprecated formats
1984  */
1985 static int ThreadingAffinityTest27(void)
1986 {
1988  SCConfInit();
1989  ResetAffinityForTest();
1990 
1991  const char *config = "%YAML 1.1\n"
1992  "---\n"
1993  "threading:\n"
1994  " cpu-affinity:\n"
1995  " management-cpu-set:\n" // New format
1996  " cpu: [ 0 ]\n"
1997  " - worker-cpu-set:\n" // Deprecated format
1998  " cpu: [ 1, 2 ]\n";
1999 
2000  SCConfYamlLoadString(config, strlen(config));
2002 
2005  // The first format should be picked-up and the other should be ignored
2006  // For ignored formats, CPU_SET is initliazed as all cores
2007  FAIL_IF(CPU_COUNT(&mgmt_taf->cpu_set) != 1 ||
2008  CPU_COUNT(&worker_taf->cpu_set) != UtilCpuGetNumProcessorsOnline());
2009 
2010  SCConfDeInit();
2012  PASS;
2013 }
2014 
2015 #endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) */
2016 
2017 /**
2018  * \brief Register all threading affinity unit tests
2019  */
2021 {
2022 #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
2023  UtRegisterTest("ThreadingAffinityTest01", ThreadingAffinityTest01);
2024  UtRegisterTest("ThreadingAffinityTest02", ThreadingAffinityTest02);
2025  UtRegisterTest("ThreadingAffinityTest03", ThreadingAffinityTest03);
2026  UtRegisterTest("ThreadingAffinityTest04", ThreadingAffinityTest04);
2027  UtRegisterTest("ThreadingAffinityTest05", ThreadingAffinityTest05);
2028  UtRegisterTest("ThreadingAffinityTest06", ThreadingAffinityTest06);
2029  UtRegisterTest("ThreadingAffinityTest07", ThreadingAffinityTest07);
2030  UtRegisterTest("ThreadingAffinityTest08", ThreadingAffinityTest08);
2031  UtRegisterTest("ThreadingAffinityTest09", ThreadingAffinityTest09);
2032  UtRegisterTest("ThreadingAffinityTest10", ThreadingAffinityTest10);
2033  UtRegisterTest("ThreadingAffinityTest11", ThreadingAffinityTest11);
2034  UtRegisterTest("ThreadingAffinityTest12", ThreadingAffinityTest12);
2035  UtRegisterTest("ThreadingAffinityTest13", ThreadingAffinityTest13);
2036  UtRegisterTest("ThreadingAffinityTest14", ThreadingAffinityTest14);
2037  UtRegisterTest("ThreadingAffinityTest15", ThreadingAffinityTest15);
2038  UtRegisterTest("ThreadingAffinityTest16", ThreadingAffinityTest16);
2039  UtRegisterTest("ThreadingAffinityTest17", ThreadingAffinityTest17);
2040  UtRegisterTest("ThreadingAffinityTest18", ThreadingAffinityTest18);
2041  UtRegisterTest("ThreadingAffinityTest19", ThreadingAffinityTest19);
2042  UtRegisterTest("ThreadingAffinityTest20", ThreadingAffinityTest20);
2043  UtRegisterTest("ThreadingAffinityTest21", ThreadingAffinityTest21);
2044  UtRegisterTest("ThreadingAffinityTest22", ThreadingAffinityTest22);
2045  UtRegisterTest("ThreadingAffinityTest23", ThreadingAffinityTest23);
2046  UtRegisterTest("ThreadingAffinityTest24", ThreadingAffinityTest24);
2047  UtRegisterTest("ThreadingAffinityTest25", ThreadingAffinityTest25);
2048  UtRegisterTest("ThreadingAffinityTest26", ThreadingAffinityTest26);
2049  UtRegisterTest("ThreadingAffinityTest27", ThreadingAffinityTest27);
2050 #endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) */
2051 }
2052 
2053 #endif /* UNITTESTS */
AffinitySetupLoadFromConfig
void AffinitySetupLoadFromConfig(void)
Extract CPU affinity configuration from current config file.
Definition: util-affinity.c:588
ThreadsAffinityType_::medprio_cpu
cpu_set_t medprio_cpu
Definition: util-affinity.h:81
util-byte.h
SCConfYamlLoadString
int SCConfYamlLoadString(const char *string, size_t len)
Load configuration from a YAML string.
Definition: conf-yaml-loader.c:523
ThreadsAffinityType_::nb_threads
uint32_t nb_threads
Definition: util-affinity.h:85
ThreadsAffinityType_::lcpu
uint16_t lcpu[MAX_NUMA_NODES]
Definition: util-affinity.h:88
ThreadVars_::iface_name
char * iface_name
Definition: threadvars.h:139
MAX_NUMA_NODES
#define MAX_NUMA_NODES
Definition: util-affinity.h:70
GetAffinityTypeForNameAndIface
ThreadsAffinityType * GetAffinityTypeForNameAndIface(const char *name, const char *interface_name)
Find affinity by name (*-cpu-set name) and an interface name.
Definition: util-affinity.c:138
SC_LOG_DEBUG
@ SC_LOG_DEBUG
Definition: util-debug.h:43
AffinityGetYamlPath
char * AffinityGetYamlPath(ThreadsAffinityType *taf)
Get the YAML path for the given affinity type. The path is built using the parent name (if available)...
Definition: util-affinity.c:429
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:279
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
ThreadsAffinityType_::lowprio_cpu
cpu_set_t lowprio_cpu
Definition: util-affinity.h:80
UtilAffinityGetAffinedCPUNum
uint16_t UtilAffinityGetAffinedCPUNum(ThreadsAffinityType *taf)
Return the total number of CPUs in a given affinity.
Definition: util-affinity.c:1043
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
SCConfGetBool
int SCConfGetBool(const char *name, int *val)
Retrieve a configuration value as a boolean.
Definition: conf.c:497
ThreadsAffinityType_::nb_children
uint32_t nb_children
Definition: util-affinity.h:86
PRIO_LOW
@ PRIO_LOW
Definition: threads.h:88
WORKER_CPU_SET
@ WORKER_CPU_SET
Definition: util-affinity.h:58
util-unittest.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
DPDKDeviceNameSetSocketID
int32_t DPDKDeviceNameSetSocketID(char *iface_name, int32_t *socket_id)
Definition: util-dpdk.c:99
SCConfInit
void SCConfInit(void)
Initialize the configuration system.
Definition: conf.c:120
MANAGEMENT_CPU_SET
@ MANAGEMENT_CPU_SET
Definition: util-affinity.h:60
RunmodeIsWorkers
bool RunmodeIsWorkers(void)
Definition: runmodes.c:204
SCRunmodeGet
SCRunMode SCRunmodeGet(void)
Get the current run mode.
Definition: suricata.c:279
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
RunmodeIsAutofp
bool RunmodeIsAutofp(void)
Definition: runmodes.c:209
util-cpu.h
ThreadsAffinityType_::hiprio_cpu
cpu_set_t hiprio_cpu
Definition: util-affinity.h:82
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:120
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
util-affinity.h
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:316
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:259
ThreadVars_::type
uint8_t type
Definition: threadvars.h:72
conf-yaml-loader.h
conf.h
ThreadsAffinityType_::cpu_set
cpu_set_t cpu_set
Definition: util-affinity.h:79
ThreadsAffinityType_::mode_flag
uint8_t mode_flag
Definition: util-affinity.h:89
ThreadsAffinityType_::parent
struct ThreadsAffinityType_ * parent
Definition: util-affinity.h:75
name
const char * name
Definition: tm-threads.c:2163
SCConfCreateContextBackup
void SCConfCreateContextBackup(void)
Creates a backup of the conf_hash hash_table used by the conf API.
Definition: conf.c:684
runmodes.h
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:229
AffinityGetNextCPU
uint16_t AffinityGetNextCPU(ThreadVars *tv, ThreadsAffinityType *taf)
Definition: util-affinity.c:1006
MAX_CPU_SET
@ MAX_CPU_SET
Definition: util-affinity.h:61
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
SCRealloc
#define SCRealloc(ptr, sz)
Definition: util-mem.h:50
SCConfNodeLookupChild
SCConfNode * SCConfNodeLookupChild(const SCConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:796
util-dpdk.h
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
ThreadsAffinityType_::name
const char * name
Definition: util-affinity.h:73
GetOrAllocAffinityTypeForIfaceOfName
ThreadsAffinityType * GetOrAllocAffinityTypeForIfaceOfName(const char *name, const char *interface_name)
Finds affinity by its name and interface name. Interfaces are children of cpu-set names....
Definition: util-affinity.c:177
suricata-common.h
thread_affinity
ThreadsAffinityType thread_affinity[MAX_CPU_SET]
Definition: util-affinity.c:38
SCLogPerf
#define SCLogPerf(...)
Definition: util-debug.h:238
ThreadsAffinityType_::children
struct ThreadsAffinityType_ ** children
Definition: util-affinity.h:74
SCConfDeInit
void SCConfDeInit(void)
De-initializes the configuration system.
Definition: conf.c:703
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
FatalError
#define FatalError(...)
Definition: util-debug.h:514
RECEIVE_CPU_SET
@ RECEIVE_CPU_SET
Definition: util-affinity.h:57
TVT_PPT
@ TVT_PPT
Definition: tm-threads-common.h:88
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
ThreadsAffinityType_
Definition: util-affinity.h:72
SCLogConfig
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
BALANCED_AFFINITY
@ BALANCED_AFFINITY
Definition: util-affinity.h:65
cond
SCCondT cond
Definition: tmqh-packetpool.h:2
SCConfGetNode
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition: conf.c:181
BuildCpusetWithCallback
int BuildCpusetWithCallback(const char *name, SCConfNode *node, void(*Callback)(int i, void *data), void *data)
Definition: util-affinity.c:227
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:271
SCFree
#define SCFree(p)
Definition: util-mem.h:61
TopologyDestroy
void TopologyDestroy(void)
RUNMODE_DPDK
@ RUNMODE_DPDK
Definition: runmodes.h:39
PRIO_MEDIUM
@ PRIO_MEDIUM
Definition: threads.h:89
SCConfRestoreContextBackup
void SCConfRestoreContextBackup(void)
Restores the backup of the hash_table present in backup_conf_hash back to conf_hash.
Definition: conf.c:694
ThreadingAffinityRegisterTests
void ThreadingAffinityRegisterTests(void)
Register all threading affinity unit tests.
Definition: util-affinity.c:2020
VERDICT_CPU_SET
@ VERDICT_CPU_SET
Definition: util-affinity.h:59
suricata.h
SCConfNode_::name
char * name
Definition: conf.h:38
thread_affinity_init_done
int thread_affinity_init_done
Definition: util-affinity.c:66
ThreadsAffinityType_::nb_children_capacity
uint32_t nb_children_capacity
Definition: util-affinity.h:87
ThreadsAffinityType_::taf_mutex
SCMutex taf_mutex
Definition: util-affinity.h:76
ThreadsAffinityType_::prio
int prio
Definition: util-affinity.h:84
FindAffinityByInterface
ThreadsAffinityType * FindAffinityByInterface(ThreadsAffinityType *parent, const char *interface_name)
Definition: util-affinity.c:113
UtilCpuGetNumProcessorsOnline
uint16_t UtilCpuGetNumProcessorsOnline(void)
Get the number of cpus online in the system.
Definition: util-cpu.c:108
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCConfNode_
Definition: conf.h:37
SCLogGetLogLevel
SCLogLevel SCLogGetLogLevel(void)
Definition: util-debug.c:1070
SCConfNode_::val
char * val
Definition: conf.h:39
EXCLUSIVE_AFFINITY
@ EXCLUSIVE_AFFINITY
Definition: util-affinity.h:66
PRIO_HIGH
@ PRIO_HIGH
Definition: threads.h:90