vppinfra: don't call dlmalloc API directly from the code
[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 typedef struct
97 {
98   /* log2 system page size */
99   clib_mem_page_sz_t log2_page_sz;
100
101   /* log2 system default hugepage size */
102   clib_mem_page_sz_t log2_default_hugepage_sz;
103
104   /* bitmap of available numa nodes */
105   u32 numa_node_bitmap;
106
107   /* per CPU heaps */
108   void *per_cpu_mheaps[CLIB_MAX_MHEAPS];
109
110   /* per NUMA heaps */
111   void *per_numa_mheaps[CLIB_MAX_NUMAS];
112
113   /* memory maps */
114   clib_mem_vm_map_hdr_t *first_map, *last_map;
115
116   /* last error */
117   clib_error_t *error;
118 } clib_mem_main_t;
119
120 extern clib_mem_main_t clib_mem_main;
121
122 /* Unspecified NUMA socket */
123 #define VEC_NUMA_UNSPECIFIED (0xFF)
124
125 always_inline void *
126 clib_mem_get_per_cpu_heap (void)
127 {
128   int cpu = os_get_thread_index ();
129   return clib_mem_main.per_cpu_mheaps[cpu];
130 }
131
132 always_inline void *
133 clib_mem_set_per_cpu_heap (u8 * new_heap)
134 {
135   int cpu = os_get_thread_index ();
136   void *old = clib_mem_main.per_cpu_mheaps[cpu];
137   clib_mem_main.per_cpu_mheaps[cpu] = new_heap;
138   return old;
139 }
140
141 always_inline void *
142 clib_mem_get_per_numa_heap (u32 numa_id)
143 {
144   ASSERT (numa_id < ARRAY_LEN (clib_mem_main.per_numa_mheaps));
145   return clib_mem_main.per_numa_mheaps[numa_id];
146 }
147
148 always_inline void *
149 clib_mem_set_per_numa_heap (u8 * new_heap)
150 {
151   int numa = os_get_numa_index ();
152   void *old = clib_mem_main.per_numa_mheaps[numa];
153   clib_mem_main.per_numa_mheaps[numa] = new_heap;
154   return old;
155 }
156
157 always_inline void
158 clib_mem_set_thread_index (void)
159 {
160   /*
161    * Find an unused slot in the per-cpu-mheaps array,
162    * and grab it for this thread. We need to be able to
163    * push/pop the thread heap without affecting other thread(s).
164    */
165   int i;
166   if (__os_thread_index != 0)
167     return;
168   for (i = 0; i < ARRAY_LEN (clib_mem_main.per_cpu_mheaps); i++)
169     if (clib_atomic_bool_cmp_and_swap (&clib_mem_main.per_cpu_mheaps[i],
170                                        0, clib_mem_main.per_cpu_mheaps[0]))
171       {
172         os_set_thread_index (i);
173         break;
174       }
175   ASSERT (__os_thread_index > 0);
176 }
177
178 always_inline uword
179 clib_mem_size_nocheck (void *p)
180 {
181   size_t mspace_usable_size_with_delta (const void *p);
182   return mspace_usable_size_with_delta (p);
183 }
184
185 /* Memory allocator which may call os_out_of_memory() if it fails */
186 always_inline void *
187 clib_mem_alloc_aligned_at_offset (uword size, uword align, uword align_offset,
188                                   int os_out_of_memory_on_failure)
189 {
190   void *heap, *p;
191   uword cpu;
192   void *mspace_get_aligned (void *msp, unsigned long n_user_data_bytes,
193                             unsigned long align, unsigned long align_offset);
194
195   if (align_offset > align)
196     {
197       if (align > 0)
198         align_offset %= align;
199       else
200         align_offset = align;
201     }
202
203   cpu = os_get_thread_index ();
204   heap = clib_mem_main.per_cpu_mheaps[cpu];
205
206   p = mspace_get_aligned (heap, size, align, align_offset);
207
208   if (PREDICT_FALSE (0 == p))
209     {
210       if (os_out_of_memory_on_failure)
211         os_out_of_memory ();
212       return 0;
213     }
214
215   CLIB_MEM_UNPOISON (p, size);
216   return p;
217 }
218
219 /* Memory allocator which calls os_out_of_memory() when it fails */
220 always_inline void *
221 clib_mem_alloc (uword size)
222 {
223   return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
224                                            /* align_offset */ 0,
225                                            /* os_out_of_memory */ 1);
226 }
227
228 always_inline void *
229 clib_mem_alloc_aligned (uword size, uword align)
230 {
231   return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
232                                            /* os_out_of_memory */ 1);
233 }
234
235 /* Memory allocator which calls os_out_of_memory() when it fails */
236 always_inline void *
237 clib_mem_alloc_or_null (uword size)
238 {
239   return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
240                                            /* align_offset */ 0,
241                                            /* os_out_of_memory */ 0);
242 }
243
244 always_inline void *
245 clib_mem_alloc_aligned_or_null (uword size, uword align)
246 {
247   return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
248                                            /* os_out_of_memory */ 0);
249 }
250
251
252
253 /* Memory allocator which panics when it fails.
254    Use macro so that clib_panic macro can expand __FUNCTION__ and __LINE__. */
255 #define clib_mem_alloc_aligned_no_fail(size,align)                              \
256 ({                                                                              \
257   uword _clib_mem_alloc_size = (size);                                          \
258   void * _clib_mem_alloc_p;                                                     \
259   _clib_mem_alloc_p = clib_mem_alloc_aligned (_clib_mem_alloc_size, (align));   \
260   if (! _clib_mem_alloc_p)                                                      \
261     clib_panic ("failed to allocate %d bytes", _clib_mem_alloc_size);           \
262   _clib_mem_alloc_p;                                                            \
263 })
264
265 #define clib_mem_alloc_no_fail(size) clib_mem_alloc_aligned_no_fail(size,1)
266
267 /* Alias to stack allocator for naming consistency. */
268 #define clib_mem_alloc_stack(bytes) __builtin_alloca(bytes)
269
270 always_inline uword
271 clib_mem_is_heap_object (void *p)
272 {
273   void *heap = clib_mem_get_per_cpu_heap ();
274   int mspace_is_heap_object (void *msp, void *p);
275
276   return mspace_is_heap_object (heap, p);
277 }
278
279 always_inline void
280 clib_mem_free (void *p)
281 {
282   u8 *heap = clib_mem_get_per_cpu_heap ();
283
284   void mspace_put (void *msp, void *p_arg);
285   /* Make sure object is in the correct heap. */
286   ASSERT (clib_mem_is_heap_object (p));
287
288   CLIB_MEM_POISON (p, clib_mem_size_nocheck (p));
289
290   mspace_put (heap, p);
291 }
292
293 always_inline void *
294 clib_mem_realloc (void *p, uword new_size, uword old_size)
295 {
296   /* By default use alloc, copy and free to emulate realloc. */
297   void *q = clib_mem_alloc (new_size);
298   if (q)
299     {
300       uword copy_size;
301       if (old_size < new_size)
302         copy_size = old_size;
303       else
304         copy_size = new_size;
305       clib_memcpy_fast (q, p, copy_size);
306       clib_mem_free (p);
307     }
308   return q;
309 }
310
311 always_inline uword
312 clib_mem_size (void *p)
313 {
314   ASSERT (clib_mem_is_heap_object (p));
315   return clib_mem_size_nocheck (p);
316 }
317
318 always_inline void
319 clib_mem_free_s (void *p)
320 {
321   uword size = clib_mem_size (p);
322   CLIB_MEM_UNPOISON (p, size);
323   memset_s_inline (p, size, 0, size);
324   clib_mem_free (p);
325 }
326
327 always_inline void *
328 clib_mem_get_heap (void)
329 {
330   return clib_mem_get_per_cpu_heap ();
331 }
332
333 always_inline void *
334 clib_mem_set_heap (void *heap)
335 {
336   return clib_mem_set_per_cpu_heap (heap);
337 }
338
339 void clib_mem_destroy_heap (void *heap);
340 void *clib_mem_create_heap (void *base, uword size, int is_locked, char *fmt,
341                             ...);
342
343 void clib_mem_main_init ();
344 void *clib_mem_init (void *heap, uword size);
345 void *clib_mem_init_with_page_size (uword memory_size,
346                                     clib_mem_page_sz_t log2_page_sz);
347 void *clib_mem_init_thread_safe (void *memory, uword memory_size);
348 void *clib_mem_init_thread_safe_numa (void *memory, uword memory_size,
349                                       u8 numa);
350
351 void clib_mem_exit (void);
352
353 void clib_mem_trace (int enable);
354
355 int clib_mem_is_traced (void);
356
357 typedef struct
358 {
359   /* Total number of objects allocated. */
360   uword object_count;
361
362   /* Total allocated bytes.  Bytes used and free.
363      used + free = total */
364   uword bytes_total, bytes_used, bytes_free;
365
366   /* Number of bytes used by mheap data structure overhead
367      (e.g. free lists, mheap header). */
368   uword bytes_overhead;
369
370   /* Amount of free space returned to operating system. */
371   uword bytes_free_reclaimed;
372
373   /* For malloc which puts small objects in sbrk region and
374      large objects in mmap'ed regions. */
375   uword bytes_used_sbrk;
376   uword bytes_used_mmap;
377
378   /* Max. number of bytes in this heap. */
379   uword bytes_max;
380 } clib_mem_usage_t;
381
382 void clib_mem_get_heap_usage (void *heap, clib_mem_usage_t * usage);
383
384 void *clib_mem_get_heap_base (void *heap);
385 uword clib_mem_get_heap_size (void *heap);
386 uword clib_mem_get_heap_free_space (void *heap);
387
388 u8 *format_clib_mem_usage (u8 * s, va_list * args);
389 u8 *format_clib_mem_heap (u8 * s, va_list * va);
390
391 /* Allocate virtual address space. */
392 always_inline void *
393 clib_mem_vm_alloc (uword size)
394 {
395   void *mmap_addr;
396   uword flags = MAP_PRIVATE;
397
398 #ifdef MAP_ANONYMOUS
399   flags |= MAP_ANONYMOUS;
400 #endif
401
402   mmap_addr = mmap (0, size, PROT_READ | PROT_WRITE, flags, -1, 0);
403   if (mmap_addr == (void *) -1)
404     mmap_addr = 0;
405   else
406     CLIB_MEM_UNPOISON (mmap_addr, size);
407
408   return mmap_addr;
409 }
410
411 always_inline void
412 clib_mem_vm_free (void *addr, uword size)
413 {
414   munmap (addr, size);
415 }
416
417 void *clib_mem_vm_map_internal (void *base, clib_mem_page_sz_t log2_page_sz,
418                                 uword size, int fd, uword offset, char *name);
419
420 void *clib_mem_vm_map (void *start, uword size,
421                        clib_mem_page_sz_t log2_page_size, char *fmt, ...);
422 void *clib_mem_vm_map_stack (uword size, clib_mem_page_sz_t log2_page_size,
423                              char *fmt, ...);
424 void *clib_mem_vm_map_shared (void *start, uword size, int fd, uword offset,
425                               char *fmt, ...);
426 int clib_mem_vm_unmap (void *base);
427 clib_mem_vm_map_hdr_t *clib_mem_vm_get_next_map_hdr (clib_mem_vm_map_hdr_t *
428                                                      hdr);
429
430 typedef struct
431 {
432 #define CLIB_MEM_VM_F_SHARED (1 << 0)
433 #define CLIB_MEM_VM_F_HUGETLB (1 << 1)
434 #define CLIB_MEM_VM_F_NUMA_PREFER (1 << 2)
435 #define CLIB_MEM_VM_F_NUMA_FORCE (1 << 3)
436 #define CLIB_MEM_VM_F_HUGETLB_PREALLOC (1 << 4)
437 #define CLIB_MEM_VM_F_LOCKED (1 << 5)
438   u32 flags; /**< vm allocation flags:
439                 <br> CLIB_MEM_VM_F_SHARED: request shared memory, file
440                 descriptor will be provided on successful allocation.
441                 <br> CLIB_MEM_VM_F_HUGETLB: request hugepages.
442                 <br> CLIB_MEM_VM_F_NUMA_PREFER: numa_node field contains valid
443                 numa node preference.
444                 <br> CLIB_MEM_VM_F_NUMA_FORCE: fail if setting numa policy fails.
445                 <br> CLIB_MEM_VM_F_HUGETLB_PREALLOC: pre-allocate hugepages if
446                 number of available pages is not sufficient.
447                 <br> CLIB_MEM_VM_F_LOCKED: request locked memory.
448              */
449   char *name; /**< Name for memory allocation, set by caller. */
450   uword size; /**< Allocation size, set by caller. */
451   int numa_node; /**< numa node preference. Valid if CLIB_MEM_VM_F_NUMA_PREFER set. */
452   void *addr; /**< Pointer to allocated memory, set on successful allocation. */
453   int fd; /**< File descriptor, set on successful allocation if CLIB_MEM_VM_F_SHARED is set. */
454   int log2_page_size;           /* Page size in log2 format, set on successful allocation. */
455   int n_pages;                  /* Number of pages. */
456   uword requested_va;           /**< Request fixed position mapping */
457 } clib_mem_vm_alloc_t;
458
459
460 static_always_inline clib_mem_page_sz_t
461 clib_mem_get_log2_page_size (void)
462 {
463   return clib_mem_main.log2_page_sz;
464 }
465
466 static_always_inline uword
467 clib_mem_get_page_size (void)
468 {
469   return 1ULL << clib_mem_main.log2_page_sz;
470 }
471
472 static_always_inline clib_mem_page_sz_t
473 clib_mem_get_log2_default_hugepage_size ()
474 {
475   return clib_mem_main.log2_default_hugepage_sz;
476 }
477
478 int clib_mem_vm_create_fd (clib_mem_page_sz_t log2_page_size, char *fmt, ...);
479 clib_error_t *clib_mem_vm_ext_alloc (clib_mem_vm_alloc_t * a);
480 void clib_mem_vm_ext_free (clib_mem_vm_alloc_t * a);
481 uword clib_mem_get_fd_page_size (int fd);
482 uword clib_mem_get_default_hugepage_size (void);
483 clib_mem_page_sz_t clib_mem_get_fd_log2_page_size (int fd);
484 uword clib_mem_vm_reserve (uword start, uword size,
485                            clib_mem_page_sz_t log2_page_sz);
486 u64 *clib_mem_vm_get_paddr (void *mem, clib_mem_page_sz_t log2_page_size,
487                             int n_pages);
488 void clib_mem_destroy (void);
489 int clib_mem_set_numa_affinity (u8 numa_node, int force);
490 int clib_mem_set_default_numa_affinity ();
491
492 typedef struct
493 {
494   uword size;           /**< Map size */
495   int fd;               /**< File descriptor to be mapped */
496   uword requested_va;   /**< Request fixed position mapping */
497   void *addr;           /**< Pointer to mapped memory, if successful */
498   u8 numa_node;
499 } clib_mem_vm_map_t;
500
501 clib_error_t *clib_mem_vm_ext_map (clib_mem_vm_map_t * a);
502 void clib_mem_vm_randomize_va (uword * requested_va,
503                                clib_mem_page_sz_t log2_page_size);
504 void mheap_trace (void *v, int enable);
505 uword clib_mem_trace_enable_disable (uword enable);
506 void clib_mem_trace (int enable);
507
508 always_inline uword
509 clib_mem_round_to_page_size (uword size, clib_mem_page_sz_t log2_page_size)
510 {
511   ASSERT (log2_page_size != CLIB_MEM_PAGE_SZ_UNKNOWN);
512
513   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT)
514     log2_page_size = clib_mem_get_log2_page_size ();
515   else if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT_HUGE)
516     log2_page_size = clib_mem_get_log2_default_hugepage_size ();
517
518   return round_pow2 (size, 1ULL << log2_page_size);
519 }
520
521 typedef struct
522 {
523   uword mapped;
524   uword not_mapped;
525   uword per_numa[CLIB_MAX_NUMAS];
526   uword unknown;
527 } clib_mem_page_stats_t;
528
529 void clib_mem_get_page_stats (void *start, clib_mem_page_sz_t log2_page_size,
530                               uword n_pages, clib_mem_page_stats_t * stats);
531
532 static_always_inline int
533 vlib_mem_get_next_numa_node (int numa)
534 {
535   clib_mem_main_t *mm = &clib_mem_main;
536   u32 bitmap = mm->numa_node_bitmap;
537
538   if (numa >= 0)
539     bitmap &= ~pow2_mask (numa + 1);
540   if (bitmap == 0)
541     return -1;
542
543   return count_trailing_zeros (bitmap);
544 }
545
546 static_always_inline clib_mem_page_sz_t
547 clib_mem_log2_page_size_validate (clib_mem_page_sz_t log2_page_size)
548 {
549   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT)
550     return clib_mem_get_log2_page_size ();
551   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT_HUGE)
552     return clib_mem_get_log2_default_hugepage_size ();
553   return log2_page_size;
554 }
555
556 static_always_inline uword
557 clib_mem_page_bytes (clib_mem_page_sz_t log2_page_size)
558 {
559   return 1 << clib_mem_log2_page_size_validate (log2_page_size);
560 }
561
562 static_always_inline clib_error_t *
563 clib_mem_get_last_error (void)
564 {
565   return clib_mem_main.error;
566 }
567
568
569 #include <vppinfra/error.h>     /* clib_panic */
570
571 #endif /* _included_clib_mem_h */
572
573 /*
574  * fd.io coding-style-patch-verification: ON
575  *
576  * Local Variables:
577  * eval: (c-set-style "gnu")
578  * End:
579  */