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  return;
96 }
97 
98 void BuildCpusetWithCallback(const char *name, ConfNode *node,
99  void (*Callback)(int i, void * data),
100  void *data)
101 {
102  ConfNode *lnode;
103  TAILQ_FOREACH(lnode, &node->head, next) {
104  int i;
105  long int a,b;
106  int stop = 0;
107  int max = UtilCpuGetNumProcessorsOnline() - 1;
108  if (!strcmp(lnode->val, "all")) {
109  a = 0;
110  b = max;
111  stop = 1;
112  } else if (strchr(lnode->val, '-') != NULL) {
113  char *sep = strchr(lnode->val, '-');
114  char *end;
115  a = strtoul(lnode->val, &end, 10);
116  if (end != sep) {
117  SCLogError("%s: invalid cpu range (start invalid): \"%s\"", name, lnode->val);
118  exit(EXIT_FAILURE);
119  }
120  b = strtol(sep + 1, &end, 10);
121  if (end != sep + strlen(sep)) {
122  SCLogError("%s: invalid cpu range (end invalid): \"%s\"", name, lnode->val);
123  exit(EXIT_FAILURE);
124  }
125  if (a > b) {
126  SCLogError("%s: invalid cpu range (bad order): \"%s\"", name, lnode->val);
127  exit(EXIT_FAILURE);
128  }
129  if (b > max) {
130  SCLogError("%s: upper bound (%ld) of cpu set is too high, only %d cpu(s)", name, b,
131  max + 1);
132  }
133  } else {
134  char *end;
135  a = strtoul(lnode->val, &end, 10);
136  if (end != lnode->val + strlen(lnode->val)) {
137  SCLogError("%s: invalid cpu range (not an integer): \"%s\"", name, lnode->val);
138  exit(EXIT_FAILURE);
139  }
140  b = a;
141  }
142  for (i = a; i<= b; i++) {
143  Callback(i, data);
144  }
145  if (stop)
146  break;
147  }
148 }
149 
150 static void AffinityCallback(int i, void *data)
151 {
152  CPU_SET(i, (cpu_set_t *)data);
153 }
154 
155 static void BuildCpuset(const char *name, ConfNode *node, cpu_set_t *cpu)
156 {
157  BuildCpusetWithCallback(name, node, AffinityCallback, (void *) cpu);
158 }
159 #endif /* OS_WIN32 and __OpenBSD__ */
160 
161 /**
162  * \brief Extract cpu affinity configuration from current config file
163  */
164 
166 {
167 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
168  ConfNode *root = ConfGetNode("threading.cpu-affinity");
169  ConfNode *affinity;
170 
171  if (thread_affinity_init_done == 0) {
172  AffinitySetupInit();
174  }
175 
176  SCLogDebug("Load affinity from config\n");
177  if (root == NULL) {
178  SCLogInfo("can't get cpu-affinity node");
179  return;
180  }
181 
182  TAILQ_FOREACH(affinity, &root->head, next) {
183  if (strcmp(affinity->val, "decode-cpu-set") == 0 ||
184  strcmp(affinity->val, "stream-cpu-set") == 0 ||
185  strcmp(affinity->val, "reject-cpu-set") == 0 ||
186  strcmp(affinity->val, "output-cpu-set") == 0) {
187  continue;
188  }
189 
190  const char *setname = affinity->val;
191  if (strcmp(affinity->val, "detect-cpu-set") == 0)
192  setname = "worker-cpu-set";
193 
195  ConfNode *node = NULL;
196  ConfNode *nprio = NULL;
197 
198  if (taf == NULL) {
199  FatalError("unknown cpu-affinity type");
200  } else {
201  SCLogConfig("Found affinity definition for \"%s\"", setname);
202  }
203 
204  CPU_ZERO(&taf->cpu_set);
205  node = ConfNodeLookupChild(affinity->head.tqh_first, "cpu");
206  if (node == NULL) {
207  SCLogInfo("unable to find 'cpu'");
208  } else {
209  BuildCpuset(setname, node, &taf->cpu_set);
210  }
211 
212  CPU_ZERO(&taf->lowprio_cpu);
213  CPU_ZERO(&taf->medprio_cpu);
214  CPU_ZERO(&taf->hiprio_cpu);
215  nprio = ConfNodeLookupChild(affinity->head.tqh_first, "prio");
216  if (nprio != NULL) {
217  node = ConfNodeLookupChild(nprio, "low");
218  if (node == NULL) {
219  SCLogDebug("unable to find 'low' prio using default value");
220  } else {
221  BuildCpuset(setname, node, &taf->lowprio_cpu);
222  }
223 
224  node = ConfNodeLookupChild(nprio, "medium");
225  if (node == NULL) {
226  SCLogDebug("unable to find 'medium' prio using default value");
227  } else {
228  BuildCpuset(setname, node, &taf->medprio_cpu);
229  }
230 
231  node = ConfNodeLookupChild(nprio, "high");
232  if (node == NULL) {
233  SCLogDebug("unable to find 'high' prio using default value");
234  } else {
235  BuildCpuset(setname, node, &taf->hiprio_cpu);
236  }
237  node = ConfNodeLookupChild(nprio, "default");
238  if (node != NULL) {
239  if (!strcmp(node->val, "low")) {
240  taf->prio = PRIO_LOW;
241  } else if (!strcmp(node->val, "medium")) {
242  taf->prio = PRIO_MEDIUM;
243  } else if (!strcmp(node->val, "high")) {
244  taf->prio = PRIO_HIGH;
245  } else {
246  FatalError("unknown cpu_affinity prio");
247  }
248  SCLogConfig("Using default prio '%s' for set '%s'",
249  node->val, setname);
250  }
251  }
252 
253  node = ConfNodeLookupChild(affinity->head.tqh_first, "mode");
254  if (node != NULL) {
255  if (!strcmp(node->val, "exclusive")) {
257  } else if (!strcmp(node->val, "balanced")) {
259  } else {
260  FatalError("unknown cpu_affinity node");
261  }
262  }
263 
264  node = ConfNodeLookupChild(affinity->head.tqh_first, "threads");
265  if (node != NULL) {
266  if (StringParseUint32(&taf->nb_threads, 10, 0, (const char *)node->val) < 0) {
267  FatalError("invalid value for threads "
268  "count: '%s'",
269  node->val);
270  }
271  if (! taf->nb_threads) {
272  FatalError("bad value for threads count");
273  }
274  }
275  }
276 #endif /* OS_WIN32 and __OpenBSD__ */
277 }
278 
279 /**
280  * \brief Return next cpu to use for a given thread family
281  * \retval the cpu to used given by its id
282  */
284 {
285  uint16_t ncpu = 0;
286 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
287  int iter = 0;
288  SCMutexLock(&taf->taf_mutex);
289  ncpu = taf->lcpu;
290  while (!CPU_ISSET(ncpu, &taf->cpu_set) && iter < 2) {
291  ncpu++;
292  if (ncpu >= UtilCpuGetNumProcessorsOnline()) {
293  ncpu = 0;
294  iter++;
295  }
296  }
297  if (iter == 2) {
298  SCLogError("cpu_set does not contain "
299  "available cpus, cpu affinity conf is invalid");
300  }
301  taf->lcpu = ncpu + 1;
302  if (taf->lcpu >= UtilCpuGetNumProcessorsOnline())
303  taf->lcpu = 0;
304  SCMutexUnlock(&taf->taf_mutex);
305  SCLogDebug("Setting affinity on CPU %d", ncpu);
306 #endif /* OS_WIN32 and __OpenBSD__ */
307  return ncpu;
308 }
309 
311 {
312  uint16_t ncpu = 0;
313 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
314  SCMutexLock(&taf->taf_mutex);
315  for (int i = UtilCpuGetNumProcessorsOnline(); i >= 0; i--)
316  if (CPU_ISSET(i, &taf->cpu_set))
317  ncpu++;
318  SCMutexUnlock(&taf->taf_mutex);
319 #endif
320  return ncpu;
321 }
322 
323 #ifdef HAVE_DPDK
324 /**
325  * Find if CPU sets overlap
326  * \return 1 if CPUs overlap, 0 otherwise
327  */
328 uint16_t UtilAffinityCpusOverlap(ThreadsAffinityType *taf1, ThreadsAffinityType *taf2)
329 {
330  ThreadsAffinityType tmptaf;
331  CPU_ZERO(&tmptaf);
332  SCMutexInit(&tmptaf.taf_mutex, NULL);
333 
334  cpu_set_t tmpcset;
335 
336  SCMutexLock(&taf1->taf_mutex);
337  SCMutexLock(&taf2->taf_mutex);
338  CPU_AND(&tmpcset, &taf1->cpu_set, &taf2->cpu_set);
339  SCMutexUnlock(&taf2->taf_mutex);
340  SCMutexUnlock(&taf1->taf_mutex);
341 
342  for (int i = UtilCpuGetNumProcessorsOnline(); i >= 0; i--)
343  if (CPU_ISSET(i, &tmpcset))
344  return 1;
345  return 0;
346 }
347 
348 /**
349  * Function makes sure that CPUs of different types don't overlap by excluding
350  * one affinity type from the other
351  * \param mod_taf - CPU set to be modified
352  * \param static_taf - static CPU set to be used only for evaluation
353  */
354 void UtilAffinityCpusExclude(ThreadsAffinityType *mod_taf, ThreadsAffinityType *static_taf)
355 {
356  cpu_set_t tmpset;
357  SCMutexLock(&mod_taf->taf_mutex);
358  SCMutexLock(&static_taf->taf_mutex);
359  CPU_XOR(&tmpset, &mod_taf->cpu_set, &static_taf->cpu_set);
360  SCMutexUnlock(&static_taf->taf_mutex);
361  mod_taf->cpu_set = tmpset;
362  SCMutexUnlock(&mod_taf->taf_mutex);
363 }
364 #endif /* HAVE_DPDK */
AffinitySetupLoadFromConfig
void AffinitySetupLoadFromConfig(void)
Extract cpu affinity configuration from current config file.
Definition: util-affinity.c:165
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
ConfNode_::val
char * val
Definition: conf.h:34
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:310
BuildCpusetWithCallback
void BuildCpusetWithCallback(const char *name, ConfNode *node, void(*Callback)(int i, void *data), void *data)
Definition: util-affinity.c:98
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
EXCLUSIVE_AFFINITY
@ EXCLUSIVE_AFFINITY
Definition: util-affinity.h:61
AffinityGetNextCPU
uint16_t AffinityGetNextCPU(ThreadsAffinityType *taf)
Return next cpu to use for a given thread family.
Definition: util-affinity.c:283
util-debug.h
util-cpu.h
ThreadsAffinityType_::hiprio_cpu
cpu_set_t hiprio_cpu
Definition: util-affinity.h:77
BALANCED_AFFINITY
@ BALANCED_AFFINITY
Definition: util-affinity.h:60
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
MAX_CPU_SET
@ MAX_CPU_SET
Definition: util-affinity.h:56
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
ConfNodeLookupChild
ConfNode * ConfNodeLookupChild(const ConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:786
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
PRIO_MEDIUM
@ PRIO_MEDIUM
Definition: threads.h:89
ConfNode_
Definition: conf.h:32
PRIO_LOW
@ PRIO_LOW
Definition: threads.h:88
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
PRIO_HIGH
@ PRIO_HIGH
Definition: threads.h:90