vppinfra: make default hugepage size configurable
[vpp.git] / src / vppinfra / mem.h
1 /*
2  * Copyright (c) 2015 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   Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
17
18   Permission is hereby granted, free of charge, to any person obtaining
19   a copy of this software and associated documentation files (the
20   "Software"), to deal in the Software without restriction, including
21   without limitation the rights to use, copy, modify, merge, publish,
22   distribute, sublicense, and/or sell copies of the Software, and to
23   permit persons to whom the Software is furnished to do so, subject to
24   the following conditions:
25
26   The above copyright notice and this permission notice shall be
27   included in all copies or substantial portions of the Software.
28
29   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37
38 #ifndef _included_clib_mem_h
39 #define _included_clib_mem_h
40
41 #include <stdarg.h>
42 #include <unistd.h>
43 #include <sys/mman.h>
44
45 #include <vppinfra/clib.h>      /* uword, etc */
46 #include <vppinfra/clib_error.h>
47
48 #include <vppinfra/os.h>
49 #include <vppinfra/string.h>    /* memcpy, clib_memset */
50 #include <vppinfra/sanitizer.h>
51
52 #define CLIB_MAX_MHEAPS 256
53 #define CLIB_MAX_NUMAS 16
54 #define CLIB_MEM_VM_MAP_FAILED ((void *) ~0)
55 #define CLIB_MEM_ERROR (-1)
56
57 typedef enum
58 {
59   CLIB_MEM_PAGE_SZ_UNKNOWN = 0,
60   CLIB_MEM_PAGE_SZ_DEFAULT = 1,
61   CLIB_MEM_PAGE_SZ_DEFAULT_HUGE = 2,
62   CLIB_MEM_PAGE_SZ_4K = 12,
63   CLIB_MEM_PAGE_SZ_16K = 14,
64   CLIB_MEM_PAGE_SZ_64K = 16,
65   CLIB_MEM_PAGE_SZ_1M = 20,
66   CLIB_MEM_PAGE_SZ_2M = 21,
67   CLIB_MEM_PAGE_SZ_16M = 24,
68   CLIB_MEM_PAGE_SZ_32M = 25,
69   CLIB_MEM_PAGE_SZ_512M = 29,
70   CLIB_MEM_PAGE_SZ_1G = 30,
71   CLIB_MEM_PAGE_SZ_16G = 34,
72 } clib_mem_page_sz_t;
73
74 typedef struct _clib_mem_vm_map_hdr
75 {
76   /* base address */
77   uword base_addr;
78
79   /* number of pages */
80   uword num_pages;
81
82   /* page size (log2) */
83   clib_mem_page_sz_t log2_page_sz;
84
85   /* file descriptor, -1 if memory is not shared */
86   int fd;
87
88   /* allocation mame */
89 #define CLIB_VM_MAP_HDR_NAME_MAX_LEN 64
90   char name[CLIB_VM_MAP_HDR_NAME_MAX_LEN];
91
92   /* linked list */
93   struct _clib_mem_vm_map_hdr *prev, *next;
94 } clib_mem_vm_map_hdr_t;
95
96 #define foreach_clib_mem_heap_flag \
97   _(0, LOCKED, "locked") \
98   _(1, UNMAP_ON_DESTROY, "unmap-on-destroy")
99
100 typedef enum
101 {
102 #define _(i, v, s) CLIB_MEM_HEAP_F_##v = (1 << i),
103   foreach_clib_mem_heap_flag
104 #undef _
105 } clib_mem_heap_flag_t;
106
107 typedef struct
108 {
109   /* base address */
110   void *base;
111
112   /* dlmalloc mspace */
113   void *mspace;
114
115   /* heap size */
116   uword size;
117
118   /* page size (log2) */
119   clib_mem_page_sz_t log2_page_sz:8;
120
121   /* flags */
122   clib_mem_heap_flag_t flags:8;
123
124   /* name - _MUST_ be last */
125   char name[0];
126 } clib_mem_heap_t;
127
128 typedef struct
129 {
130   /* log2 system page size */
131   clib_mem_page_sz_t log2_page_sz;
132
133   /* log2 default hugepage size */
134   clib_mem_page_sz_t log2_default_hugepage_sz;
135
136   /* log2 system default hugepage size */
137   clib_mem_page_sz_t log2_sys_default_hugepage_sz;
138
139   /* bitmap of available numa nodes */
140   u32 numa_node_bitmap;
141
142   /* per CPU heaps */
143   void *per_cpu_mheaps[CLIB_MAX_MHEAPS];
144
145   /* per NUMA heaps */
146   void *per_numa_mheaps[CLIB_MAX_NUMAS];
147
148   /* memory maps */
149   clib_mem_vm_map_hdr_t *first_map, *last_map;
150
151   /* map lock */
152   u8 map_lock;
153
154   /* last error */
155   clib_error_t *error;
156 } clib_mem_main_t;
157
158 extern clib_mem_main_t clib_mem_main;
159
160 /* Unspecified NUMA socket */
161 #define VEC_NUMA_UNSPECIFIED (0xFF)
162
163 always_inline clib_mem_heap_t *
164 clib_mem_get_per_cpu_heap (void)
165 {
166   int cpu = os_get_thread_index ();
167   return clib_mem_main.per_cpu_mheaps[cpu];
168 }
169
170 always_inline void *
171 clib_mem_set_per_cpu_heap (void *new_heap)
172 {
173   int cpu = os_get_thread_index ();
174   void *old = clib_mem_main.per_cpu_mheaps[cpu];
175   clib_mem_main.per_cpu_mheaps[cpu] = new_heap;
176   return old;
177 }
178
179 always_inline void *
180 clib_mem_get_per_numa_heap (u32 numa_id)
181 {
182   ASSERT (numa_id < ARRAY_LEN (clib_mem_main.per_numa_mheaps));
183   return clib_mem_main.per_numa_mheaps[numa_id];
184 }
185
186 always_inline void *
187 clib_mem_set_per_numa_heap (void *new_heap)
188 {
189   int numa = os_get_numa_index ();
190   void *old = clib_mem_main.per_numa_mheaps[numa];
191   clib_mem_main.per_numa_mheaps[numa] = new_heap;
192   return old;
193 }
194
195 always_inline void
196 clib_mem_set_thread_index (void)
197 {
198   /*
199    * Find an unused slot in the per-cpu-mheaps array,
200    * and grab it for this thread. We need to be able to
201    * push/pop the thread heap without affecting other thread(s).
202    */
203   int i;
204   if (__os_thread_index != 0)
205     return;
206   for (i = 0; i < ARRAY_LEN (clib_mem_main.per_cpu_mheaps); i++)
207     if (clib_atomic_bool_cmp_and_swap (&clib_mem_main.per_cpu_mheaps[i],
208                                        0, clib_mem_main.per_cpu_mheaps[0]))
209       {
210         os_set_thread_index (i);
211         break;
212       }
213   ASSERT (__os_thread_index > 0);
214 }
215
216 always_inline uword
217 clib_mem_size_nocheck (void *p)
218 {
219   size_t mspace_usable_size_with_delta (const void *p);
220   return mspace_usable_size_with_delta (p);
221 }
222
223 /* Memory allocator which may call os_out_of_memory() if it fails */
224 always_inline void *
225 clib_mem_alloc_aligned_at_offset (uword size, uword align, uword align_offset,
226                                   int os_out_of_memory_on_failure)
227 {
228   void *mspace_get_aligned (void *msp, unsigned long n_user_data_bytes,
229                             unsigned long align, unsigned long align_offset);
230   clib_mem_heap_t *h = clib_mem_get_per_cpu_heap ();
231   void *p;
232
233   if (align_offset > align)
234     {
235       if (align > 0)
236         align_offset %= align;
237       else
238         align_offset = align;
239     }
240
241   p = mspace_get_aligned (h->mspace, size, align, align_offset);
242
243   if (PREDICT_FALSE (0 == p))
244     {
245       if (os_out_of_memory_on_failure)
246         os_out_of_memory ();
247       return 0;
248     }
249
250   CLIB_MEM_UNPOISON (p, size);
251   return p;
252 }
253
254 /* Memory allocator which calls os_out_of_memory() when it fails */
255 always_inline void *
256 clib_mem_alloc (uword size)
257 {
258   return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
259                                            /* align_offset */ 0,
260                                            /* os_out_of_memory */ 1);
261 }
262
263 always_inline void *
264 clib_mem_alloc_aligned (uword size, uword align)
265 {
266   return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
267                                            /* os_out_of_memory */ 1);
268 }
269
270 /* Memory allocator which calls os_out_of_memory() when it fails */
271 always_inline void *
272 clib_mem_alloc_or_null (uword size)
273 {
274   return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
275                                            /* align_offset */ 0,
276                                            /* os_out_of_memory */ 0);
277 }
278
279 always_inline void *
280 clib_mem_alloc_aligned_or_null (uword size, uword align)
281 {
282   return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
283                                            /* os_out_of_memory */ 0);
284 }
285
286
287
288 /* Memory allocator which panics when it fails.
289    Use macro so that clib_panic macro can expand __FUNCTION__ and __LINE__. */
290 #define clib_mem_alloc_aligned_no_fail(size,align)                              \
291 ({                                                                              \
292   uword _clib_mem_alloc_size = (size);                                          \
293   void * _clib_mem_alloc_p;                                                     \
294   _clib_mem_alloc_p = clib_mem_alloc_aligned (_clib_mem_alloc_size, (align));   \
295   if (! _clib_mem_alloc_p)                                                      \
296     clib_panic ("failed to allocate %d bytes", _clib_mem_alloc_size);           \
297   _clib_mem_alloc_p;                                                            \
298 })
299
300 #define clib_mem_alloc_no_fail(size) clib_mem_alloc_aligned_no_fail(size,1)
301
302 /* Alias to stack allocator for naming consistency. */
303 #define clib_mem_alloc_stack(bytes) __builtin_alloca(bytes)
304
305 always_inline uword
306 clib_mem_is_heap_object (void *p)
307 {
308   int mspace_is_heap_object (void *msp, void *p);
309   clib_mem_heap_t *h = clib_mem_get_per_cpu_heap ();
310   return mspace_is_heap_object (h->mspace, p);
311 }
312
313 always_inline void
314 clib_mem_free (void *p)
315 {
316   void mspace_put (void *msp, void *p_arg);
317   clib_mem_heap_t *h = clib_mem_get_per_cpu_heap ();
318
319   /* Make sure object is in the correct heap. */
320   ASSERT (clib_mem_is_heap_object (p));
321
322   CLIB_MEM_POISON (p, clib_mem_size_nocheck (p));
323
324   mspace_put (h->mspace, p);
325 }
326
327 always_inline void *
328 clib_mem_realloc (void *p, uword new_size, uword old_size)
329 {
330   /* By default use alloc, copy and free to emulate realloc. */
331   void *q = clib_mem_alloc (new_size);
332   if (q)
333     {
334       uword copy_size;
335       if (old_size < new_size)
336         copy_size = old_size;
337       else
338         copy_size = new_size;
339       clib_memcpy_fast (q, p, copy_size);
340       clib_mem_free (p);
341     }
342   return q;
343 }
344
345 always_inline uword
346 clib_mem_size (void *p)
347 {
348   ASSERT (clib_mem_is_heap_object (p));
349   return clib_mem_size_nocheck (p);
350 }
351
352 always_inline void
353 clib_mem_free_s (void *p)
354 {
355   uword size = clib_mem_size (p);
356   CLIB_MEM_UNPOISON (p, size);
357   memset_s_inline (p, size, 0, size);
358   clib_mem_free (p);
359 }
360
361 always_inline clib_mem_heap_t *
362 clib_mem_get_heap (void)
363 {
364   return clib_mem_get_per_cpu_heap ();
365 }
366
367 always_inline clib_mem_heap_t *
368 clib_mem_set_heap (clib_mem_heap_t * heap)
369 {
370   return clib_mem_set_per_cpu_heap (heap);
371 }
372
373 void clib_mem_destroy_heap (clib_mem_heap_t * heap);
374 clib_mem_heap_t *clib_mem_create_heap (void *base, uword size, int is_locked,
375                                        char *fmt, ...);
376
377 void clib_mem_main_init ();
378 void *clib_mem_init (void *base, uword size);
379 void *clib_mem_init_with_page_size (uword memory_size,
380                                     clib_mem_page_sz_t log2_page_sz);
381 void *clib_mem_init_thread_safe (void *memory, uword memory_size);
382
383 void clib_mem_exit (void);
384
385 void clib_mem_trace (int enable);
386
387 int clib_mem_is_traced (void);
388
389 typedef struct
390 {
391   /* Total number of objects allocated. */
392   uword object_count;
393
394   /* Total allocated bytes.  Bytes used and free.
395      used + free = total */
396   uword bytes_total, bytes_used, bytes_free;
397
398   /* Number of bytes used by mheap data structure overhead
399      (e.g. free lists, mheap header). */
400   uword bytes_overhead;
401
402   /* Amount of free space returned to operating system. */
403   uword bytes_free_reclaimed;
404
405   /* For malloc which puts small objects in sbrk region and
406      large objects in mmap'ed regions. */
407   uword bytes_used_sbrk;
408   uword bytes_used_mmap;
409
410   /* Max. number of bytes in this heap. */
411   uword bytes_max;
412 } clib_mem_usage_t;
413
414 void clib_mem_get_heap_usage (clib_mem_heap_t * heap,
415                               clib_mem_usage_t * usage);
416
417 void *clib_mem_get_heap_base (clib_mem_heap_t * heap);
418 uword clib_mem_get_heap_size (clib_mem_heap_t * heap);
419 uword clib_mem_get_heap_free_space (clib_mem_heap_t * heap);
420
421 u8 *format_clib_mem_usage (u8 * s, va_list * args);
422 u8 *format_clib_mem_heap (u8 * s, va_list * va);
423 u8 *format_clib_mem_page_stats (u8 * s, va_list * va);
424
425 /* Allocate virtual address space. */
426 always_inline void *
427 clib_mem_vm_alloc (uword size)
428 {
429   void *mmap_addr;
430   uword flags = MAP_PRIVATE;
431
432 #ifdef MAP_ANONYMOUS
433   flags |= MAP_ANONYMOUS;
434 #endif
435
436   mmap_addr = mmap (0, size, PROT_READ | PROT_WRITE, flags, -1, 0);
437   if (mmap_addr == (void *) -1)
438     mmap_addr = 0;
439   else
440     CLIB_MEM_UNPOISON (mmap_addr, size);
441
442   return mmap_addr;
443 }
444
445 always_inline void
446 clib_mem_vm_free (void *addr, uword size)
447 {
448   munmap (addr, size);
449 }
450
451 void *clib_mem_vm_map_internal (void *base, clib_mem_page_sz_t log2_page_sz,
452                                 uword size, int fd, uword offset, char *name);
453
454 void *clib_mem_vm_map (void *start, uword size,
455                        clib_mem_page_sz_t log2_page_size, char *fmt, ...);
456 void *clib_mem_vm_map_stack (uword size, clib_mem_page_sz_t log2_page_size,
457                              char *fmt, ...);
458 void *clib_mem_vm_map_shared (void *start, uword size, int fd, uword offset,
459                               char *fmt, ...);
460 int clib_mem_vm_unmap (void *base);
461 clib_mem_vm_map_hdr_t *clib_mem_vm_get_next_map_hdr (clib_mem_vm_map_hdr_t *
462                                                      hdr);
463
464 static_always_inline clib_mem_page_sz_t
465 clib_mem_get_log2_page_size (void)
466 {
467   return clib_mem_main.log2_page_sz;
468 }
469
470 static_always_inline uword
471 clib_mem_get_page_size (void)
472 {
473   return 1ULL << clib_mem_main.log2_page_sz;
474 }
475
476 static_always_inline void
477 clib_mem_set_log2_default_hugepage_size (clib_mem_page_sz_t log2_page_sz)
478 {
479   clib_mem_main.log2_default_hugepage_sz = log2_page_sz;
480 }
481
482 static_always_inline clib_mem_page_sz_t
483 clib_mem_get_log2_default_hugepage_size ()
484 {
485   return clib_mem_main.log2_default_hugepage_sz;
486 }
487
488 static_always_inline uword
489 clib_mem_get_default_hugepage_size (void)
490 {
491   return 1ULL << clib_mem_main.log2_default_hugepage_sz;
492 }
493
494 int clib_mem_vm_create_fd (clib_mem_page_sz_t log2_page_size, char *fmt, ...);
495 uword clib_mem_get_fd_page_size (int fd);
496 clib_mem_page_sz_t clib_mem_get_fd_log2_page_size (int fd);
497 uword clib_mem_vm_reserve (uword start, uword size,
498                            clib_mem_page_sz_t log2_page_sz);
499 u64 *clib_mem_vm_get_paddr (void *mem, clib_mem_page_sz_t log2_page_size,
500                             int n_pages);
501 void clib_mem_destroy (void);
502 int clib_mem_set_numa_affinity (u8 numa_node, int force);
503 int clib_mem_set_default_numa_affinity ();
504 void clib_mem_vm_randomize_va (uword * requested_va,
505                                clib_mem_page_sz_t log2_page_size);
506 void mheap_trace (clib_mem_heap_t * v, int enable);
507 uword clib_mem_trace_enable_disable (uword enable);
508 void clib_mem_trace (int enable);
509
510 always_inline uword
511 clib_mem_round_to_page_size (uword size, clib_mem_page_sz_t log2_page_size)
512 {
513   ASSERT (log2_page_size != CLIB_MEM_PAGE_SZ_UNKNOWN);
514
515   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT)
516     log2_page_size = clib_mem_get_log2_page_size ();
517   else if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT_HUGE)
518     log2_page_size = clib_mem_get_log2_default_hugepage_size ();
519
520   return round_pow2 (size, 1ULL << log2_page_size);
521 }
522
523 typedef struct
524 {
525   clib_mem_page_sz_t log2_page_sz;
526   uword total;
527   uword mapped;
528   uword not_mapped;
529   uword per_numa[CLIB_MAX_NUMAS];
530   uword unknown;
531 } clib_mem_page_stats_t;
532
533 void clib_mem_get_page_stats (void *start, clib_mem_page_sz_t log2_page_size,
534                               uword n_pages, clib_mem_page_stats_t * stats);
535
536 static_always_inline int
537 vlib_mem_get_next_numa_node (int numa)
538 {
539   clib_mem_main_t *mm = &clib_mem_main;
540   u32 bitmap = mm->numa_node_bitmap;
541
542   if (numa >= 0)
543     bitmap &= ~pow2_mask (numa + 1);
544   if (bitmap == 0)
545     return -1;
546
547   return count_trailing_zeros (bitmap);
548 }
549
550 static_always_inline clib_mem_page_sz_t
551 clib_mem_log2_page_size_validate (clib_mem_page_sz_t log2_page_size)
552 {
553   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT)
554     return clib_mem_get_log2_page_size ();
555   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT_HUGE)
556     return clib_mem_get_log2_default_hugepage_size ();
557   return log2_page_size;
558 }
559
560 static_always_inline uword
561 clib_mem_page_bytes (clib_mem_page_sz_t log2_page_size)
562 {
563   return 1ULL << clib_mem_log2_page_size_validate (log2_page_size);
564 }
565
566 static_always_inline clib_error_t *
567 clib_mem_get_last_error (void)
568 {
569   return clib_mem_main.error;
570 }
571
572 /* bulk allocator */
573
574 typedef void *clib_mem_bulk_handle_t;
575 clib_mem_bulk_handle_t clib_mem_bulk_init (u32 elt_sz, u32 align,
576                                            u32 min_elts_per_chunk);
577 void clib_mem_bulk_destroy (clib_mem_bulk_handle_t h);
578 void *clib_mem_bulk_alloc (clib_mem_bulk_handle_t h);
579 void clib_mem_bulk_free (clib_mem_bulk_handle_t h, void *p);
580 u8 *format_clib_mem_bulk (u8 *s, va_list *args);
581
582 #include <vppinfra/error.h>     /* clib_panic */
583
584 #endif /* _included_clib_mem_h */
585
586 /*
587  * fd.io coding-style-patch-verification: ON
588  *
589  * Local Variables:
590  * eval: (c-set-style "gnu")
591  * End:
592  */