253ae87845b3f216e71bba87357e867c687efd77
[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 u64
50 clib_mem_vm_get_page_size (int fd)
51 {
52   struct stat st = { 0 };
53   if (fstat (fd, &st) == -1)
54     return 0;
55   return st.st_blksize;
56 }
57
58 int
59 clib_mem_vm_get_log2_page_size (int fd)
60 {
61   return min_log2 (clib_mem_vm_get_page_size (fd));
62 }
63
64 void
65 clib_mem_vm_randomize_va (uword * requested_va, u32 log2_page_size)
66 {
67   u8 bit_mask = 15;
68
69   if (log2_page_size <= 12)
70     bit_mask = 15;
71   else if (log2_page_size > 12 && log2_page_size <= 16)
72     bit_mask = 3;
73   else
74     bit_mask = 0;
75
76   *requested_va +=
77     (clib_cpu_time_now () & bit_mask) * (1ull << log2_page_size);
78 }
79
80 #ifndef MFD_HUGETLB
81 #define MFD_HUGETLB 0x0004U
82 #endif
83
84 clib_error_t *
85 clib_mem_create_hugetlb_fd (char *name, int *fdp)
86 {
87   clib_error_t *err = 0;
88   int fd = -1;
89   static int memfd_hugetlb_supported = 1;
90   char *mount_dir;
91   char template[] = "/tmp/hugepage_mount.XXXXXX";
92   u8 *filename;
93
94   ASSERT (name);
95
96   if (memfd_hugetlb_supported)
97     {
98       if ((fd = memfd_create (name, MFD_HUGETLB)) != -1)
99         goto done;
100
101       /* avoid further tries if memfd MFD_HUGETLB is not supported */
102       if (errno == EINVAL && strnlen (name, 256) <= 249)
103         memfd_hugetlb_supported = 0;
104     }
105
106   mount_dir = mkdtemp (template);
107   if (mount_dir == 0)
108     return clib_error_return_unix (0, "mkdtemp \'%s\'", template);
109
110   if (mount ("none", (char *) mount_dir, "hugetlbfs", 0, NULL))
111     {
112       rmdir ((char *) mount_dir);
113       err = clib_error_return_unix (0, "mount hugetlb directory '%s'",
114                                     mount_dir);
115     }
116
117   filename = format (0, "%s/%s%c", mount_dir, name, 0);
118   fd = open ((char *) filename, O_CREAT | O_RDWR, 0755);
119   umount2 ((char *) mount_dir, MNT_DETACH);
120   rmdir ((char *) mount_dir);
121
122   if (fd == -1)
123     err = clib_error_return_unix (0, "open");
124
125 done:
126   if (fd != -1)
127     fdp[0] = fd;
128   return err;
129 }
130
131 clib_error_t *
132 clib_mem_vm_ext_alloc (clib_mem_vm_alloc_t * a)
133 {
134   int fd = -1;
135   clib_error_t *err = 0;
136   void *addr = 0;
137   u8 *filename = 0;
138   int mmap_flags = 0;
139   int log2_page_size;
140   int n_pages;
141   int old_mpol = -1;
142   long unsigned int old_mask[16] = { 0 };
143
144   /* save old numa mem policy if needed */
145   if (a->flags & (CLIB_MEM_VM_F_NUMA_PREFER | CLIB_MEM_VM_F_NUMA_FORCE))
146     {
147       int rv;
148       rv = get_mempolicy (&old_mpol, old_mask, sizeof (old_mask) * 8 + 1,
149                           0, 0);
150
151       if (rv == -1)
152         {
153           if (a->numa_node != 0 && (a->flags & CLIB_MEM_VM_F_NUMA_FORCE) != 0)
154             {
155               err = clib_error_return_unix (0, "get_mempolicy");
156               goto error;
157             }
158           else
159             old_mpol = -1;
160         }
161     }
162
163   if (a->flags & CLIB_MEM_VM_F_LOCKED)
164     mmap_flags |= MAP_LOCKED;
165
166   /* if we are creating shared segment, we need file descriptor */
167   if (a->flags & CLIB_MEM_VM_F_SHARED)
168     {
169       mmap_flags |= MAP_SHARED;
170       /* if hugepages are needed we need to create mount point */
171       if (a->flags & CLIB_MEM_VM_F_HUGETLB)
172         {
173           if ((err = clib_mem_create_hugetlb_fd (a->name, &fd)))
174             goto error;
175
176           mmap_flags |= MAP_LOCKED;
177         }
178       else
179         {
180           if ((fd = memfd_create (a->name, MFD_ALLOW_SEALING)) == -1)
181             {
182               err = clib_error_return_unix (0, "memfd_create");
183               goto error;
184             }
185
186           if ((fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK)) == -1)
187             {
188               err = clib_error_return_unix (0, "fcntl (F_ADD_SEALS)");
189               goto error;
190             }
191         }
192
193       log2_page_size = clib_mem_vm_get_log2_page_size (fd);
194       if (log2_page_size == 0)
195         {
196           err = clib_error_return_unix (0, "cannot determine page size");
197           goto error;
198         }
199
200       if (a->requested_va)
201         {
202           clib_mem_vm_randomize_va (&a->requested_va, log2_page_size);
203           mmap_flags |= MAP_FIXED;
204         }
205     }
206   else                          /* not CLIB_MEM_VM_F_SHARED */
207     {
208       mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
209       if (a->flags & CLIB_MEM_VM_F_HUGETLB)
210         {
211           mmap_flags |= MAP_HUGETLB;
212           log2_page_size = 21;
213         }
214       else
215         {
216           log2_page_size = min_log2 (sysconf (_SC_PAGESIZE));
217         }
218     }
219
220   n_pages = ((a->size - 1) >> log2_page_size) + 1;
221
222   if (a->flags & CLIB_MEM_VM_F_HUGETLB_PREALLOC)
223     {
224       err = clib_sysfs_prealloc_hugepages (a->numa_node,
225                                            1 << (log2_page_size - 10),
226                                            n_pages);
227       if (err)
228         goto error;
229
230     }
231
232   if (fd != -1)
233     if ((ftruncate (fd, (u64) n_pages * (1 << log2_page_size))) == -1)
234       {
235         err = clib_error_return_unix (0, "ftruncate");
236         goto error;
237       }
238
239   if (old_mpol != -1)
240     {
241       int rv;
242       long unsigned int mask[16] = { 0 };
243       mask[0] = 1 << a->numa_node;
244       rv = set_mempolicy (MPOL_BIND, mask, sizeof (mask) * 8 + 1);
245       if (rv == -1 && a->numa_node != 0 &&
246           (a->flags & CLIB_MEM_VM_F_NUMA_FORCE) != 0)
247         {
248           err = clib_error_return_unix (0, "set_mempolicy");
249           goto error;
250         }
251     }
252
253   addr = mmap (uword_to_pointer (a->requested_va, void *), a->size,
254                (PROT_READ | PROT_WRITE), mmap_flags, fd, 0);
255   if (addr == MAP_FAILED)
256     {
257       err = clib_error_return_unix (0, "mmap");
258       goto error;
259     }
260
261   /* re-apply old numa memory policy */
262   if (old_mpol != -1 &&
263       set_mempolicy (old_mpol, old_mask, sizeof (old_mask) * 8 + 1) == -1)
264     {
265       err = clib_error_return_unix (0, "set_mempolicy");
266       goto error;
267     }
268
269   a->log2_page_size = log2_page_size;
270   a->n_pages = n_pages;
271   a->addr = addr;
272   a->fd = fd;
273   goto done;
274
275 error:
276   if (fd != -1)
277     close (fd);
278
279 done:
280   vec_free (filename);
281   return err;
282 }
283
284 void
285 clib_mem_vm_ext_free (clib_mem_vm_alloc_t * a)
286 {
287   if (a != 0)
288     {
289       clib_mem_vm_free (a->addr, 1ull << a->log2_page_size);
290       if (a->fd != -1)
291         close (a->fd);
292     }
293 }
294
295 u64 *
296 clib_mem_vm_get_paddr (void *mem, int log2_page_size, int n_pages)
297 {
298   int pagesize = sysconf (_SC_PAGESIZE);
299   int fd;
300   int i;
301   u64 *r = 0;
302
303   if ((fd = open ((char *) "/proc/self/pagemap", O_RDONLY)) == -1)
304     return 0;
305
306   for (i = 0; i < n_pages; i++)
307     {
308       u64 seek, pagemap = 0;
309       uword vaddr = pointer_to_uword (mem) + (((u64) i) << log2_page_size);
310       seek = ((u64) vaddr / pagesize) * sizeof (u64);
311       if (lseek (fd, seek, SEEK_SET) != seek)
312         goto done;
313
314       if (read (fd, &pagemap, sizeof (pagemap)) != (sizeof (pagemap)))
315         goto done;
316
317       if ((pagemap & (1ULL << 63)) == 0)
318         goto done;
319
320       pagemap &= pow2_mask (55);
321       vec_add1 (r, pagemap * pagesize);
322     }
323
324 done:
325   close (fd);
326   if (vec_len (r) != n_pages)
327     {
328       vec_free (r);
329       return 0;
330     }
331   return r;
332 }
333
334 clib_error_t *
335 clib_mem_vm_ext_map (clib_mem_vm_map_t * a)
336 {
337   int mmap_flags = MAP_SHARED;
338   void *addr;
339
340   if (a->requested_va)
341     mmap_flags |= MAP_FIXED;
342
343   addr = (void *) mmap (uword_to_pointer (a->requested_va, void *), a->size,
344                         PROT_READ | PROT_WRITE, mmap_flags, a->fd, 0);
345
346   if (addr == MAP_FAILED)
347     return clib_error_return_unix (0, "mmap");
348
349   a->addr = addr;
350   return 0;
351 }
352
353 /*
354  * fd.io coding-style-patch-verification: ON
355  *
356  * Local Variables:
357  * eval: (c-set-style "gnu")
358  * End:
359  */