]>
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 | ||
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 | ||
37 | VLOG_DEFINE_THIS_MODULE(ovs_numa); | |
38 | ||
012c0a04 | 39 | #define MAX_NUMA_NODES 128 |
7c5a3bbf | 40 | |
012c0a04 AW |
41 | /* numa node. */ |
42 | struct 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 |
49 | struct 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. */ |
58 | static struct hmap all_numa_nodes = HMAP_INITIALIZER(&all_numa_nodes); | |
7c5a3bbf AW |
59 | /* Contains all 'struct cpu_core's. */ |
60 | static struct hmap all_cpu_cores = HMAP_INITIALIZER(&all_cpu_cores); | |
012c0a04 AW |
61 | /* True if numa node and core info are correctly extracted. */ |
62 | static bool found_numa_and_core; | |
7c5a3bbf AW |
63 | |
64 | /* Returns true if 'str' contains all digits. Returns false otherwise. */ | |
65 | static bool | |
66 | contain_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 | 73 | static void |
012c0a04 | 74 | discover_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'. */ | |
132 | void | |
133 | ovs_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 | ||
143 | bool | |
012c0a04 | 144 | ovs_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 | ||
149 | bool | |
012c0a04 | 150 | ovs_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 | 156 | int |
012c0a04 | 157 | ovs_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. */ | |
164 | int | |
165 | ovs_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 | 173 | int |
012c0a04 | 174 | ovs_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 | 191 | int |
012c0a04 | 192 | ovs_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 |
216 | bool |
217 | ovs_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 |
237 | int |
238 | ovs_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 | 255 | int |
012c0a04 | 256 | ovs_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'. */ | |
277 | void | |
278 | ovs_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__ */ |