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