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