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