]> git.proxmox.com Git - mirror_ovs.git/blame - lib/ovs-numa.c
ovs-numa: Relax the ovs_numa_*() input argument check.
[mirror_ovs.git] / lib / ovs-numa.c
CommitLineData
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
17/* On non-Linux, these functions are defined inline in ovs-numa.h. */
18#ifdef __linux__
19
20#include <config.h>
21#include "ovs-numa.h"
22
23#include <ctype.h>
24#include <dirent.h>
25#include <errno.h>
26#include <stddef.h>
27#include <string.h>
28#include <sys/types.h>
29#include <unistd.h>
30
31#include "hash.h"
32#include "hmap.h"
33#include "list.h"
34#include "ovs-thread.h"
35#include "vlog.h"
36
37VLOG_DEFINE_THIS_MODULE(ovs_numa);
38
012c0a04 39#define MAX_NUMA_NODES 128
7c5a3bbf 40
012c0a04
AW
41/* numa node. */
42struct numa_node {
43 struct hmap_node hmap_node; /* In the 'all_numa_nodes'. */
44 struct list cores; /* List of cpu cores on the numa node. */
45 int numa_id; /* numa node id. */
7c5a3bbf
AW
46};
47
012c0a04 48/* Cpu core on a numa node. */
7c5a3bbf
AW
49struct cpu_core {
50 struct hmap_node hmap_node;/* In the 'all_cpu_cores'. */
012c0a04
AW
51 struct list list_node; /* In 'numa_node->cores' list. */
52 struct numa_node *numa; /* numa node containing the core. */
7c5a3bbf
AW
53 int core_id; /* Core id. */
54 bool pinned; /* If a thread has been pinned to the core. */
55};
56
012c0a04
AW
57/* Contains all 'struct numa_node's. */
58static struct hmap all_numa_nodes = HMAP_INITIALIZER(&all_numa_nodes);
7c5a3bbf
AW
59/* Contains all 'struct cpu_core's. */
60static struct hmap all_cpu_cores = HMAP_INITIALIZER(&all_cpu_cores);
012c0a04
AW
61/* True if numa node and core info are correctly extracted. */
62static bool found_numa_and_core;
7c5a3bbf
AW
63
64/* Returns true if 'str' contains all digits. Returns false otherwise. */
65static bool
66contain_all_digits(const char *str)
67{
68 return str[strspn(str, "0123456789")] == '\0';
69}
70
012c0a04
AW
71/* Discovers all numa nodes and the corresponding cpu cores.
72 * Constructs the 'struct numa_node' and 'struct cpu_core'. */
7c5a3bbf 73static void
012c0a04 74discover_numa_and_core(void)
7c5a3bbf
AW
75{
76 int n_cpus = 0;
77 int i;
78
012c0a04 79 for (i = 0; i < MAX_NUMA_NODES; i++) {
7c5a3bbf
AW
80 DIR *dir;
81 char* path;
82
83 /* Constructs the path to node /sys/devices/system/nodeX. */
84 path = xasprintf("/sys/devices/system/node/node%d", i);
85 dir = opendir(path);
86
012c0a04 87 /* Creates 'struct numa_node' if the 'dir' is non-null. */
7c5a3bbf 88 if (dir) {
012c0a04 89 struct numa_node *n = xzalloc(sizeof *n);
7c5a3bbf
AW
90 struct dirent *subdir;
91
012c0a04
AW
92 hmap_insert(&all_numa_nodes, &n->hmap_node, hash_int(i, 0));
93 list_init(&n->cores);
94 n->numa_id = i;
7c5a3bbf
AW
95
96 while ((subdir = readdir(dir)) != NULL) {
97 if (!strncmp(subdir->d_name, "cpu", 3)
98 && contain_all_digits(subdir->d_name + 3)){
99 struct cpu_core *c = xzalloc(sizeof *c);
100 uint32_t core_id;
101
102 core_id = strtoul(subdir->d_name + 3, NULL, 10);
103 hmap_insert(&all_cpu_cores, &c->hmap_node,
104 hash_int(core_id, 0));
012c0a04 105 list_insert(&n->cores, &c->list_node);
7c5a3bbf
AW
106 c->core_id = core_id;
107 n_cpus++;
108 }
109 }
012c0a04
AW
110 VLOG_INFO("Discovered %"PRIuSIZE" CPU cores on NUMA node %d",
111 list_size(&n->cores), n->numa_id);
7c5a3bbf
AW
112 free(path);
113 closedir(dir);
114 } else {
115 if (errno != ENOENT) {
116 VLOG_WARN("opendir(%s) failed (%s)", path,
117 ovs_strerror(errno));
118 }
119 free(path);
120 break;
121 }
122 }
123
012c0a04
AW
124 VLOG_INFO("Discovered %"PRIuSIZE" NUMA nodes and %d CPU cores",
125 hmap_count(&all_numa_nodes), n_cpus);
126 if (hmap_count(&all_numa_nodes) && hmap_count(&all_cpu_cores)) {
127 found_numa_and_core = true;
7c5a3bbf
AW
128 }
129}
130
131/* Extracts the numa node and core info from the 'sysfs'. */
132void
133ovs_numa_init(void)
134{
135 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
136
137 if (ovsthread_once_start(&once)) {
012c0a04 138 discover_numa_and_core();
7c5a3bbf
AW
139 ovsthread_once_done(&once);
140 }
141}
142
143bool
012c0a04 144ovs_numa_numa_id_is_valid(int numa_id)
7c5a3bbf 145{
421aa227 146 return found_numa_and_core && numa_id < ovs_numa_get_n_numas();
7c5a3bbf
AW
147}
148
149bool
012c0a04 150ovs_numa_core_id_is_valid(int core_id)
7c5a3bbf 151{
421aa227 152 return found_numa_and_core && core_id < ovs_numa_get_n_cores();
7c5a3bbf
AW
153}
154
012c0a04 155/* Returns the number of numa nodes. */
7c5a3bbf 156int
012c0a04 157ovs_numa_get_n_numas(void)
7c5a3bbf 158{
012c0a04
AW
159 return found_numa_and_core ? hmap_count(&all_numa_nodes)
160 : OVS_NUMA_UNSPEC;
7c5a3bbf
AW
161}
162
163/* Returns the number of cpu cores. */
164int
165ovs_numa_get_n_cores(void)
166{
012c0a04
AW
167 return found_numa_and_core ? hmap_count(&all_cpu_cores)
168 : OVS_CORE_UNSPEC;
7c5a3bbf
AW
169}
170
421aa227
AW
171/* Returns the number of cpu cores on numa node. Returns OVS_CORE_UNSPEC
172 * if 'numa_id' is invalid. */
7c5a3bbf 173int
012c0a04 174ovs_numa_get_n_cores_on_numa(int numa_id)
7c5a3bbf 175{
421aa227 176 if (ovs_numa_numa_id_is_valid(numa_id)) {
012c0a04 177 struct numa_node *numa;
7c5a3bbf 178
012c0a04
AW
179 numa = CONTAINER_OF(hmap_first_with_hash(&all_numa_nodes,
180 hash_int(numa_id, 0)),
181 struct numa_node, hmap_node);
7c5a3bbf 182
012c0a04 183 return list_size(&numa->cores);
7c5a3bbf
AW
184 }
185
186 return OVS_CORE_UNSPEC;
187}
188
421aa227
AW
189/* Returns the number of unpinned cpu cores on numa node. Returns
190 * OVS_CORE_UNSPEC if 'numa_id' is invalid. */
7c5a3bbf 191int
012c0a04 192ovs_numa_get_n_unpinned_cores_on_numa(int numa_id)
7c5a3bbf 193{
421aa227 194 if (ovs_numa_numa_id_is_valid(numa_id)) {
012c0a04 195 struct numa_node *numa;
7c5a3bbf
AW
196 struct cpu_core *core;
197 int count = 0;
198
012c0a04
AW
199 numa = CONTAINER_OF(hmap_first_with_hash(&all_numa_nodes,
200 hash_int(numa_id, 0)),
201 struct numa_node, hmap_node);
202 LIST_FOR_EACH(core, list_node, &numa->cores) {
7c5a3bbf
AW
203 if (!core->pinned) {
204 count++;
205 }
206 }
207
208 return count;
209 }
210
211 return OVS_CORE_UNSPEC;
212}
213
214/* Given 'core_id', tries to pin that core. Returns true, if succeeds.
421aa227 215 * False, if the core has already been pinned or if 'core_id' is invalid. */
7c5a3bbf
AW
216bool
217ovs_numa_try_pin_core_specific(int core_id)
218{
421aa227
AW
219 if (ovs_numa_core_id_is_valid(core_id)) {
220 struct cpu_core *core;
7c5a3bbf 221
421aa227
AW
222 core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores,
223 hash_int(core_id, 0)),
224 struct cpu_core, hmap_node);
225 if (!core->pinned) {
226 core->pinned = true;
227 return true;
228 }
7c5a3bbf
AW
229 }
230
231 return false;
232}
233
234/* Searches through all cores for an unpinned core. Returns the core_id
421aa227
AW
235 * if found and set the 'core->pinned' to true. Otherwise, returns
236 * OVS_CORE_UNSPEC. */
7c5a3bbf
AW
237int
238ovs_numa_get_unpinned_core_any(void)
239{
240 struct cpu_core *core;
241
242 HMAP_FOR_EACH(core, hmap_node, &all_cpu_cores) {
243 if (!core->pinned) {
244 core->pinned = true;
245 return core->core_id;
246 }
247 }
248
249 return OVS_CORE_UNSPEC;
250}
251
012c0a04
AW
252/* Searches through all cores on numa node with 'numa_id' for an unpinned
253 * core. Returns the core_id if found and sets the 'core->pinned' to true.
421aa227 254 * Otherwise, returns OVS_CORE_UNSPEC. */
7c5a3bbf 255int
012c0a04 256ovs_numa_get_unpinned_core_on_numa(int numa_id)
7c5a3bbf 257{
421aa227
AW
258 if (ovs_numa_numa_id_is_valid(numa_id)) {
259 struct numa_node *numa;
260 struct cpu_core *core;
7c5a3bbf 261
421aa227
AW
262 numa = CONTAINER_OF(hmap_first_with_hash(&all_numa_nodes,
263 hash_int(numa_id, 0)),
264 struct numa_node, hmap_node);
265 LIST_FOR_EACH(core, list_node, &numa->cores) {
266 if (!core->pinned) {
267 core->pinned = true;
268 return core->core_id;
269 }
7c5a3bbf
AW
270 }
271 }
272
273 return OVS_CORE_UNSPEC;
274}
275
276/* Resets the 'core->pinned' for the core with 'core_id'. */
277void
278ovs_numa_unpin_core(int core_id)
279{
421aa227
AW
280 if (ovs_numa_core_id_is_valid(core_id)) {
281 struct cpu_core *core;
7c5a3bbf 282
421aa227
AW
283 core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores,
284 hash_int(core_id, 0)),
285 struct cpu_core, hmap_node);
286 core->pinned = false;
287 }
7c5a3bbf
AW
288}
289
290#endif /* __linux__ */