vppinfra: fix coverity warnings
[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 #ifndef MFD_HUGETLB
50 #define MFD_HUGETLB 0x0004U
51 #endif
52
53 #ifndef MAP_HUGE_SHIFT
54 #define MAP_HUGE_SHIFT 26
55 #endif
56
57 #ifndef MFD_HUGE_SHIFT
58 #define MFD_HUGE_SHIFT 26
59 #endif
60
61 #ifndef MAP_FIXED_NOREPLACE
62 #define MAP_FIXED_NOREPLACE 0x100000
63 #endif
64
65 uword
66 clib_mem_get_default_hugepage_size (void)
67 {
68   unformat_input_t input;
69   static u32 size = 0;
70   int fd;
71
72   if (size)
73     goto done;
74
75   /*
76    * If the kernel doesn't support hugepages, /proc/meminfo won't
77    * say anything about it. Use the regular page size as a default.
78    */
79   size = clib_mem_get_page_size () / 1024;
80
81   if ((fd = open ("/proc/meminfo", 0)) == -1)
82     return 0;
83
84   unformat_init_clib_file (&input, fd);
85
86   while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
87     {
88       if (unformat (&input, "Hugepagesize:%_%u kB", &size))
89         ;
90       else
91         unformat_skip_line (&input);
92     }
93   unformat_free (&input);
94   close (fd);
95 done:
96   return 1024ULL * size;
97 }
98
99 static clib_mem_page_sz_t
100 legacy_get_log2_default_hugepage_size (void)
101 {
102   clib_mem_page_sz_t log2_page_size = CLIB_MEM_PAGE_SZ_UNKNOWN;
103   FILE *fp;
104   char tmp[33] = { };
105
106   if ((fp = fopen ("/proc/meminfo", "r")) == NULL)
107     return CLIB_MEM_PAGE_SZ_UNKNOWN;
108
109   while (fscanf (fp, "%32s", tmp) > 0)
110     if (strncmp ("Hugepagesize:", tmp, 13) == 0)
111       {
112         u32 size;
113         if (fscanf (fp, "%u", &size) > 0)
114           log2_page_size = 10 + min_log2 (size);
115         break;
116       }
117
118   fclose (fp);
119   return log2_page_size;
120 }
121
122 void
123 clib_mem_main_init ()
124 {
125   clib_mem_main_t *mm = &clib_mem_main;
126   uword page_size;
127   void *va;
128   int fd;
129
130   if (mm->log2_page_sz != CLIB_MEM_PAGE_SZ_UNKNOWN)
131     return;
132
133   /* system page size */
134   page_size = sysconf (_SC_PAGESIZE);
135   mm->log2_page_sz = min_log2 (page_size);
136
137   /* default system hugeppage size */
138   if ((fd = memfd_create ("test", MFD_HUGETLB)) != -1)
139     {
140       mm->log2_default_hugepage_sz = clib_mem_get_fd_log2_page_size (fd);
141       close (fd);
142     }
143   else                          /* likely kernel older than 4.14 */
144     mm->log2_default_hugepage_sz = legacy_get_log2_default_hugepage_size ();
145
146   /* numa nodes */
147   va = mmap (0, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE |
148              MAP_ANONYMOUS, -1, 0);
149   if (va == MAP_FAILED)
150     return;
151
152   if (mlock (va, page_size))
153     goto done;
154
155   for (int i = 0; i < CLIB_MAX_NUMAS; i++)
156     {
157       int status;
158       if (move_pages (0, 1, &va, &i, &status, 0) == 0)
159         mm->numa_node_bitmap |= 1ULL << i;
160     }
161
162 done:
163   munmap (va, page_size);
164 }
165
166 u64
167 clib_mem_get_fd_page_size (int fd)
168 {
169   struct stat st = { 0 };
170   if (fstat (fd, &st) == -1)
171     return 0;
172   return st.st_blksize;
173 }
174
175 clib_mem_page_sz_t
176 clib_mem_get_fd_log2_page_size (int fd)
177 {
178   uword page_size = clib_mem_get_fd_page_size (fd);
179   return page_size ? min_log2 (page_size) : CLIB_MEM_PAGE_SZ_UNKNOWN;
180 }
181
182 void
183 clib_mem_vm_randomize_va (uword * requested_va,
184                           clib_mem_page_sz_t log2_page_size)
185 {
186   u8 bit_mask = 15;
187
188   if (log2_page_size <= 12)
189     bit_mask = 15;
190   else if (log2_page_size > 12 && log2_page_size <= 16)
191     bit_mask = 3;
192   else
193     bit_mask = 0;
194
195   *requested_va +=
196     (clib_cpu_time_now () & bit_mask) * (1ull << log2_page_size);
197 }
198
199 static int
200 legacy_memfd_create (u8 * name)
201 {
202   clib_mem_main_t *mm = &clib_mem_main;
203   int fd = -1;
204   char *mount_dir;
205   u8 *temp;
206   u8 *filename;
207
208   /*
209    * Since mkdtemp will modify template string "/tmp/hugepage_mount.XXXXXX",
210    * it must not be a string constant, but should be declared as
211    * a character array.
212    */
213   temp = format (0, "/tmp/hugepage_mount.XXXXXX%c", 0);
214
215   /* create mount directory */
216   if ((mount_dir = mkdtemp ((char *) temp)) == 0)
217     {
218       vec_free (temp);
219       vec_reset_length (mm->error);
220       mm->error = clib_error_return_unix (mm->error, "mkdtemp");
221       return CLIB_MEM_ERROR;
222     }
223
224   if (mount ("none", mount_dir, "hugetlbfs", 0, NULL))
225     {
226       vec_free (temp);
227       rmdir ((char *) mount_dir);
228       vec_reset_length (mm->error);
229       mm->error = clib_error_return_unix (mm->error, "mount");
230       return CLIB_MEM_ERROR;
231     }
232
233   filename = format (0, "%s/%s%c", mount_dir, name, 0);
234
235   if ((fd = open ((char *) filename, O_CREAT | O_RDWR, 0755)) == -1)
236     {
237       vec_reset_length (mm->error);
238       mm->error = clib_error_return_unix (mm->error, "mkdtemp");
239     }
240
241   umount2 ((char *) mount_dir, MNT_DETACH);
242   rmdir ((char *) mount_dir);
243   vec_free (filename);
244   vec_free (temp);
245
246   return fd;
247 }
248
249 int
250 clib_mem_vm_create_fd (clib_mem_page_sz_t log2_page_size, char *fmt, ...)
251 {
252   clib_mem_main_t *mm = &clib_mem_main;
253   int fd;
254   unsigned int memfd_flags;
255   va_list va;
256   u8 *s = 0;
257
258   if (log2_page_size == mm->log2_page_sz)
259     log2_page_size = CLIB_MEM_PAGE_SZ_DEFAULT;
260   else if (log2_page_size == mm->log2_default_hugepage_sz)
261     log2_page_size = CLIB_MEM_PAGE_SZ_DEFAULT_HUGE;
262
263   switch (log2_page_size)
264     {
265     case CLIB_MEM_PAGE_SZ_UNKNOWN:
266       return CLIB_MEM_ERROR;
267     case CLIB_MEM_PAGE_SZ_DEFAULT:
268       memfd_flags = MFD_ALLOW_SEALING;
269       break;
270     case CLIB_MEM_PAGE_SZ_DEFAULT_HUGE:
271       memfd_flags = MFD_HUGETLB;
272       break;
273     default:
274       memfd_flags = MFD_HUGETLB | log2_page_size << MFD_HUGE_SHIFT;
275     }
276
277   va_start (va, fmt);
278   s = va_format (0, fmt, &va);
279   va_end (va);
280
281   /* memfd_create maximum string size is 249 chars without trailing zero */
282   if (vec_len (s) > 249)
283     _vec_len (s) = 249;
284   vec_add1 (s, 0);
285
286   /* memfd_create introduced in kernel 3.17, we don't support older kernels */
287   fd = memfd_create ((char *) s, memfd_flags);
288
289   /* kernel versions < 4.14 does not support memfd_create for huge pages */
290   if (fd == -1 && errno == EINVAL &&
291       log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT_HUGE)
292     {
293       fd = legacy_memfd_create (s);
294     }
295   else if (fd == -1)
296     {
297       vec_reset_length (mm->error);
298       mm->error = clib_error_return_unix (mm->error, "memfd_create");
299       vec_free (s);
300       return CLIB_MEM_ERROR;
301     }
302
303   vec_free (s);
304
305   if ((memfd_flags & MFD_ALLOW_SEALING) &&
306       ((fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK)) == -1))
307     {
308       vec_reset_length (mm->error);
309       mm->error = clib_error_return_unix (mm->error, "fcntl (F_ADD_SEALS)");
310       close (fd);
311       return CLIB_MEM_ERROR;
312     }
313
314   return fd;
315 }
316
317 uword
318 clib_mem_vm_reserve (uword start, uword size, clib_mem_page_sz_t log2_page_sz)
319 {
320   clib_mem_main_t *mm = &clib_mem_main;
321   uword pagesize = 1ULL << log2_page_sz;
322   uword sys_page_sz = 1ULL << mm->log2_page_sz;
323   uword n_bytes;
324   void *base = 0, *p;
325
326   size = round_pow2 (size, pagesize);
327
328   /* in adition of requested reservation, we also rserve one system page
329    * (typically 4K) adjacent to the start off reservation */
330
331   if (start)
332     {
333       /* start address is provided, so we just need to make sure we are not
334        * replacing existing map */
335       if (start & pow2_mask (log2_page_sz))
336         return ~0;
337
338       base = (void *) start - sys_page_sz;
339       base = mmap (base, size + sys_page_sz, PROT_NONE,
340                    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0);
341       return (base == MAP_FAILED) ? ~0 : start;
342     }
343
344   /* to make sure that we get reservation aligned to page_size we need to
345    * request one additional page as mmap will return us address which is
346    * aligned only to system page size */
347   base = mmap (0, size + pagesize, PROT_NONE,
348                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
349
350   if (base == MAP_FAILED)
351     return ~0;
352
353   /* return additional space at the end of allocation */
354   p = base + size + pagesize;
355   n_bytes = (uword) p & pow2_mask (log2_page_sz);
356   if (n_bytes)
357     {
358       p -= n_bytes;
359       munmap (p, n_bytes);
360     }
361
362   /* return additional space at the start of allocation */
363   n_bytes = pagesize - sys_page_sz - n_bytes;
364   if (n_bytes)
365     {
366       munmap (base, n_bytes);
367       base += n_bytes;
368     }
369
370   return (uword) base + sys_page_sz;
371 }
372
373 clib_mem_vm_map_hdr_t *
374 clib_mem_vm_get_next_map_hdr (clib_mem_vm_map_hdr_t * hdr)
375 {
376   clib_mem_main_t *mm = &clib_mem_main;
377   uword sys_page_sz = 1ULL << mm->log2_page_sz;
378   clib_mem_vm_map_hdr_t *next;
379   if (hdr == 0)
380     {
381       hdr = mm->first_map;
382       if (hdr)
383         mprotect (hdr, sys_page_sz, PROT_READ);
384       return hdr;
385     }
386   next = hdr->next;
387   mprotect (hdr, sys_page_sz, PROT_NONE);
388   if (next)
389     mprotect (next, sys_page_sz, PROT_READ);
390   return next;
391 }
392
393 void *
394 clib_mem_vm_map_internal (void *base, clib_mem_page_sz_t log2_page_sz,
395                           uword size, int fd, uword offset, char *name)
396 {
397   clib_mem_main_t *mm = &clib_mem_main;
398   clib_mem_vm_map_hdr_t *hdr;
399   uword sys_page_sz = 1ULL << mm->log2_page_sz;
400   int mmap_flags = MAP_FIXED, is_huge = 0;
401
402   if (fd != -1)
403     {
404       mmap_flags |= MAP_SHARED;
405       log2_page_sz = clib_mem_get_fd_log2_page_size (fd);
406       if (log2_page_sz > mm->log2_page_sz)
407         is_huge = 1;
408     }
409   else
410     {
411       mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
412
413       if (log2_page_sz == mm->log2_page_sz)
414         log2_page_sz = CLIB_MEM_PAGE_SZ_DEFAULT;
415
416       switch (log2_page_sz)
417         {
418         case CLIB_MEM_PAGE_SZ_UNKNOWN:
419           /* will fail later */
420           break;
421         case CLIB_MEM_PAGE_SZ_DEFAULT:
422           log2_page_sz = mm->log2_page_sz;
423           break;
424         case CLIB_MEM_PAGE_SZ_DEFAULT_HUGE:
425           mmap_flags |= MAP_HUGETLB;
426           log2_page_sz = mm->log2_default_hugepage_sz;
427           is_huge = 1;
428           break;
429         default:
430           mmap_flags |= MAP_HUGETLB;
431           mmap_flags |= log2_page_sz << MAP_HUGE_SHIFT;
432           is_huge = 1;
433         }
434     }
435
436   if (log2_page_sz == CLIB_MEM_PAGE_SZ_UNKNOWN)
437     return CLIB_MEM_VM_MAP_FAILED;
438
439   size = round_pow2 (size, 1ULL << log2_page_sz);
440
441   base = (void *) clib_mem_vm_reserve ((uword) base, size, log2_page_sz);
442
443   if (base == (void *) ~0)
444     return CLIB_MEM_VM_MAP_FAILED;
445
446   base = mmap (base, size, PROT_READ | PROT_WRITE, mmap_flags, fd, offset);
447
448   if (base == MAP_FAILED)
449     return CLIB_MEM_VM_MAP_FAILED;
450
451   if (is_huge && (mlock (base, size) != 0))
452     {
453       munmap (base, size);
454       return CLIB_MEM_VM_MAP_FAILED;
455     }
456
457   hdr = mmap (base - sys_page_sz, sys_page_sz, PROT_READ | PROT_WRITE,
458               MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
459
460   if (hdr != base - sys_page_sz)
461     {
462       munmap (base, size);
463       return CLIB_MEM_VM_MAP_FAILED;
464     }
465
466   if (mm->last_map)
467     {
468       mprotect (mm->last_map, sys_page_sz, PROT_READ | PROT_WRITE);
469       mm->last_map->next = hdr;
470       mprotect (mm->last_map, sys_page_sz, PROT_NONE);
471     }
472   else
473     mm->first_map = hdr;
474
475   hdr->next = 0;
476   hdr->prev = mm->last_map;
477   mm->last_map = hdr;
478
479   hdr->base_addr = (uword) base;
480   hdr->log2_page_sz = log2_page_sz;
481   hdr->num_pages = size >> log2_page_sz;
482   hdr->fd = fd;
483   snprintf (hdr->name, CLIB_VM_MAP_HDR_NAME_MAX_LEN - 1, "%s", (char *) name);
484   hdr->name[CLIB_VM_MAP_HDR_NAME_MAX_LEN - 1] = 0;
485   mprotect (hdr, sys_page_sz, PROT_NONE);
486
487   CLIB_MEM_UNPOISON (base, size);
488   return base;
489 }
490
491 int
492 clib_mem_vm_unmap (void *base)
493 {
494   clib_mem_main_t *mm = &clib_mem_main;
495   uword size, sys_page_sz = 1ULL << mm->log2_page_sz;
496   clib_mem_vm_map_hdr_t *hdr = base - sys_page_sz;;
497
498   if (mprotect (hdr, sys_page_sz, PROT_READ | PROT_WRITE) != 0)
499     return CLIB_MEM_ERROR;
500
501   size = hdr->num_pages << hdr->log2_page_sz;
502   if (munmap ((void *) hdr->base_addr, size) != 0)
503     return CLIB_MEM_ERROR;
504
505   if (hdr->next)
506     {
507       mprotect (hdr->next, sys_page_sz, PROT_READ | PROT_WRITE);
508       hdr->next->prev = hdr->prev;
509       mprotect (hdr->next, sys_page_sz, PROT_NONE);
510     }
511   else
512     mm->last_map = hdr->prev;
513
514   if (hdr->prev)
515     {
516       mprotect (hdr->prev, sys_page_sz, PROT_READ | PROT_WRITE);
517       hdr->prev->next = hdr->next;
518       mprotect (hdr->prev, sys_page_sz, PROT_NONE);
519     }
520   else
521     mm->first_map = hdr->next;
522
523   if (munmap (hdr, sys_page_sz) != 0)
524     return CLIB_MEM_ERROR;
525
526   return 0;
527 }
528
529 void
530 clib_mem_get_page_stats (void *start, clib_mem_page_sz_t log2_page_size,
531                          uword n_pages, clib_mem_page_stats_t * stats)
532 {
533   int i, *status = 0;
534   void **ptr = 0;
535
536   log2_page_size = clib_mem_log2_page_size_validate (log2_page_size);
537
538   vec_validate (status, n_pages - 1);
539   vec_validate (ptr, n_pages - 1);
540
541   for (i = 0; i < n_pages; i++)
542     ptr[i] = start + (i << log2_page_size);
543
544   clib_memset (stats, 0, sizeof (clib_mem_page_stats_t));
545   stats->total = n_pages;
546   stats->log2_page_sz = log2_page_size;
547
548   if (move_pages (0, n_pages, ptr, 0, status, 0) != 0)
549     {
550       stats->unknown = n_pages;
551       return;
552     }
553
554   for (i = 0; i < n_pages; i++)
555     {
556       if (status[i] >= 0 && status[i] < CLIB_MAX_NUMAS)
557         {
558           stats->mapped++;
559           stats->per_numa[status[i]]++;
560         }
561       else if (status[i] == -EFAULT)
562         stats->not_mapped++;
563       else
564         stats->unknown++;
565     }
566 }
567
568
569 u64 *
570 clib_mem_vm_get_paddr (void *mem, clib_mem_page_sz_t log2_page_size,
571                        int n_pages)
572 {
573   int pagesize = sysconf (_SC_PAGESIZE);
574   int fd;
575   int i;
576   u64 *r = 0;
577
578   log2_page_size = clib_mem_log2_page_size_validate (log2_page_size);
579
580   if ((fd = open ((char *) "/proc/self/pagemap", O_RDONLY)) == -1)
581     return 0;
582
583   for (i = 0; i < n_pages; i++)
584     {
585       u64 seek, pagemap = 0;
586       uword vaddr = pointer_to_uword (mem) + (((u64) i) << log2_page_size);
587       seek = ((u64) vaddr / pagesize) * sizeof (u64);
588       if (lseek (fd, seek, SEEK_SET) != seek)
589         goto done;
590
591       if (read (fd, &pagemap, sizeof (pagemap)) != (sizeof (pagemap)))
592         goto done;
593
594       if ((pagemap & (1ULL << 63)) == 0)
595         goto done;
596
597       pagemap &= pow2_mask (55);
598       vec_add1 (r, pagemap * pagesize);
599     }
600
601 done:
602   close (fd);
603   if (vec_len (r) != n_pages)
604     {
605       vec_free (r);
606       return 0;
607     }
608   return r;
609 }
610
611 int
612 clib_mem_set_numa_affinity (u8 numa_node, int force)
613 {
614   clib_mem_main_t *mm = &clib_mem_main;
615   long unsigned int mask[16] = { 0 };
616   int mask_len = sizeof (mask) * 8 + 1;
617
618   /* no numa support */
619   if (mm->numa_node_bitmap == 0)
620     {
621       if (numa_node)
622         {
623           vec_reset_length (mm->error);
624           mm->error = clib_error_return (mm->error, "%s: numa not supported",
625                                          (char *) __func__);
626           return CLIB_MEM_ERROR;
627         }
628       else
629         return 0;
630     }
631
632   mask[0] = 1 << numa_node;
633
634   if (set_mempolicy (force ? MPOL_BIND : MPOL_PREFERRED, mask, mask_len))
635     goto error;
636
637   vec_reset_length (mm->error);
638   return 0;
639
640 error:
641   vec_reset_length (mm->error);
642   mm->error = clib_error_return_unix (mm->error, (char *) __func__);
643   return CLIB_MEM_ERROR;
644 }
645
646 int
647 clib_mem_set_default_numa_affinity ()
648 {
649   clib_mem_main_t *mm = &clib_mem_main;
650
651   if (set_mempolicy (MPOL_DEFAULT, 0, 0))
652     {
653       vec_reset_length (mm->error);
654       mm->error = clib_error_return_unix (mm->error, (char *) __func__);
655       return CLIB_MEM_ERROR;
656     }
657   return 0;
658 }
659
660 /*
661  * fd.io coding-style-patch-verification: ON
662  *
663  * Local Variables:
664  * eval: (c-set-style "gnu")
665  * End:
666  */