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