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