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