Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2014 Nicira, Inc.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at:
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #include <config.h>
18 : : #include "ovs-numa.h"
19 : :
20 : : #include <ctype.h>
21 : : #include <errno.h>
22 : : #ifdef __linux__
23 : : #include <dirent.h>
24 : : #include <stddef.h>
25 : : #include <string.h>
26 : : #include <sys/types.h>
27 : : #include <unistd.h>
28 : : #endif /* __linux__ */
29 : :
30 : : #include "hash.h"
31 : : #include "openvswitch/hmap.h"
32 : : #include "openvswitch/list.h"
33 : : #include "ovs-thread.h"
34 : : #include "openvswitch/vlog.h"
35 : :
36 : 20190 : VLOG_DEFINE_THIS_MODULE(ovs_numa);
37 : :
38 : : /* ovs-numa module
39 : : * ===============
40 : : *
41 : : * This module stores the affinity information of numa nodes and cpu cores.
42 : : * It also provides functions to bookkeep the pin of threads on cpu cores.
43 : : *
44 : : * It is assumed that the numa node ids and cpu core ids all start from 0 and
45 : : * range continuously. So, for example, if 'ovs_numa_get_n_cores()' returns N,
46 : : * user can assume core ids from 0 to N-1 are all valid and there is a
47 : : * 'struct cpu_core' for each id.
48 : : *
49 : : * NOTE, this module should only be used by the main thread.
50 : : *
51 : : * NOTE, the assumption above will fail when cpu hotplug is used. In that
52 : : * case ovs-numa will not function correctly. For now, add a TODO entry
53 : : * for addressing it in the future.
54 : : *
55 : : * TODO: Fix ovs-numa when cpu hotplug is used.
56 : : */
57 : :
58 : : #define MAX_NUMA_NODES 128
59 : :
60 : : /* numa node. */
61 : : struct numa_node {
62 : : struct hmap_node hmap_node; /* In the 'all_numa_nodes'. */
63 : : struct ovs_list cores; /* List of cpu cores on the numa node. */
64 : : int numa_id; /* numa node id. */
65 : : };
66 : :
67 : : /* Cpu core on a numa node. */
68 : : struct cpu_core {
69 : : struct hmap_node hmap_node;/* In the 'all_cpu_cores'. */
70 : : struct ovs_list list_node; /* In 'numa_node->cores' list. */
71 : : struct numa_node *numa; /* numa node containing the core. */
72 : : unsigned core_id; /* Core id. */
73 : : bool available; /* If the core can be pinned. */
74 : : bool pinned; /* If a thread has been pinned to the core. */
75 : : };
76 : :
77 : : /* Contains all 'struct numa_node's. */
78 : : static struct hmap all_numa_nodes = HMAP_INITIALIZER(&all_numa_nodes);
79 : : /* Contains all 'struct cpu_core's. */
80 : : static struct hmap all_cpu_cores = HMAP_INITIALIZER(&all_cpu_cores);
81 : : /* True if numa node and core info are correctly extracted. */
82 : : static bool found_numa_and_core;
83 : : /* True if the module was initialized with dummy options. In this case, the
84 : : * module must not interact with the actual cpus/nodes in the system. */
85 : : static bool dummy_numa = false;
86 : : /* If 'dummy_numa' is true, contains a copy of the dummy numa configuration
87 : : * parameter */
88 : : static char *dummy_config;
89 : :
90 : : static struct numa_node *get_numa_by_numa_id(int numa_id);
91 : :
92 : : #ifdef __linux__
93 : : /* Returns true if 'str' contains all digits. Returns false otherwise. */
94 : : static bool
95 : 2392 : contain_all_digits(const char *str)
96 : : {
97 : 2392 : return str[strspn(str, "0123456789")] == '\0';
98 : : }
99 : : #endif /* __linux__ */
100 : :
101 : : static struct numa_node *
102 : 631 : insert_new_numa_node(int numa_id)
103 : : {
104 : 631 : struct numa_node *n = xzalloc(sizeof *n);
105 : :
106 : 631 : hmap_insert(&all_numa_nodes, &n->hmap_node, hash_int(numa_id, 0));
107 : 631 : ovs_list_init(&n->cores);
108 : 631 : n->numa_id = numa_id;
109 : :
110 : 631 : return n;
111 : : }
112 : :
113 : : static struct cpu_core *
114 : 1318 : insert_new_cpu_core(struct numa_node *n, unsigned core_id)
115 : : {
116 : 1318 : struct cpu_core *c = xzalloc(sizeof *c);
117 : :
118 : 1318 : hmap_insert(&all_cpu_cores, &c->hmap_node, hash_int(core_id, 0));
119 : 1318 : ovs_list_insert(&n->cores, &c->list_node);
120 : 1318 : c->core_id = core_id;
121 : 1318 : c->numa = n;
122 : 1318 : c->available = true;
123 : :
124 : 1318 : return c;
125 : : }
126 : :
127 : : /* Has the same effect as discover_numa_and_core(), but instead of reading
128 : : * sysfs entries, extracts the info from 'dummy_config'.
129 : : *
130 : : * 'dummy_config' lists the numa_ids of each CPU separated by a comma, e.g.
131 : : * - "0,0,0,0": four cores on numa socket 0.
132 : : * - "0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1": 16 cores on two numa sockets.
133 : : * - "0,0,0,0,1,1,1,1": 8 cores on two numa sockets.
134 : : *
135 : : * The different numa ids must be consecutives or the function will abort. */
136 : : static void
137 : 19 : discover_numa_and_core_dummy(const char *dummy_config)
138 : : {
139 : 19 : char *conf = xstrdup(dummy_config);
140 : 19 : char *id, *saveptr = NULL;
141 : 19 : unsigned i = 0;
142 : 19 : long max_numa_id = 0;
143 : :
144 [ + + ]: 141 : for (id = strtok_r(conf, ",", &saveptr); id;
145 : 122 : id = strtok_r(NULL, ",", &saveptr)) {
146 : : struct hmap_node *hnode;
147 : : struct numa_node *n;
148 : : long numa_id;
149 : :
150 : 122 : numa_id = strtol(id, NULL, 10);
151 [ + - ][ - + ]: 122 : if (numa_id < 0 || numa_id >= MAX_NUMA_NODES) {
152 [ # # ]: 0 : VLOG_WARN("Invalid numa node %ld", numa_id);
153 : 0 : continue;
154 : : }
155 : :
156 : 122 : max_numa_id = MAX(max_numa_id, numa_id);
157 : :
158 : 122 : hnode = hmap_first_with_hash(&all_numa_nodes, hash_int(numa_id, 0));
159 : :
160 [ + + ]: 122 : if (hnode) {
161 : 89 : n = CONTAINER_OF(hnode, struct numa_node, hmap_node);
162 : : } else {
163 : 33 : n = insert_new_numa_node(numa_id);
164 : : }
165 : :
166 : 122 : insert_new_cpu_core(n, i);
167 : :
168 : 122 : i++;
169 : : }
170 : :
171 : 19 : free(conf);
172 : :
173 [ - + ]: 19 : if (max_numa_id + 1 != hmap_count(&all_numa_nodes)) {
174 : 0 : ovs_fatal(0, "dummy numa contains non consecutive numa ids");
175 : : }
176 : 19 : }
177 : :
178 : : /* Discovers all numa nodes and the corresponding cpu cores.
179 : : * Constructs the 'struct numa_node' and 'struct cpu_core'. */
180 : : static void
181 : 598 : discover_numa_and_core(void)
182 : : {
183 : : #ifdef __linux__
184 : : int i;
185 : : DIR *dir;
186 : 598 : bool numa_supported = true;
187 : :
188 : : /* Check if NUMA supported on this system. */
189 : 598 : dir = opendir("/sys/devices/system/node");
190 : :
191 [ - + ][ # # ]: 598 : if (!dir && errno == ENOENT) {
192 : 0 : numa_supported = false;
193 : : }
194 [ + - ]: 598 : if (dir) {
195 : 598 : closedir(dir);
196 : : }
197 : :
198 [ + - ]: 1196 : for (i = 0; i < MAX_NUMA_NODES; i++) {
199 : : char* path;
200 : :
201 [ + - ]: 1196 : if (numa_supported) {
202 : : /* Constructs the path to node /sys/devices/system/nodeX. */
203 : 1196 : path = xasprintf("/sys/devices/system/node/node%d", i);
204 : : } else {
205 : 0 : path = xasprintf("/sys/devices/system/cpu/");
206 : : }
207 : :
208 : 1196 : dir = opendir(path);
209 : :
210 : : /* Creates 'struct numa_node' if the 'dir' is non-null. */
211 [ + + ]: 1196 : if (dir) {
212 : : struct numa_node *n;
213 : : struct dirent *subdir;
214 : :
215 : 598 : n = insert_new_numa_node(i);
216 : :
217 [ + + ]: 28704 : while ((subdir = readdir(dir)) != NULL) {
218 [ + + ]: 28106 : if (!strncmp(subdir->d_name, "cpu", 3)
219 [ + + ]: 2392 : && contain_all_digits(subdir->d_name + 3)) {
220 : : unsigned core_id;
221 : :
222 : 1196 : core_id = strtoul(subdir->d_name + 3, NULL, 10);
223 : 1196 : insert_new_cpu_core(n, core_id);
224 : : }
225 : : }
226 : 598 : closedir(dir);
227 [ - + ]: 598 : } else if (errno != ENOENT) {
228 [ # # ]: 0 : VLOG_WARN("opendir(%s) failed (%s)", path,
229 : : ovs_strerror(errno));
230 : : }
231 : :
232 : 1196 : free(path);
233 [ + + ][ + - ]: 1196 : if (!dir || !numa_supported) {
234 : : break;
235 : : }
236 : : }
237 : : #endif /* __linux__ */
238 : 598 : }
239 : :
240 : : /* Gets 'struct cpu_core' by 'core_id'. */
241 : : static struct cpu_core*
242 : 31 : get_core_by_core_id(unsigned core_id)
243 : : {
244 : 31 : struct cpu_core *core = NULL;
245 : :
246 [ + - ]: 31 : if (ovs_numa_core_id_is_valid(core_id)) {
247 : 31 : core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores,
248 : : hash_int(core_id, 0)),
249 : : struct cpu_core, hmap_node);
250 : : }
251 : :
252 : 31 : return core;
253 : : }
254 : :
255 : : /* Gets 'struct numa_node' by 'numa_id'. */
256 : : static struct numa_node*
257 : 87 : get_numa_by_numa_id(int numa_id)
258 : : {
259 : 87 : struct numa_node *numa = NULL;
260 : :
261 [ + - ]: 87 : if (ovs_numa_numa_id_is_valid(numa_id)) {
262 : 87 : numa = CONTAINER_OF(hmap_first_with_hash(&all_numa_nodes,
263 : : hash_int(numa_id, 0)),
264 : : struct numa_node, hmap_node);
265 : : }
266 : :
267 : 87 : return numa;
268 : : }
269 : :
270 : :
271 : :
272 : : static bool
273 : 617 : ovs_numa_init__(const char *dummy_config)
274 : : {
275 : : static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
276 : :
277 [ + - ]: 617 : if (ovsthread_once_start(&once)) {
278 : : const struct numa_node *n;
279 : :
280 [ + + ]: 617 : if (!dummy_config) {
281 : 598 : discover_numa_and_core();
282 : : } else {
283 : 19 : discover_numa_and_core_dummy(dummy_config);
284 : : }
285 : :
286 [ + + ][ - + ]: 1248 : HMAP_FOR_EACH(n, hmap_node, &all_numa_nodes) {
287 [ + - ]: 631 : VLOG_INFO("Discovered %"PRIuSIZE" CPU cores on NUMA node %d",
288 : : ovs_list_size(&n->cores), n->numa_id);
289 : : }
290 : :
291 [ + - ]: 617 : VLOG_INFO("Discovered %"PRIuSIZE" NUMA nodes and %"PRIuSIZE" CPU cores",
292 : : hmap_count(&all_numa_nodes), hmap_count(&all_cpu_cores));
293 : :
294 [ + - ][ + - ]: 617 : if (hmap_count(&all_numa_nodes) && hmap_count(&all_cpu_cores)) {
295 : 617 : found_numa_and_core = true;
296 : : }
297 : :
298 : 617 : ovsthread_once_done(&once);
299 : :
300 : 617 : return true;
301 : : } else {
302 : 0 : return false;
303 : : }
304 : : }
305 : :
306 : : /* Extracts the numa node and core info from the 'config'. This is useful for
307 : : * testing purposes. The function must be called once, before ovs_numa_init().
308 : : *
309 : : * The format of 'config' is explained in the comment above
310 : : * discover_numa_and_core_dummy().*/
311 : : void
312 : 19 : ovs_numa_set_dummy(const char *config)
313 : : {
314 : 19 : dummy_numa = true;
315 [ - + ]: 19 : ovs_assert(config);
316 : 19 : free(dummy_config);
317 : 19 : dummy_config = xstrdup(config);
318 : 19 : }
319 : :
320 : : /* Initializes the numa module. */
321 : : void
322 : 617 : ovs_numa_init(void)
323 : : {
324 [ + + ]: 617 : if (dummy_numa) {
325 : 19 : ovs_numa_init__(dummy_config);
326 : : } else {
327 : 598 : ovs_numa_init__(NULL);
328 : : }
329 : 617 : }
330 : :
331 : : bool
332 : 166 : ovs_numa_numa_id_is_valid(int numa_id)
333 : : {
334 [ + - ][ + - ]: 166 : return found_numa_and_core && numa_id < ovs_numa_get_n_numas();
335 : : }
336 : :
337 : : bool
338 : 31 : ovs_numa_core_id_is_valid(unsigned core_id)
339 : : {
340 [ + - ][ + - ]: 31 : return found_numa_and_core && core_id < ovs_numa_get_n_cores();
341 : : }
342 : :
343 : : bool
344 : 0 : ovs_numa_core_is_pinned(unsigned core_id)
345 : : {
346 : 0 : struct cpu_core *core = get_core_by_core_id(core_id);
347 : :
348 [ # # ]: 0 : if (core) {
349 : 0 : return core->pinned;
350 : : }
351 : :
352 : 0 : return false;
353 : : }
354 : :
355 : : /* Returns the number of numa nodes. */
356 : : int
357 : 166 : ovs_numa_get_n_numas(void)
358 : : {
359 [ + - ]: 166 : return found_numa_and_core ? hmap_count(&all_numa_nodes)
360 : : : OVS_NUMA_UNSPEC;
361 : : }
362 : :
363 : : /* Returns the number of cpu cores. */
364 : : int
365 : 643 : ovs_numa_get_n_cores(void)
366 : : {
367 [ + - ]: 643 : return found_numa_and_core ? hmap_count(&all_cpu_cores)
368 : : : OVS_CORE_UNSPEC;
369 : : }
370 : :
371 : : /* Given 'core_id', returns the corresponding numa node id. Returns
372 : : * OVS_NUMA_UNSPEC if 'core_id' is invalid. */
373 : : int
374 : 0 : ovs_numa_get_numa_id(unsigned core_id)
375 : : {
376 : 0 : struct cpu_core *core = get_core_by_core_id(core_id);
377 : :
378 [ # # ]: 0 : if (core) {
379 : 0 : return core->numa->numa_id;
380 : : }
381 : :
382 : 0 : return OVS_NUMA_UNSPEC;
383 : : }
384 : :
385 : : /* Returns the number of cpu cores on numa node. Returns OVS_CORE_UNSPEC
386 : : * if 'numa_id' is invalid. */
387 : : int
388 : 0 : ovs_numa_get_n_cores_on_numa(int numa_id)
389 : : {
390 : 0 : struct numa_node *numa = get_numa_by_numa_id(numa_id);
391 : :
392 [ # # ]: 0 : if (numa) {
393 : 0 : return ovs_list_size(&numa->cores);
394 : : }
395 : :
396 : 0 : return OVS_CORE_UNSPEC;
397 : : }
398 : :
399 : : /* Returns the number of cpu cores that are available and unpinned
400 : : * on numa node. Returns OVS_CORE_UNSPEC if 'numa_id' is invalid. */
401 : : int
402 : 34 : ovs_numa_get_n_unpinned_cores_on_numa(int numa_id)
403 : : {
404 : 34 : struct numa_node *numa = get_numa_by_numa_id(numa_id);
405 : :
406 [ + - ]: 34 : if (numa) {
407 : : struct cpu_core *core;
408 : 34 : int count = 0;
409 : :
410 [ + + ]: 163 : LIST_FOR_EACH(core, list_node, &numa->cores) {
411 [ + + ][ + - ]: 129 : if (core->available && !core->pinned) {
412 : 99 : count++;
413 : : }
414 : : }
415 : 34 : return count;
416 : : }
417 : :
418 : 0 : return OVS_CORE_UNSPEC;
419 : : }
420 : :
421 : : /* Given 'core_id', tries to pin that core. Returns true, if succeeds.
422 : : * False, if the core has already been pinned, or if it is invalid or
423 : : * not available. */
424 : : bool
425 : 0 : ovs_numa_try_pin_core_specific(unsigned core_id)
426 : : {
427 : 0 : struct cpu_core *core = get_core_by_core_id(core_id);
428 : :
429 [ # # ]: 0 : if (core) {
430 [ # # ][ # # ]: 0 : if (core->available && !core->pinned) {
431 : 0 : core->pinned = true;
432 : 0 : return true;
433 : : }
434 : : }
435 : :
436 : 0 : return false;
437 : : }
438 : :
439 : : /* Searches through all cores for an unpinned and available core. Returns
440 : : * the 'core_id' if found and sets the 'core->pinned' to true. Otherwise,
441 : : * returns OVS_CORE_UNSPEC. */
442 : : unsigned
443 : 0 : ovs_numa_get_unpinned_core_any(void)
444 : : {
445 : : struct cpu_core *core;
446 : :
447 [ # # ][ # # ]: 0 : HMAP_FOR_EACH(core, hmap_node, &all_cpu_cores) {
448 [ # # ][ # # ]: 0 : if (core->available && !core->pinned) {
449 : 0 : core->pinned = true;
450 : 0 : return core->core_id;
451 : : }
452 : : }
453 : :
454 : 0 : return OVS_CORE_UNSPEC;
455 : : }
456 : :
457 : : /* Searches through all cores on numa node with 'numa_id' for an
458 : : * unpinned and available core. Returns the core_id if found and
459 : : * sets the 'core->pinned' to true. Otherwise, returns OVS_CORE_UNSPEC. */
460 : : unsigned
461 : 53 : ovs_numa_get_unpinned_core_on_numa(int numa_id)
462 : : {
463 : 53 : struct numa_node *numa = get_numa_by_numa_id(numa_id);
464 : :
465 [ + - ]: 53 : if (numa) {
466 : : struct cpu_core *core;
467 : :
468 [ + - ]: 137 : LIST_FOR_EACH(core, list_node, &numa->cores) {
469 [ + + ][ + + ]: 137 : if (core->available && !core->pinned) {
470 : 53 : core->pinned = true;
471 : 53 : return core->core_id;
472 : : }
473 : : }
474 : : }
475 : :
476 : 0 : return OVS_CORE_UNSPEC;
477 : : }
478 : :
479 : : /* Unpins the core with 'core_id'. */
480 : : void
481 : 31 : ovs_numa_unpin_core(unsigned core_id)
482 : : {
483 : 31 : struct cpu_core *core = get_core_by_core_id(core_id);
484 : :
485 [ + - ]: 31 : if (core) {
486 : 31 : core->pinned = false;
487 : : }
488 : 31 : }
489 : :
490 : : /* Given the 'numa_id', returns dump of all cores on the numa node. */
491 : : struct ovs_numa_dump *
492 : 0 : ovs_numa_dump_cores_on_numa(int numa_id)
493 : : {
494 : 0 : struct ovs_numa_dump *dump = xmalloc(sizeof *dump);
495 : 0 : struct numa_node *numa = get_numa_by_numa_id(numa_id);
496 : :
497 : 0 : ovs_list_init(&dump->dump);
498 : :
499 [ # # ]: 0 : if (numa) {
500 : : struct cpu_core *core;
501 : :
502 [ # # ]: 0 : LIST_FOR_EACH(core, list_node, &numa->cores) {
503 : 0 : struct ovs_numa_info *info = xmalloc(sizeof *info);
504 : :
505 : 0 : info->numa_id = numa->numa_id;
506 : 0 : info->core_id = core->core_id;
507 : 0 : ovs_list_insert(&dump->dump, &info->list_node);
508 : : }
509 : : }
510 : :
511 : 0 : return dump;
512 : : }
513 : :
514 : : void
515 : 0 : ovs_numa_dump_destroy(struct ovs_numa_dump *dump)
516 : : {
517 : : struct ovs_numa_info *iter;
518 : :
519 [ # # ]: 0 : if (!dump) {
520 : 0 : return;
521 : : }
522 : :
523 [ # # ]: 0 : LIST_FOR_EACH_POP (iter, list_node, &dump->dump) {
524 : 0 : free(iter);
525 : : }
526 : :
527 : 0 : free(dump);
528 : : }
529 : :
530 : : /* Reads the cpu mask configuration from 'cmask' and sets the
531 : : * 'available' of corresponding cores. For unspecified cores,
532 : : * sets 'available' to false. */
533 : : void
534 : 14 : ovs_numa_set_cpu_mask(const char *cmask)
535 : : {
536 : 14 : int core_id = 0;
537 : : int i;
538 : :
539 [ - + ]: 14 : if (!found_numa_and_core) {
540 : 0 : return;
541 : : }
542 : :
543 : : /* If no mask specified, resets the 'available' to true for all cores. */
544 [ + + ]: 14 : if (!cmask) {
545 : : struct cpu_core *core;
546 : :
547 [ + + ][ - + ]: 11 : HMAP_FOR_EACH(core, hmap_node, &all_cpu_cores) {
548 : 9 : core->available = true;
549 : : }
550 : :
551 : 2 : return;
552 : : }
553 : :
554 [ + + ]: 21 : for (i = strlen(cmask) - 1; i >= 0; i--) {
555 : 16 : char hex = toupper((unsigned char)cmask[i]);
556 : : int bin, j;
557 : :
558 [ + - ][ + + ]: 16 : if (hex >= '0' && hex <= '9') {
559 : 12 : bin = hex - '0';
560 [ + - ][ + - ]: 4 : } else if (hex >= 'A' && hex <= 'F') {
561 : 4 : bin = hex - 'A' + 10;
562 : : } else {
563 : 0 : bin = 0;
564 [ # # ]: 0 : VLOG_WARN("Invalid cpu mask: %c", cmask[i]);
565 : : }
566 : :
567 [ + + ]: 57 : for (j = 0; j < 4; j++) {
568 : : struct cpu_core *core;
569 : :
570 : 48 : core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores,
571 : : hash_int(core_id++, 0)),
572 : : struct cpu_core, hmap_node);
573 : 48 : core->available = (bin >> j) & 0x1;
574 : :
575 [ + + ]: 48 : if (core_id >= hmap_count(&all_cpu_cores)) {
576 : 7 : return;
577 : : }
578 : : }
579 : : }
580 : :
581 : : /* For unspecified cores, sets 'available' to false. */
582 [ + + ]: 28 : while (core_id < hmap_count(&all_cpu_cores)) {
583 : : struct cpu_core *core;
584 : :
585 : 23 : core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores,
586 : : hash_int(core_id++, 0)),
587 : : struct cpu_core, hmap_node);
588 : 23 : core->available = false;
589 : : }
590 : : }
591 : :
592 : 53 : int ovs_numa_thread_setaffinity_core(unsigned core_id OVS_UNUSED)
593 : : {
594 [ + - ]: 53 : if (dummy_numa) {
595 : : /* Nothing to do */
596 : 53 : return 0;
597 : : }
598 : :
599 : : #ifdef __linux__
600 : : cpu_set_t cpuset;
601 : : int err;
602 : :
603 : 0 : CPU_ZERO(&cpuset);
604 [ # # ]: 0 : CPU_SET(core_id, &cpuset);
605 : 0 : err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
606 [ # # ]: 0 : if (err) {
607 [ # # ]: 0 : VLOG_ERR("Thread affinity error %d",err);
608 : 0 : return err;
609 : : }
610 : :
611 : 53 : return 0;
612 : : #else /* !__linux__ */
613 : : return EOPNOTSUPP;
614 : : #endif /* __linux__ */
615 : : }
|