vlib physmem rework
[vpp.git] / src / vlib / unix / util.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * pci.c: Linux user space PCI bus management.
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vlib/vlib.h>
41 #include <vlib/unix/unix.h>
42
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include <dirent.h>
47
48 clib_error_t *
49 foreach_directory_file (char *dir_name,
50                         clib_error_t * (*f) (void *arg, u8 * path_name,
51                                              u8 * file_name), void *arg,
52                         int scan_dirs)
53 {
54   DIR *d;
55   struct dirent *e;
56   clib_error_t *error = 0;
57   u8 *s, *t;
58
59   d = opendir (dir_name);
60   if (!d)
61     {
62       if (errno == ENOENT)
63         return 0;
64       return clib_error_return_unix (0, "open `%s'", dir_name);
65     }
66
67   s = t = 0;
68   while (1)
69     {
70       e = readdir (d);
71       if (!e)
72         break;
73       if (scan_dirs)
74         {
75           if (e->d_type == DT_DIR
76               && (!strcmp (e->d_name, ".") || !strcmp (e->d_name, "..")))
77             continue;
78         }
79       else
80         {
81           if (e->d_type == DT_DIR)
82             continue;
83         }
84
85       s = format (s, "%s/%s", dir_name, e->d_name);
86       t = format (t, "%s", e->d_name);
87       error = f (arg, s, t);
88       _vec_len (s) = 0;
89       _vec_len (t) = 0;
90
91       if (error)
92         break;
93     }
94
95   vec_free (s);
96   closedir (d);
97
98   return error;
99 }
100
101 clib_error_t *
102 vlib_sysfs_write (char *file_name, char *fmt, ...)
103 {
104   u8 *s;
105   int fd;
106   clib_error_t *error = 0;
107
108   fd = open (file_name, O_WRONLY);
109   if (fd < 0)
110     return clib_error_return_unix (0, "open `%s'", file_name);
111
112   va_list va;
113   va_start (va, fmt);
114   s = va_format (0, fmt, &va);
115   va_end (va);
116
117   if (write (fd, s, vec_len (s)) < 0)
118     error = clib_error_return_unix (0, "write `%s'", file_name);
119
120   vec_free (s);
121   close (fd);
122   return error;
123 }
124
125 clib_error_t *
126 vlib_sysfs_read (char *file_name, char *fmt, ...)
127 {
128   unformat_input_t input;
129   u8 *s = 0;
130   int fd;
131   ssize_t sz;
132   uword result;
133
134   fd = open (file_name, O_RDONLY);
135   if (fd < 0)
136     return clib_error_return_unix (0, "open `%s'", file_name);
137
138   vec_validate (s, 4095);
139
140   sz = read (fd, s, vec_len (s));
141   if (sz < 0)
142     {
143       close (fd);
144       vec_free (s);
145       return clib_error_return_unix (0, "read `%s'", file_name);
146     }
147
148   _vec_len (s) = sz;
149   unformat_init_vector (&input, s);
150
151   va_list va;
152   va_start (va, fmt);
153   result = va_unformat (&input, fmt, &va);
154   va_end (va);
155
156   vec_free (s);
157   close (fd);
158
159   if (result == 0)
160     return clib_error_return (0, "unformat error");
161
162   return 0;
163 }
164
165 u8 *
166 vlib_sysfs_link_to_name (char *link)
167 {
168   char *p, buffer[64];
169   unformat_input_t in;
170   u8 *s = 0;
171   int r;
172
173   r = readlink (link, buffer, sizeof (buffer) - 1);
174
175   if (r < 0)
176     return 0;
177
178   buffer[r] = 0;
179   p = strrchr (buffer, '/');
180
181   if (!p)
182     return 0;
183
184   unformat_init_string (&in, p + 1, strlen (p + 1));
185   if (unformat (&in, "%s", &s) != 1)
186     clib_unix_warning ("no string?");
187   unformat_free (&in);
188
189   return s;
190 }
191
192 clib_error_t *
193 vlib_sysfs_set_nr_hugepages (unsigned int numa_node, int page_size, int nr)
194 {
195   clib_error_t *error = 0;
196   struct stat sb;
197   u8 *p = 0;
198
199   p = format (p, "/sys/devices/system/node/node%u%c", numa_node, 0);
200
201   if (stat ((char *) p, &sb) == 0)
202     {
203       if (S_ISDIR (sb.st_mode) == 0)
204         {
205           error = clib_error_return (0, "'%s' is not directory", p);
206           goto done;
207         }
208     }
209   else if (numa_node == 0)
210     {
211       vec_reset_length (p);
212       p = format (p, "/sys/kernel/mm%c", 0);
213       if (stat ((char *) p, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
214         {
215           error = clib_error_return (0, "'%s' does not exist or it is not "
216                                      "directory", p);
217           goto done;
218         }
219     }
220   else
221     {
222       error = clib_error_return (0, "'%s' does not exist", p);
223       goto done;
224     }
225
226   _vec_len (p) -= 1;
227   p = format (p, "/hugepages/hugepages-%ukB/nr_hugepages%c", page_size, 0);
228   vlib_sysfs_write ((char *) p, "%d", nr);
229
230 done:
231   vec_free (p);
232   return error;
233 }
234
235
236 static clib_error_t *
237 vlib_sysfs_get_xxx_hugepages (char *type, unsigned int numa_node,
238                               int page_size, int *val)
239 {
240   clib_error_t *error = 0;
241   struct stat sb;
242   u8 *p = 0;
243
244   p = format (p, "/sys/devices/system/node/node%u%c", numa_node, 0);
245
246   if (stat ((char *) p, &sb) == 0)
247     {
248       if (S_ISDIR (sb.st_mode) == 0)
249         {
250           error = clib_error_return (0, "'%s' is not directory", p);
251           goto done;
252         }
253     }
254   else if (numa_node == 0)
255     {
256       vec_reset_length (p);
257       p = format (p, "/sys/kernel/mm%c", 0);
258       if (stat ((char *) p, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
259         {
260           error = clib_error_return (0, "'%s' does not exist or it is not "
261                                      "directory", p);
262           goto done;
263         }
264     }
265   else
266     {
267       error = clib_error_return (0, "'%s' does not exist", p);
268       goto done;
269     }
270
271   _vec_len (p) -= 1;
272   p = format (p, "/hugepages/hugepages-%ukB/%s_hugepages%c", page_size,
273               type, 0);
274   error = vlib_sysfs_read ((char *) p, "%d", val);
275
276 done:
277   vec_free (p);
278   return error;
279 }
280
281 clib_error_t *
282 vlib_sysfs_get_free_hugepages (unsigned int numa_node, int page_size, int *v)
283 {
284   return vlib_sysfs_get_xxx_hugepages ("free", numa_node, page_size, v);
285 }
286
287 clib_error_t *
288 vlib_sysfs_get_nr_hugepages (unsigned int numa_node, int page_size, int *v)
289 {
290   return vlib_sysfs_get_xxx_hugepages ("nr", numa_node, page_size, v);
291 }
292
293 clib_error_t *
294 vlib_sysfs_get_surplus_hugepages (unsigned int numa_node, int page_size,
295                                   int *v)
296 {
297   return vlib_sysfs_get_xxx_hugepages ("surplus", numa_node, page_size, v);
298 }
299
300 clib_error_t *
301 vlib_sysfs_prealloc_hugepages (unsigned int numa_node, int page_size, int nr)
302 {
303   clib_error_t *error = 0;
304   int n, needed;
305   error = vlib_sysfs_get_free_hugepages (numa_node, page_size, &n);
306   if (error)
307     return error;
308   needed = nr - n;
309   if (needed <= 0)
310     return 0;
311
312   error = vlib_sysfs_get_nr_hugepages (numa_node, page_size, &n);
313   if (error)
314     return error;
315   clib_warning ("pre-allocating %u additional %uK hugepages on numa node %u",
316                 needed, page_size, numa_node);
317   return vlib_sysfs_set_nr_hugepages (numa_node, page_size, n + needed);
318 }
319
320 clib_error_t *
321 vlib_unix_recursive_mkdir (char *path)
322 {
323   clib_error_t *error = 0;
324   char *c = 0;
325   int i = 0;
326
327   while (path[i] != 0)
328     {
329       if (c && path[i] == '/')
330         {
331           vec_add1 (c, 0);
332           if ((mkdir (c, 0755)) && (errno != EEXIST))
333             {
334               error = clib_error_return_unix (0, "mkdir '%s'", c);
335               goto done;
336             }
337           _vec_len (c)--;
338         }
339       vec_add1 (c, path[i]);
340       i++;
341     }
342
343   if ((mkdir (path, 0755)) && (errno != EEXIST))
344     {
345       error = clib_error_return_unix (0, "mkdir '%s'", path);
346       goto done;
347     }
348
349 done:
350   vec_free (c);
351
352   return error;
353 }
354
355 clib_error_t *
356 vlib_unix_validate_runtime_file (unix_main_t * um,
357                                  const char *path, u8 ** full_path)
358 {
359   u8 *fp = 0;
360   char *last_slash = 0;
361
362   if (path[0] == '\0')
363     {
364       return clib_error_return (0, "path is an empty string");
365     }
366   else if (strncmp (path, "../", 3) == 0 || strstr (path, "/../"))
367     {
368       return clib_error_return (0, "'..' not allowed in runtime path");
369     }
370   else if (path[0] == '/')
371     {
372       /* Absolute path. Has to start with runtime directory */
373       if (strncmp ((char *) um->runtime_dir, path,
374                    strlen ((char *) um->runtime_dir)))
375         {
376           return clib_error_return (0,
377                                     "file %s is not in runtime directory %s",
378                                     path, um->runtime_dir);
379         }
380       fp = format (0, "%s%c", path, '\0');
381     }
382   else
383     {
384       /* Relative path, just append to runtime */
385       fp = format (0, "%s/%s%c", um->runtime_dir, path, '\0');
386     }
387
388   /* We don't want to create a directory out of the last file */
389   if ((last_slash = strrchr ((char *) fp, '/')) != NULL)
390     *last_slash = '\0';
391
392   clib_error_t *error = vlib_unix_recursive_mkdir ((char *) fp);
393
394   if (last_slash != NULL)
395     *last_slash = '/';
396
397   if (error)
398     vec_free (fp);
399
400   *full_path = fp;
401   return error;
402 }
403
404 /*
405  * fd.io coding-style-patch-verification: ON
406  *
407  * Local Variables:
408  * eval: (c-set-style "gnu")
409  * End:
410  */