dpif-netdev: Introduce port_try_ref() to prevent a race.
[cascardo/ovs.git] / lib / ovs-numa.c
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
39 #define MAX_CPU_SOCKETS 128
40
41 /* Cpu socket. */
42 struct cpu_socket {
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. */
46 };
47
48 /* Cpu core on a cpu socket. */
49 struct cpu_core {
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. */
55 };
56
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;
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
71 /* Discovers all cpu sockets and the corresponding cpu cores for each socket.
72  * Constructs the 'struct cpu_socket' and 'struct cpu_core'. */
73 static void
74 discover_sockets_and_cores(void)
75 {
76     int n_cpus = 0;
77     int i;
78
79     for (i = 0; i < MAX_CPU_SOCKETS; i++) {
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
87         /* Creates 'struct cpu_socket' if the 'dir' is non-null. */
88         if (dir) {
89             struct cpu_socket *s = xzalloc(sizeof *s);
90             struct dirent *subdir;
91
92             hmap_insert(&all_cpu_sockets, &s->hmap_node, hash_int(i, 0));
93             list_init(&s->cores);
94             s->socket_id = i;
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));
105                     list_insert(&s->cores, &c->list_node);
106                     c->core_id = core_id;
107                     n_cpus++;
108                 }
109             }
110             VLOG_INFO("Discovered %"PRIuSIZE" CPU cores on CPU socket %d",
111                       list_size(&s->cores), s->socket_id);
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
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;
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)) {
138         discover_sockets_and_cores();
139         ovsthread_once_done(&once);
140     }
141 }
142
143 bool
144 ovs_numa_cpu_socket_id_is_valid(int sid)
145 {
146     return sid < ovs_numa_get_n_sockets();
147 }
148
149 bool
150 ovs_numa_cpu_core_id_is_valid(int cid)
151 {
152     return cid < ovs_numa_get_n_cores();
153 }
154
155 /* Returns the number of cpu sockets. */
156 int
157 ovs_numa_get_n_sockets(void)
158 {
159     return found_sockets_and_cores ? hmap_count(&all_cpu_sockets)
160                                    : OVS_SOCKET_UNSPEC;
161 }
162
163 /* Returns the number of cpu cores. */
164 int
165 ovs_numa_get_n_cores(void)
166 {
167     return found_sockets_and_cores ? hmap_count(&all_cpu_cores)
168                                    : OVS_CORE_UNSPEC;
169 }
170
171 /* Returns the number of cpu cores on socket. */
172 int
173 ovs_numa_get_n_cores_on_socket(int socket_id)
174 {
175     if (found_sockets_and_cores) {
176         struct cpu_socket *socket;
177
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);
182
183         return list_size(&socket->cores);
184     }
185
186     return OVS_CORE_UNSPEC;
187 }
188
189 /* Returns the number of unpinned cpu cores on socket. */
190 int
191 ovs_numa_get_n_unpinned_cores_on_socket(int socket_id)
192 {
193     if (found_sockets_and_cores) {
194         struct cpu_socket *socket;
195         struct cpu_core *core;
196         int count = 0;
197
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) {
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.
215  * False, if the core has already been pinned. */
216 bool
217 ovs_numa_try_pin_core_specific(int core_id)
218 {
219     struct cpu_core *core;
220
221     ovs_assert(ovs_numa_cpu_core_id_is_valid(core_id));
222
223     core = CONTAINER_OF(hmap_first_with_hash(&all_cpu_cores,
224                                              hash_int(core_id, 0)),
225                         struct cpu_core, hmap_node);
226     if (!core->pinned) {
227         core->pinned = true;
228         return true;
229     }
230
231     return false;
232 }
233
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. */
236 int
237 ovs_numa_get_unpinned_core_any(void)
238 {
239     struct cpu_core *core;
240
241     HMAP_FOR_EACH(core, hmap_node, &all_cpu_cores) {
242         if (!core->pinned) {
243             core->pinned = true;
244             return core->core_id;
245         }
246     }
247
248     return OVS_CORE_UNSPEC;
249 }
250
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. */
254 int
255 ovs_numa_get_unpinned_core_on_socket(int socket_id)
256 {
257     struct cpu_socket *socket;
258     struct cpu_core *core;
259
260     ovs_assert(ovs_numa_cpu_socket_id_is_valid(socket_id));
261
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) {
266         if (!core->pinned) {
267             core->pinned = true;
268             return core->core_id;
269         }
270     }
271
272     return OVS_CORE_UNSPEC;
273 }
274
275 /* Resets the 'core->pinned' for the core with 'core_id'. */
276 void
277 ovs_numa_unpin_core(int core_id)
278 {
279     struct cpu_core *core;
280
281     ovs_assert(ovs_numa_cpu_core_id_is_valid(core_id));
282
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 }
288
289 #endif /* __linux__ */