2 * Copyright (c) 2014 Nicira, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /* On non-Linux, these functions are defined inline in ovs-numa.h. */
28 #include <sys/types.h>
34 #include "ovs-thread.h"
37 VLOG_DEFINE_THIS_MODULE(ovs_numa);
39 #define MAX_CPU_SOCKETS 128
43 struct hmap_node hmap_node; /* In the 'all_cpu_sockets'. */
44 struct list cores; /* List of cpu cores on the socket. */
45 int socket_id; /* Socket id. */
48 /* Cpu core on a cpu socket. */
50 struct hmap_node hmap_node;/* In the 'all_cpu_cores'. */
51 struct list list_node; /* In 'cpu_socket->cores' list. */
52 struct cpu_socket *socket; /* Socket containing the core. */
53 int core_id; /* Core id. */
54 bool pinned; /* If a thread has been pinned to the core. */
57 /* Contains all 'struct cpu_socket's. */
58 static struct hmap all_cpu_sockets = HMAP_INITIALIZER(&all_cpu_sockets);
59 /* Contains all 'struct cpu_core's. */
60 static struct hmap all_cpu_cores = HMAP_INITIALIZER(&all_cpu_cores);
61 /* True if socket and core info are correctly extracted. */
62 static bool found_sockets_and_cores;
64 /* Returns true if 'str' contains all digits. Returns false otherwise. */
66 contain_all_digits(const char *str)
68 return str[strspn(str, "0123456789")] == '\0';
71 /* Discovers all cpu sockets and the corresponding cpu cores for each socket.
72 * Constructs the 'struct cpu_socket' and 'struct cpu_core'. */
74 discover_sockets_and_cores(void)
79 for (i = 0; i < MAX_CPU_SOCKETS; i++) {
83 /* Constructs the path to node /sys/devices/system/nodeX. */
84 path = xasprintf("/sys/devices/system/node/node%d", i);
87 /* Creates 'struct cpu_socket' if the 'dir' is non-null. */
89 struct cpu_socket *s = xzalloc(sizeof *s);
90 struct dirent *subdir;
92 hmap_insert(&all_cpu_sockets, &s->hmap_node, hash_int(i, 0));
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);
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));
105 list_insert(&s->cores, &c->list_node);
106 c->core_id = core_id;
110 VLOG_INFO("Discovered %"PRIuSIZE" CPU cores on CPU socket %d",
111 list_size(&s->cores), s->socket_id);
115 if (errno != ENOENT) {
116 VLOG_WARN("opendir(%s) failed (%s)", path,
117 ovs_strerror(errno));
124 VLOG_INFO("Discovered %"PRIuSIZE" CPU Sockets and %d CPU cores",
125 hmap_count(&all_cpu_sockets), n_cpus);
126 if (hmap_count(&all_cpu_sockets) && hmap_count(&all_cpu_cores)) {
127 found_sockets_and_cores = true;
131 /* Extracts the numa node and core info from the 'sysfs'. */
135 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
137 if (ovsthread_once_start(&once)) {
138 discover_sockets_and_cores();
139 ovsthread_once_done(&once);
144 ovs_numa_cpu_socket_id_is_valid(int sid)
146 return sid < ovs_numa_get_n_sockets();
150 ovs_numa_cpu_core_id_is_valid(int cid)
152 return cid < ovs_numa_get_n_cores();
155 /* Returns the number of cpu sockets. */
157 ovs_numa_get_n_sockets(void)
159 return found_sockets_and_cores ? hmap_count(&all_cpu_sockets)
163 /* Returns the number of cpu cores. */
165 ovs_numa_get_n_cores(void)
167 return found_sockets_and_cores ? hmap_count(&all_cpu_cores)
171 /* Returns the number of cpu cores on socket. */
173 ovs_numa_get_n_cores_on_socket(int socket_id)
175 if (found_sockets_and_cores) {
176 struct cpu_socket *socket;
178 ovs_assert(ovs_numa_cpu_socket_id_is_valid(socket_id));
179 socket = CONTAINER_OF(hmap_first_with_hash(&all_cpu_sockets,
180 hash_int(socket_id, 0)),
181 struct cpu_socket, hmap_node);
183 return list_size(&socket->cores);
186 return OVS_CORE_UNSPEC;
189 /* Returns the number of unpinned cpu cores on socket. */
191 ovs_numa_get_n_unpinned_cores_on_socket(int socket_id)
193 if (found_sockets_and_cores) {
194 struct cpu_socket *socket;
195 struct cpu_core *core;
198 ovs_assert(ovs_numa_cpu_socket_id_is_valid(socket_id));
199 socket = CONTAINER_OF(hmap_first_with_hash(&all_cpu_sockets,
200 hash_int(socket_id, 0)),
201 struct cpu_socket, hmap_node);
202 LIST_FOR_EACH(core, list_node, &socket->cores) {
211 return OVS_CORE_UNSPEC;
214 /* Given 'core_id', tries to pin that core. Returns true, if succeeds.
215 * False, if the core has already been pinned. */
217 ovs_numa_try_pin_core_specific(int core_id)
219 struct cpu_core *core;
221 ovs_assert(ovs_numa_cpu_core_id_is_valid(core_id));
223 core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores,
224 hash_int(core_id, 0)),
225 struct cpu_core, hmap_node);
234 /* Searches through all cores for an unpinned core. Returns the core_id
235 * if found and set the 'core->pinned' to true. Otherwise, returns -1. */
237 ovs_numa_get_unpinned_core_any(void)
239 struct cpu_core *core;
241 HMAP_FOR_EACH(core, hmap_node, &all_cpu_cores) {
244 return core->core_id;
248 return OVS_CORE_UNSPEC;
251 /* Searches through all cores on socket with 'socket_id' for an unpinned core.
252 * Returns the core_id if found and sets the 'core->pinned' to true.
253 * Otherwise, returns -1. */
255 ovs_numa_get_unpinned_core_on_socket(int socket_id)
257 struct cpu_socket *socket;
258 struct cpu_core *core;
260 ovs_assert(ovs_numa_cpu_socket_id_is_valid(socket_id));
262 socket = CONTAINER_OF(hmap_first_with_hash(&all_cpu_sockets,
263 hash_int(socket_id, 0)),
264 struct cpu_socket, hmap_node);
265 LIST_FOR_EACH(core, list_node, &socket->cores) {
268 return core->core_id;
272 return OVS_CORE_UNSPEC;
275 /* Resets the 'core->pinned' for the core with 'core_id'. */
277 ovs_numa_unpin_core(int core_id)
279 struct cpu_core *core;
281 ovs_assert(ovs_numa_cpu_core_id_is_valid(core_id));
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;
289 #endif /* __linux__ */