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 #define _THREAD_AFFINITY
27 #include "util-affinity.h"
28 #include "conf.h"
29 #include "runmodes.h"
30 #include "util-cpu.h"
31 #include "util-byte.h"
32 #include "util-debug.h"
33 
35  {
36  .name = "receive-cpu-set",
37  .mode_flag = EXCLUSIVE_AFFINITY,
38  .prio = PRIO_MEDIUM,
39  .lcpu = 0,
40  },
41  {
42  .name = "worker-cpu-set",
43  .mode_flag = EXCLUSIVE_AFFINITY,
44  .prio = PRIO_MEDIUM,
45  .lcpu = 0,
46  },
47  {
48  .name = "verdict-cpu-set",
49  .mode_flag = BALANCED_AFFINITY,
50  .prio = PRIO_MEDIUM,
51  .lcpu = 0,
52  },
53  {
54  .name = "management-cpu-set",
55  .mode_flag = BALANCED_AFFINITY,
56  .prio = PRIO_MEDIUM,
57  .lcpu = 0,
58  },
59 
60 };
61 
63 
64 /**
65  * \brief find affinity by its name
66  * \retval a pointer to the affinity or NULL if not found
67  */
69 {
70  int i;
71  for (i = 0; i < MAX_CPU_SET; i++) {
72  if (!strcmp(thread_affinity[i].name, name)) {
73  return &thread_affinity[i];
74  }
75  }
76  return NULL;
77 }
78 
79 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
80 static void AffinitySetupInit(void)
81 {
82  int i, j;
84 
85  SCLogDebug("Initialize affinity setup\n");
86  /* be conservative relatively to OS: use all cpus by default */
87  for (i = 0; i < MAX_CPU_SET; i++) {
88  cpu_set_t *cs = &thread_affinity[i].cpu_set;
89  CPU_ZERO(cs);
90  for (j = 0; j < ncpu; j++) {
91  CPU_SET(j, cs);
92  }
93  SCMutexInit(&thread_affinity[i].taf_mutex, NULL);
94  }
95 }
96 
97 void BuildCpusetWithCallback(const char *name, ConfNode *node,
98  void (*Callback)(int i, void * data),
99  void *data)
100 {
101  ConfNode *lnode;
102  TAILQ_FOREACH(lnode, &node->head, next) {
103  int i;
104  long int a,b;
105  int stop = 0;
106  int max = UtilCpuGetNumProcessorsOnline() - 1;
107  if (!strcmp(lnode->val, "all")) {
108  a = 0;
109  b = max;
110  stop = 1;
111  } else if (strchr(lnode->val, '-') != NULL) {
112  char *sep = strchr(lnode->val, '-');
113  char *end;
114  a = strtoul(lnode->val, &end, 10);
115  if (end != sep) {
116  SCLogError("%s: invalid cpu range (start invalid): \"%s\"", name, lnode->val);
117  exit(EXIT_FAILURE);
118  }
119  b = strtol(sep + 1, &end, 10);
120  if (end != sep + strlen(sep)) {
121  SCLogError("%s: invalid cpu range (end invalid): \"%s\"", name, lnode->val);
122  exit(EXIT_FAILURE);
123  }
124  if (a > b) {
125  SCLogError("%s: invalid cpu range (bad order): \"%s\"", name, lnode->val);
126  exit(EXIT_FAILURE);
127  }
128  if (b > max) {
129  SCLogError("%s: upper bound (%ld) of cpu set is too high, only %d cpu(s)", name, b,
130  max + 1);
131  }
132  } else {
133  char *end;
134  a = strtoul(lnode->val, &end, 10);
135  if (end != lnode->val + strlen(lnode->val)) {
136  SCLogError("%s: invalid cpu range (not an integer): \"%s\"", name, lnode->val);
137  exit(EXIT_FAILURE);
138  }
139  b = a;
140  }
141  for (i = a; i<= b; i++) {
142  Callback(i, data);
143  }
144  if (stop)
145  break;
146  }
147 }
148 
149 static void AffinityCallback(int i, void *data)
150 {
151  CPU_SET(i, (cpu_set_t *)data);
152 }
153 
154 static void BuildCpuset(const char *name, ConfNode *node, cpu_set_t *cpu)
155 {
156  BuildCpusetWithCallback(name, node, AffinityCallback, (void *) cpu);
157 }
158 #endif /* OS_WIN32 and __OpenBSD__ */
159 
160 /**
161  * \brief Extract cpu affinity configuration from current config file
162  */
163 
165 {
166 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
167  ConfNode *root = ConfGetNode("threading.cpu-affinity");
168  ConfNode *affinity;
169 
170  if (thread_affinity_init_done == 0) {
171  AffinitySetupInit();
173  }
174 
175  SCLogDebug("Load affinity from config\n");
176  if (root == NULL) {
177  SCLogInfo("can't get cpu-affinity node");
178  return;
179  }
180 
181  TAILQ_FOREACH(affinity, &root->head, next) {
182  if (strcmp(affinity->val, "decode-cpu-set") == 0 ||
183  strcmp(affinity->val, "stream-cpu-set") == 0 ||
184  strcmp(affinity->val, "reject-cpu-set") == 0 ||
185  strcmp(affinity->val, "output-cpu-set") == 0) {
186  continue;
187  }
188 
189  const char *setname = affinity->val;
190  if (strcmp(affinity->val, "detect-cpu-set") == 0)
191  setname = "worker-cpu-set";
192 
194  ConfNode *node = NULL;
195  ConfNode *nprio = NULL;
196 
197  if (taf == NULL) {
198  FatalError("unknown cpu-affinity type");
199  } else {
200  SCLogConfig("Found affinity definition for \"%s\"", setname);
201  }
202 
203  CPU_ZERO(&taf->cpu_set);
204  node = ConfNodeLookupChild(affinity->head.tqh_first, "cpu");
205  if (node == NULL) {
206  SCLogInfo("unable to find 'cpu'");
207  } else {
208  BuildCpuset(setname, node, &taf->cpu_set);
209  }
210 
211  CPU_ZERO(&taf->lowprio_cpu);
212  CPU_ZERO(&taf->medprio_cpu);
213  CPU_ZERO(&taf->hiprio_cpu);
214  nprio = ConfNodeLookupChild(affinity->head.tqh_first, "prio");
215  if (nprio != NULL) {
216  node = ConfNodeLookupChild(nprio, "low");
217  if (node == NULL) {
218  SCLogDebug("unable to find 'low' prio using default value");
219  } else {
220  BuildCpuset(setname, node, &taf->lowprio_cpu);
221  }
222 
223  node = ConfNodeLookupChild(nprio, "medium");
224  if (node == NULL) {
225  SCLogDebug("unable to find 'medium' prio using default value");
226  } else {
227  BuildCpuset(setname, node, &taf->medprio_cpu);
228  }
229 
230  node = ConfNodeLookupChild(nprio, "high");
231  if (node == NULL) {
232  SCLogDebug("unable to find 'high' prio using default value");
233  } else {
234  BuildCpuset(setname, node, &taf->hiprio_cpu);
235  }
236  node = ConfNodeLookupChild(nprio, "default");
237  if (node != NULL) {
238  if (!strcmp(node->val, "low")) {
239  taf->prio = PRIO_LOW;
240  } else if (!strcmp(node->val, "medium")) {
241  taf->prio = PRIO_MEDIUM;
242  } else if (!strcmp(node->val, "high")) {
243  taf->prio = PRIO_HIGH;
244  } else {
245  FatalError("unknown cpu_affinity prio");
246  }
247  SCLogConfig("Using default prio '%s' for set '%s'",
248  node->val, setname);
249  }
250  }
251 
252  node = ConfNodeLookupChild(affinity->head.tqh_first, "mode");
253  if (node != NULL) {
254  if (!strcmp(node->val, "exclusive")) {
256  } else if (!strcmp(node->val, "balanced")) {
258  } else {
259  FatalError("unknown cpu_affinity node");
260  }
261  }
262 
263  node = ConfNodeLookupChild(affinity->head.tqh_first, "threads");
264  if (node != NULL) {
265  if (StringParseUint32(&taf->nb_threads, 10, 0, (const char *)node->val) < 0) {
266  FatalError("invalid value for threads "
267  "count: '%s'",
268  node->val);
269  }
270  if (! taf->nb_threads) {
271  FatalError("bad value for threads count");
272  }
273  }
274  }
275 #endif /* OS_WIN32 and __OpenBSD__ */
276 }
277 
278 /**
279  * \brief Return next cpu to use for a given thread family
280  * \retval the cpu to used given by its id
281  */
283 {
284  uint16_t ncpu = 0;
285 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
286  int iter = 0;
287  SCMutexLock(&taf->taf_mutex);
288  ncpu = taf->lcpu;
289  while (!CPU_ISSET(ncpu, &taf->cpu_set) && iter < 2) {
290  ncpu++;
291  if (ncpu >= UtilCpuGetNumProcessorsOnline()) {
292  ncpu = 0;
293  iter++;
294  }
295  }
296  if (iter == 2) {
297  SCLogError("cpu_set does not contain "
298  "available cpus, cpu affinity conf is invalid");
299  }
300  taf->lcpu = ncpu + 1;
301  if (taf->lcpu >= UtilCpuGetNumProcessorsOnline())
302  taf->lcpu = 0;
303  SCMutexUnlock(&taf->taf_mutex);
304  SCLogDebug("Setting affinity on CPU %d", ncpu);
305 #endif /* OS_WIN32 and __OpenBSD__ */
306  return ncpu;
307 }
308 
310 {
311  uint16_t ncpu = 0;
312 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
313  SCMutexLock(&taf->taf_mutex);
314  for (int i = UtilCpuGetNumProcessorsOnline(); i >= 0; i--)
315  if (CPU_ISSET(i, &taf->cpu_set))
316  ncpu++;
317  SCMutexUnlock(&taf->taf_mutex);
318 #endif
319  return ncpu;
320 }
321 
322 #ifdef HAVE_DPDK
323 /**
324  * Find if CPU sets overlap
325  * \return 1 if CPUs overlap, 0 otherwise
326  */
327 uint16_t UtilAffinityCpusOverlap(ThreadsAffinityType *taf1, ThreadsAffinityType *taf2)
328 {
329  ThreadsAffinityType tmptaf;
330  CPU_ZERO(&tmptaf);
331  SCMutexInit(&tmptaf.taf_mutex, NULL);
332 
333  cpu_set_t tmpcset;
334 
335  SCMutexLock(&taf1->taf_mutex);
336  SCMutexLock(&taf2->taf_mutex);
337  CPU_AND(&tmpcset, &taf1->cpu_set, &taf2->cpu_set);
338  SCMutexUnlock(&taf2->taf_mutex);
339  SCMutexUnlock(&taf1->taf_mutex);
340 
341  for (int i = UtilCpuGetNumProcessorsOnline(); i >= 0; i--)
342  if (CPU_ISSET(i, &tmpcset))
343  return 1;
344  return 0;
345 }
346 
347 /**
348  * Function makes sure that CPUs of different types don't overlap by excluding
349  * one affinity type from the other
350  * \param mod_taf - CPU set to be modified
351  * \param static_taf - static CPU set to be used only for evaluation
352  */
353 void UtilAffinityCpusExclude(ThreadsAffinityType *mod_taf, ThreadsAffinityType *static_taf)
354 {
355  cpu_set_t tmpset;
356  SCMutexLock(&mod_taf->taf_mutex);
357  SCMutexLock(&static_taf->taf_mutex);
358  CPU_XOR(&tmpset, &mod_taf->cpu_set, &static_taf->cpu_set);
359  SCMutexUnlock(&static_taf->taf_mutex);
360  mod_taf->cpu_set = tmpset;
361  SCMutexUnlock(&mod_taf->taf_mutex);
362 }
363 #endif /* HAVE_DPDK */
AffinitySetupLoadFromConfig
void AffinitySetupLoadFromConfig(void)
Extract cpu affinity configuration from current config file.
Definition: util-affinity.c:164
ThreadsAffinityType_::medprio_cpu
cpu_set_t medprio_cpu
Definition: util-affinity.h:76
util-byte.h
ThreadsAffinityType_::nb_threads
uint32_t nb_threads
Definition: util-affinity.h:70
ThreadsAffinityType_::lcpu
uint16_t lcpu
Definition: util-affinity.h:68
EXCLUSIVE_AFFINITY
@ EXCLUSIVE_AFFINITY
Definition: util-affinity.h:61
ConfNode_::val
char * val
Definition: conf.h:34
MAX_CPU_SET
@ MAX_CPU_SET
Definition: util-affinity.h:56
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
ThreadsAffinityType_::lowprio_cpu
cpu_set_t lowprio_cpu
Definition: util-affinity.h:75
ConfGetNode
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:181
UtilAffinityGetAffinedCPUNum
uint16_t UtilAffinityGetAffinedCPUNum(ThreadsAffinityType *taf)
Definition: util-affinity.c:309
BuildCpusetWithCallback
void BuildCpusetWithCallback(const char *name, ConfNode *node, void(*Callback)(int i, void *data), void *data)
Definition: util-affinity.c:97
UtilCpuGetNumProcessorsConfigured
uint16_t UtilCpuGetNumProcessorsConfigured(void)
Get the number of cpus configured in the system.
Definition: util-cpu.c:59
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
PRIO_LOW
@ PRIO_LOW
Definition: threads.h:88
PRIO_MEDIUM
@ PRIO_MEDIUM
Definition: threads.h:89
BALANCED_AFFINITY
@ BALANCED_AFFINITY
Definition: util-affinity.h:60
AffinityGetNextCPU
uint16_t AffinityGetNextCPU(ThreadsAffinityType *taf)
Return next cpu to use for a given thread family.
Definition: util-affinity.c:282
util-debug.h
util-cpu.h
ThreadsAffinityType_::hiprio_cpu
cpu_set_t hiprio_cpu
Definition: util-affinity.h:77
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
util-affinity.h
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:313
conf.h
ThreadsAffinityType_::cpu_set
cpu_set_t cpu_set
Definition: util-affinity.h:74
ThreadsAffinityType_::mode_flag
uint8_t mode_flag
Definition: util-affinity.h:67
runmodes.h
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:224
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
PRIO_HIGH
@ PRIO_HIGH
Definition: threads.h:90
ConfNodeLookupChild
ConfNode * ConfNodeLookupChild(const ConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:781
ThreadsAffinityType_::name
const char * name
Definition: util-affinity.h:66
suricata-common.h
thread_affinity
ThreadsAffinityType thread_affinity[MAX_CPU_SET]
Definition: util-affinity.c:34
FatalError
#define FatalError(...)
Definition: util-debug.h:502
ThreadsAffinityType_
Definition: util-affinity.h:65
SCLogConfig
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
ConfNode_
Definition: conf.h:32
thread_affinity_init_done
int thread_affinity_init_done
Definition: util-affinity.c:62
ThreadsAffinityType_::taf_mutex
SCMutex taf_mutex
Definition: util-affinity.h:71
GetAffinityTypeFromName
ThreadsAffinityType * GetAffinityTypeFromName(const char *name)
find affinity by its name
Definition: util-affinity.c:68
ThreadsAffinityType_::prio
int prio
Definition: util-affinity.h:69
UtilCpuGetNumProcessorsOnline
uint16_t UtilCpuGetNumProcessorsOnline(void)
Get the number of cpus online in the system.
Definition: util-cpu.c:108