8edae74f9cd5e023fca219b146b39719f5f70102
[vpp.git] / src / vppinfra / linux / mem.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 #define _GNU_SOURCE
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <sys/mount.h>
22 #include <sys/mman.h>
23 #include <fcntl.h>
24 #include <linux/mempolicy.h>
25 #include <linux/memfd.h>
26
27 #include <vppinfra/clib.h>
28 #include <vppinfra/mem.h>
29 #include <vppinfra/time.h>
30 #include <vppinfra/format.h>
31 #include <vppinfra/clib_error.h>
32 #include <vppinfra/linux/syscall.h>
33 #include <vppinfra/linux/sysfs.h>
34
35 #ifndef F_LINUX_SPECIFIC_BASE
36 #define F_LINUX_SPECIFIC_BASE 1024
37 #endif
38
39 #ifndef F_ADD_SEALS
40 #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
41 #define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
42
43 #define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
44 #define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
45 #define F_SEAL_GROW     0x0004  /* prevent file from growing */
46 #define F_SEAL_WRITE    0x0008  /* prevent writes */
47 #endif
48
49
50 uword
51 clib_mem_get_page_size (void)
52 {
53   return getpagesize ();
54 }
55
56 uword
57 clib_mem_get_default_hugepage_size (void)
58 {
59   unformat_input_t input;
60   static u32 size = 0;
61   int fd;
62
63   if (size)
64     goto done;
65
66   if ((fd = open ("/proc/meminfo", 0)) == -1)
67     return 0;
68
69   unformat_init_clib_file (&input, fd);
70
71   while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
72     {
73       if (unformat (&input, "Hugepagesize:%_%u kB", &size))
74         ;
75       else
76         unformat_skip_line (&input);
77     }
78   unformat_free (&input);
79   close (fd);
80 done:
81   return 1024ULL * size;
82 }
83
84 u64
85 clib_mem_get_fd_page_size (int fd)
86 {
87   struct stat st = { 0 };
88   if (fstat (fd, &st) == -1)
89     return 0;
90   return st.st_blksize;
91 }
92
93 int
94 clib_mem_get_fd_log2_page_size (int fd)
95 {
96   return min_log2 (clib_mem_get_fd_page_size (fd));
97 }
98
99 void
100 clib_mem_vm_randomize_va (uword * requested_va, u32 log2_page_size)
101 {
102   u8 bit_mask = 15;
103
104   if (log2_page_size <= 12)
105     bit_mask = 15;
106   else if (log2_page_size > 12 && log2_page_size <= 16)
107     bit_mask = 3;
108   else
109     bit_mask = 0;
110
111   *requested_va +=
112     (clib_cpu_time_now () & bit_mask) * (1ull << log2_page_size);
113 }
114
115 #ifndef MFD_HUGETLB
116 #define MFD_HUGETLB 0x0004U
117 #endif
118
119 clib_error_t *
120 clib_mem_create_fd (char *name, int *fdp)
121 {
122   int fd;
123
124   ASSERT (name);
125
126   if ((fd = memfd_create (name, MFD_ALLOW_SEALING)) == -1)
127     return clib_error_return_unix (0, "memfd_create");
128
129   if ((fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK)) == -1)
130     {
131       close (fd);
132       return clib_error_return_unix (0, "fcntl (F_ADD_SEALS)");
133     }
134
135   *fdp = fd;
136   return 0;
137 }
138
139 clib_error_t *
140 clib_mem_create_hugetlb_fd (char *name, int *fdp)
141 {
142   clib_error_t *err = 0;
143   int fd = -1;
144   static int memfd_hugetlb_supported = 1;
145   char *mount_dir;
146   char template[] = "/tmp/hugepage_mount.XXXXXX";
147   u8 *filename;
148
149   ASSERT (name);
150
151   if (memfd_hugetlb_supported)
152     {
153       if ((fd = memfd_create (name, MFD_HUGETLB)) != -1)
154         goto done;
155
156       /* avoid further tries if memfd MFD_HUGETLB is not supported */
157       if (errno == EINVAL && strnlen (name, 256) <= 249)
158         memfd_hugetlb_supported = 0;
159     }
160
161   mount_dir = mkdtemp (template);
162   if (mount_dir == 0)
163     return clib_error_return_unix (0, "mkdtemp \'%s\'", template);
164
165   if (mount ("none", (char *) mount_dir, "hugetlbfs", 0, NULL))
166     {
167       rmdir ((char *) mount_dir);
168       err = clib_error_return_unix (0, "mount hugetlb directory '%s'",
169                                     mount_dir);
170     }
171
172   filename = format (0, "%s/%s%c", mount_dir, name, 0);
173   fd = open ((char *) filename, O_CREAT | O_RDWR, 0755);
174   umount2 ((char *) mount_dir, MNT_DETACH);
175   rmdir ((char *) mount_dir);
176
177   if (fd == -1)
178     err = clib_error_return_unix (0, "open");
179
180 done:
181   if (fd != -1)
182     fdp[0] = fd;
183   return err;
184 }
185
186 clib_error_t *
187 clib_mem_vm_ext_alloc (clib_mem_vm_alloc_t * a)
188 {
189   int fd = -1;
190   clib_error_t *err = 0;
191   void *addr = 0;
192   u8 *filename = 0;
193   int mmap_flags = 0;
194   int log2_page_size;
195   int n_pages;
196   int old_mpol = -1;
197   long unsigned int old_mask[16] = { 0 };
198
199   /* save old numa mem policy if needed */
200   if (a->flags & (CLIB_MEM_VM_F_NUMA_PREFER | CLIB_MEM_VM_F_NUMA_FORCE))
201     {
202       int rv;
203       rv = get_mempolicy (&old_mpol, old_mask, sizeof (old_mask) * 8 + 1,
204                           0, 0);
205
206       if (rv == -1)
207         {
208           if (a->numa_node != 0 && (a->flags & CLIB_MEM_VM_F_NUMA_FORCE) != 0)
209             {
210               err = clib_error_return_unix (0, "get_mempolicy");
211               goto error;
212             }
213           else
214             old_mpol = -1;
215         }
216     }
217
218   if (a->flags & CLIB_MEM_VM_F_LOCKED)
219     mmap_flags |= MAP_LOCKED;
220
221   /* if we are creating shared segment, we need file descriptor */
222   if (a->flags & CLIB_MEM_VM_F_SHARED)
223     {
224       mmap_flags |= MAP_SHARED;
225       /* if hugepages are needed we need to create mount point */
226       if (a->flags & CLIB_MEM_VM_F_HUGETLB)
227         {
228           if ((err = clib_mem_create_hugetlb_fd (a->name, &fd)))
229             goto error;
230
231           mmap_flags |= MAP_LOCKED;
232         }
233       else
234         {
235           if ((err = clib_mem_create_fd (a->name, &fd)))
236             goto error;
237         }
238
239       log2_page_size = clib_mem_get_fd_log2_page_size (fd);
240       if (log2_page_size == 0)
241         {
242           err = clib_error_return_unix (0, "cannot determine page size");
243           goto error;
244         }
245
246       if (a->requested_va)
247         {
248           clib_mem_vm_randomize_va (&a->requested_va, log2_page_size);
249           mmap_flags |= MAP_FIXED;
250         }
251     }
252   else                          /* not CLIB_MEM_VM_F_SHARED */
253     {
254       mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
255       if (a->flags & CLIB_MEM_VM_F_HUGETLB)
256         {
257           mmap_flags |= MAP_HUGETLB;
258           log2_page_size = 21;
259         }
260       else
261         {
262           log2_page_size = min_log2 (sysconf (_SC_PAGESIZE));
263         }
264     }
265
266   n_pages = ((a->size - 1) >> log2_page_size) + 1;
267
268   if (a->flags & CLIB_MEM_VM_F_HUGETLB_PREALLOC)
269     {
270       err = clib_sysfs_prealloc_hugepages (a->numa_node, log2_page_size,
271                                            n_pages);
272       if (err)
273         goto error;
274
275     }
276
277   if (fd != -1)
278     if ((ftruncate (fd, (u64) n_pages * (1 << log2_page_size))) == -1)
279       {
280         err = clib_error_return_unix (0, "ftruncate");
281         goto error;
282       }
283
284   if (old_mpol != -1)
285     {
286       int rv;
287       long unsigned int mask[16] = { 0 };
288       mask[0] = 1 << a->numa_node;
289       rv = set_mempolicy (MPOL_BIND, mask, sizeof (mask) * 8 + 1);
290       if (rv == -1 && a->numa_node != 0 &&
291           (a->flags & CLIB_MEM_VM_F_NUMA_FORCE) != 0)
292         {
293           err = clib_error_return_unix (0, "set_mempolicy");
294           goto error;
295         }
296     }
297
298   addr = mmap (uword_to_pointer (a->requested_va, void *), a->size,
299                (PROT_READ | PROT_WRITE), mmap_flags, fd, 0);
300   if (addr == MAP_FAILED)
301     {
302       err = clib_error_return_unix (0, "mmap");
303       goto error;
304     }
305
306   /* re-apply old numa memory policy */
307   if (old_mpol != -1 &&
308       set_mempolicy (old_mpol, old_mask, sizeof (old_mask) * 8 + 1) == -1)
309     {
310       err = clib_error_return_unix (0, "set_mempolicy");
311       goto error;
312     }
313
314   a->log2_page_size = log2_page_size;
315   a->n_pages = n_pages;
316   a->addr = addr;
317   a->fd = fd;
318   goto done;
319
320 error:
321   if (fd != -1)
322     close (fd);
323
324 done:
325   vec_free (filename);
326   return err;
327 }
328
329 void
330 clib_mem_vm_ext_free (clib_mem_vm_alloc_t * a)
331 {
332   if (a != 0)
333     {
334       clib_mem_vm_free (a->addr, 1ull << a->log2_page_size);
335       if (a->fd != -1)
336         close (a->fd);
337     }
338 }
339
340 u64 *
341 clib_mem_vm_get_paddr (void *mem, int log2_page_size, int n_pages)
342 {
343   int pagesize = sysconf (_SC_PAGESIZE);
344   int fd;
345   int i;
346   u64 *r = 0;
347
348   if ((fd = open ((char *) "/proc/self/pagemap", O_RDONLY)) == -1)
349     return 0;
350
351   for (i = 0; i < n_pages; i++)
352     {
353       u64 seek, pagemap = 0;
354       uword vaddr = pointer_to_uword (mem) + (((u64) i) << log2_page_size);
355       seek = ((u64) vaddr / pagesize) * sizeof (u64);
356       if (lseek (fd, seek, SEEK_SET) != seek)
357         goto done;
358
359       if (read (fd, &pagemap, sizeof (pagemap)) != (sizeof (pagemap)))
360         goto done;
361
362       if ((pagemap & (1ULL << 63)) == 0)
363         goto done;
364
365       pagemap &= pow2_mask (55);
366       vec_add1 (r, pagemap * pagesize);
367     }
368
369 done:
370   close (fd);
371   if (vec_len (r) != n_pages)
372     {
373       vec_free (r);
374       return 0;
375     }
376   return r;
377 }
378
379 clib_error_t *
380 clib_mem_vm_ext_map (clib_mem_vm_map_t * a)
381 {
382   int mmap_flags = MAP_SHARED;
383   void *addr;
384
385   if (a->requested_va)
386     mmap_flags |= MAP_FIXED;
387
388   addr = (void *) mmap (uword_to_pointer (a->requested_va, void *), a->size,
389                         PROT_READ | PROT_WRITE, mmap_flags, a->fd, 0);
390
391   if (addr == MAP_FAILED)
392     return clib_error_return_unix (0, "mmap");
393
394   a->addr = addr;
395   return 0;
396 }
397
398 /*
399  * fd.io coding-style-patch-verification: ON
400  *
401  * Local Variables:
402  * eval: (c-set-style "gnu")
403  * End:
404  */