199e6bbfd69604607bd2ad7cbea961b5a716d522
[vpp.git] / src / vppinfra / linux / sysfs.c
1 /*
2  * Copyright (c) 2017 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 #include <vppinfra/clib.h>
17 #include <vppinfra/clib_error.h>
18 #include <vppinfra/format.h>
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <dirent.h>
24
25 #define DEFAULT_HUGETLB_SIZE 2048
26
27 clib_error_t *
28 clib_sysfs_write (char *file_name, char *fmt, ...)
29 {
30   u8 *s;
31   int fd;
32   clib_error_t *error = 0;
33
34   fd = open (file_name, O_WRONLY);
35   if (fd < 0)
36     return clib_error_return_unix (0, "open `%s'", file_name);
37
38   va_list va;
39   va_start (va, fmt);
40   s = va_format (0, fmt, &va);
41   va_end (va);
42
43   if (write (fd, s, vec_len (s)) < 0)
44     error = clib_error_return_unix (0, "write `%s'", file_name);
45
46   vec_free (s);
47   close (fd);
48   return error;
49 }
50
51 clib_error_t *
52 clib_sysfs_read (char *file_name, char *fmt, ...)
53 {
54   unformat_input_t input;
55   u8 *s = 0;
56   int fd;
57   ssize_t sz;
58   uword result;
59
60   fd = open (file_name, O_RDONLY);
61   if (fd < 0)
62     return clib_error_return_unix (0, "open `%s'", file_name);
63
64   vec_validate (s, 4095);
65
66   sz = read (fd, s, vec_len (s));
67   if (sz < 0)
68     {
69       close (fd);
70       vec_free (s);
71       return clib_error_return_unix (0, "read `%s'", file_name);
72     }
73
74   _vec_len (s) = sz;
75   unformat_init_vector (&input, s);
76
77   va_list va;
78   va_start (va, fmt);
79   result = va_unformat (&input, fmt, &va);
80   va_end (va);
81
82   vec_free (s);
83   close (fd);
84
85   if (result == 0)
86     return clib_error_return (0, "unformat error");
87
88   return 0;
89 }
90
91 u8 *
92 clib_sysfs_link_to_name (char *link)
93 {
94   char *p, buffer[64];
95   unformat_input_t in;
96   u8 *s = 0;
97   int r;
98
99   r = readlink (link, buffer, sizeof (buffer) - 1);
100
101   if (r < 0)
102     return 0;
103
104   buffer[r] = 0;
105   p = strrchr (buffer, '/');
106
107   if (!p)
108     return 0;
109
110   unformat_init_string (&in, p + 1, strlen (p + 1));
111   if (unformat (&in, "%s", &s) != 1)
112     clib_unix_warning ("no string?");
113   unformat_free (&in);
114
115   return s;
116 }
117
118 clib_error_t *
119 clib_sysfs_set_nr_hugepages (int numa_node, int log2_page_size, int nr)
120 {
121   clib_error_t *error = 0;
122   struct stat sb;
123   u8 *p = 0;
124   int page_size = log2_page_size ? 1ULL << (log2_page_size - 10) :
125     DEFAULT_HUGETLB_SIZE;
126
127   p = format (p, "/sys/devices/system/node/node%u%c", numa_node, 0);
128
129   if (stat ((char *) p, &sb) == 0)
130     {
131       if (S_ISDIR (sb.st_mode) == 0)
132         {
133           error = clib_error_return (0, "'%s' is not directory", p);
134           goto done;
135         }
136     }
137   else if (numa_node == 0)
138     {
139       vec_reset_length (p);
140       p = format (p, "/sys/kernel/mm%c", 0);
141       if (stat ((char *) p, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
142         {
143           error = clib_error_return (0, "'%s' does not exist or it is not "
144                                      "directory", p);
145           goto done;
146         }
147     }
148   else
149     {
150       error = clib_error_return (0, "'%s' does not exist", p);
151       goto done;
152     }
153
154   _vec_len (p) -= 1;
155   p = format (p, "/hugepages/hugepages-%ukB/nr_hugepages%c", page_size, 0);
156   clib_sysfs_write ((char *) p, "%d", nr);
157
158 done:
159   vec_free (p);
160   return error;
161 }
162
163
164 static clib_error_t *
165 clib_sysfs_get_xxx_hugepages (char *type, int numa_node,
166                               int log2_page_size, int *val)
167 {
168   clib_error_t *error = 0;
169   struct stat sb;
170   u8 *p = 0;
171   int page_size = log2_page_size ? 1ULL << (log2_page_size - 10) :
172     DEFAULT_HUGETLB_SIZE;
173
174   p = format (p, "/sys/devices/system/node/node%u%c", numa_node, 0);
175
176   if (stat ((char *) p, &sb) == 0)
177     {
178       if (S_ISDIR (sb.st_mode) == 0)
179         {
180           error = clib_error_return (0, "'%s' is not directory", p);
181           goto done;
182         }
183     }
184   else if (numa_node == 0)
185     {
186       vec_reset_length (p);
187       p = format (p, "/sys/kernel/mm%c", 0);
188       if (stat ((char *) p, &sb) < 0 || S_ISDIR (sb.st_mode) == 0)
189         {
190           error = clib_error_return (0, "'%s' does not exist or it is not "
191                                      "directory", p);
192           goto done;
193         }
194     }
195   else
196     {
197       error = clib_error_return (0, "'%s' does not exist", p);
198       goto done;
199     }
200
201   _vec_len (p) -= 1;
202   p = format (p, "/hugepages/hugepages-%ukB/%s_hugepages%c", page_size,
203               type, 0);
204   error = clib_sysfs_read ((char *) p, "%d", val);
205
206 done:
207   vec_free (p);
208   return error;
209 }
210
211 clib_error_t *
212 clib_sysfs_get_free_hugepages (int numa_node, int log2_page_size, int *v)
213 {
214   return clib_sysfs_get_xxx_hugepages ("free", numa_node, log2_page_size, v);
215 }
216
217 clib_error_t *
218 clib_sysfs_get_nr_hugepages (int numa_node, int log2_page_size, int *v)
219 {
220   return clib_sysfs_get_xxx_hugepages ("nr", numa_node, log2_page_size, v);
221 }
222
223 clib_error_t *
224 clib_sysfs_get_surplus_hugepages (int numa_node, int log2_page_size, int *v)
225 {
226   return clib_sysfs_get_xxx_hugepages ("surplus", numa_node, log2_page_size,
227                                        v);
228 }
229
230 clib_error_t *
231 clib_sysfs_prealloc_hugepages (int numa_node, int log2_page_size, int nr)
232 {
233   clib_error_t *error = 0;
234   int n, needed;
235   int page_size = log2_page_size ? 1ULL << (log2_page_size - 10) :
236     DEFAULT_HUGETLB_SIZE;
237   error = clib_sysfs_get_free_hugepages (numa_node, log2_page_size, &n);
238   if (error)
239     return error;
240   needed = nr - n;
241   if (needed <= 0)
242     return 0;
243
244   error = clib_sysfs_get_nr_hugepages (numa_node, log2_page_size, &n);
245   if (error)
246     return error;
247   clib_warning ("pre-allocating %u additional %uK hugepages on numa node %u",
248                 needed, page_size, numa_node);
249   return clib_sysfs_set_nr_hugepages (numa_node, log2_page_size, n + needed);
250 }
251
252
253 /*
254  * fd.io coding-style-patch-verification: ON
255  *
256  * Local Variables:
257  * eval: (c-set-style "gnu")
258  * End:
259  */