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 "util-cpu.h"
29 #include "conf.h"
30 #include "threads.h"
31 #include "queue.h"
32 #include "runmodes.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) {
118  "%s: invalid cpu range (start invalid): \"%s\"",
119  name,
120  lnode->val);
121  exit(EXIT_FAILURE);
122  }
123  b = strtol(sep + 1, &end, 10);
124  if (end != sep + strlen(sep)) {
126  "%s: invalid cpu range (end invalid): \"%s\"",
127  name,
128  lnode->val);
129  exit(EXIT_FAILURE);
130  }
131  if (a > b) {
133  "%s: invalid cpu range (bad order): \"%s\"",
134  name,
135  lnode->val);
136  exit(EXIT_FAILURE);
137  }
138  if (b > max) {
140  "%s: upper bound (%ld) of cpu set is too high, only %d cpu(s)",
141  name,
142  b, max + 1);
143  }
144  } else {
145  char *end;
146  a = strtoul(lnode->val, &end, 10);
147  if (end != lnode->val + strlen(lnode->val)) {
149  "%s: invalid cpu range (not an integer): \"%s\"",
150  name,
151  lnode->val);
152  exit(EXIT_FAILURE);
153  }
154  b = a;
155  }
156  for (i = a; i<= b; i++) {
157  Callback(i, data);
158  }
159  if (stop)
160  break;
161  }
162 }
163 
164 static void AffinityCallback(int i, void *data)
165 {
166  CPU_SET(i, (cpu_set_t *)data);
167 }
168 
169 static void BuildCpuset(const char *name, ConfNode *node, cpu_set_t *cpu)
170 {
171  BuildCpusetWithCallback(name, node, AffinityCallback, (void *) cpu);
172 }
173 #endif /* OS_WIN32 and __OpenBSD__ */
174 
175 /**
176  * \brief Extract cpu affinity configuration from current config file
177  */
178 
180 {
181 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
182  ConfNode *root = ConfGetNode("threading.cpu-affinity");
183  ConfNode *affinity;
184 
185  if (thread_affinity_init_done == 0) {
186  AffinitySetupInit();
188  }
189 
190  SCLogDebug("Load affinity from config\n");
191  if (root == NULL) {
192  SCLogInfo("can't get cpu-affinity node");
193  return;
194  }
195 
196  TAILQ_FOREACH(affinity, &root->head, next) {
197  if (strcmp(affinity->val, "decode-cpu-set") == 0 ||
198  strcmp(affinity->val, "stream-cpu-set") == 0 ||
199  strcmp(affinity->val, "reject-cpu-set") == 0 ||
200  strcmp(affinity->val, "output-cpu-set") == 0) {
201  continue;
202  }
203 
204  const char *setname = affinity->val;
205  if (strcmp(affinity->val, "detect-cpu-set") == 0)
206  setname = "worker-cpu-set";
207 
209  ConfNode *node = NULL;
210  ConfNode *nprio = NULL;
211 
212  if (taf == NULL) {
213  SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown cpu-affinity type");
214  exit(EXIT_FAILURE);
215  } else {
216  SCLogConfig("Found affinity definition for \"%s\"", setname);
217  }
218 
219  CPU_ZERO(&taf->cpu_set);
220  node = ConfNodeLookupChild(affinity->head.tqh_first, "cpu");
221  if (node == NULL) {
222  SCLogInfo("unable to find 'cpu'");
223  } else {
224  BuildCpuset(setname, node, &taf->cpu_set);
225  }
226 
227  CPU_ZERO(&taf->lowprio_cpu);
228  CPU_ZERO(&taf->medprio_cpu);
229  CPU_ZERO(&taf->hiprio_cpu);
230  nprio = ConfNodeLookupChild(affinity->head.tqh_first, "prio");
231  if (nprio != NULL) {
232  node = ConfNodeLookupChild(nprio, "low");
233  if (node == NULL) {
234  SCLogDebug("unable to find 'low' prio using default value");
235  } else {
236  BuildCpuset(setname, node, &taf->lowprio_cpu);
237  }
238 
239  node = ConfNodeLookupChild(nprio, "medium");
240  if (node == NULL) {
241  SCLogDebug("unable to find 'medium' prio using default value");
242  } else {
243  BuildCpuset(setname, node, &taf->medprio_cpu);
244  }
245 
246  node = ConfNodeLookupChild(nprio, "high");
247  if (node == NULL) {
248  SCLogDebug("unable to find 'high' prio using default value");
249  } else {
250  BuildCpuset(setname, node, &taf->hiprio_cpu);
251  }
252  node = ConfNodeLookupChild(nprio, "default");
253  if (node != NULL) {
254  if (!strcmp(node->val, "low")) {
255  taf->prio = PRIO_LOW;
256  } else if (!strcmp(node->val, "medium")) {
257  taf->prio = PRIO_MEDIUM;
258  } else if (!strcmp(node->val, "high")) {
259  taf->prio = PRIO_HIGH;
260  } else {
261  SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown cpu_affinity prio");
262  exit(EXIT_FAILURE);
263  }
264  SCLogConfig("Using default prio '%s' for set '%s'",
265  node->val, setname);
266  }
267  }
268 
269  node = ConfNodeLookupChild(affinity->head.tqh_first, "mode");
270  if (node != NULL) {
271  if (!strcmp(node->val, "exclusive")) {
273  } else if (!strcmp(node->val, "balanced")) {
275  } else {
276  SCLogError(SC_ERR_INVALID_ARGUMENT, "unknown cpu_affinity node");
277  exit(EXIT_FAILURE);
278  }
279  }
280 
281  node = ConfNodeLookupChild(affinity->head.tqh_first, "threads");
282  if (node != NULL) {
283  taf->nb_threads = atoi(node->val);
284  if (! taf->nb_threads) {
285  SCLogError(SC_ERR_INVALID_ARGUMENT, "bad value for threads count");
286  exit(EXIT_FAILURE);
287  }
288  }
289  }
290 #endif /* OS_WIN32 and __OpenBSD__ */
291 }
292 
293 /**
294  * \brief Return next cpu to use for a given thread family
295  * \retval the cpu to used given by its id
296  */
298 {
299  int ncpu = 0;
300 #if !defined __CYGWIN__ && !defined OS_WIN32 && !defined __OpenBSD__ && !defined sun
301  int iter = 0;
302  SCMutexLock(&taf->taf_mutex);
303  ncpu = taf->lcpu;
304  while (!CPU_ISSET(ncpu, &taf->cpu_set) && iter < 2) {
305  ncpu++;
306  if (ncpu >= UtilCpuGetNumProcessorsOnline()) {
307  ncpu = 0;
308  iter++;
309  }
310  }
311  if (iter == 2) {
312  SCLogError(SC_ERR_INVALID_ARGUMENT, "cpu_set does not contain "
313  "available cpus, cpu affinity conf is invalid");
314  }
315  taf->lcpu = ncpu + 1;
316  if (taf->lcpu >= UtilCpuGetNumProcessorsOnline())
317  taf->lcpu = 0;
318  SCMutexUnlock(&taf->taf_mutex);
319  SCLogDebug("Setting affinity on CPU %d", ncpu);
320 #endif /* OS_WIN32 and __OpenBSD__ */
321  return ncpu;
322 }
#define SCLogDebug(...)
Definition: util-debug.h:335
int AffinityGetNextCPU(ThreadsAffinityType *taf)
Return next cpu to use for a given thread family.
uint16_t UtilCpuGetNumProcessorsConfigured(void)
Get the number of cpus configured in the system.
Definition: util-cpu.c:58
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct HtpBodyChunk_ * next
char * val
Definition: conf.h:34
ConfNode * ConfNodeLookupChild(const ConfNode *node, const char *name)
Lookup a child configuration node by name.
Definition: conf.c:815
#define SCMutexLock(mut)
void BuildCpusetWithCallback(const char *name, ConfNode *node, void(*Callback)(int i, void *data), void *data)
Definition: util-affinity.c:98
#define SCMutexUnlock(mut)
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define SCMutexInit(mut, mutattrs)
void AffinitySetupLoadFromConfig()
Extract cpu affinity configuration from current config file.
ThreadsAffinityType thread_affinity[MAX_CPU_SET]
Definition: util-affinity.c:34
Definition: conf.h:32
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
int thread_affinity_init_done
Definition: util-affinity.c:62
ThreadsAffinityType * GetAffinityTypeFromName(const char *name)
find affinity by its name
Definition: util-affinity.c:68
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:176
uint16_t UtilCpuGetNumProcessorsOnline(void)
Get the number of cpus online in the system.
Definition: util-cpu.c:99
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.