vppinfra: add clib_log2_page_sz and format/unformat functions
[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/dlmalloc.h>
49
50 #include <vppinfra/os.h>
51 #include <vppinfra/string.h>    /* memcpy, clib_memset */
52 #include <vppinfra/sanitizer.h>
53
54 #define CLIB_MAX_MHEAPS 256
55 #define CLIB_MAX_NUMAS 8
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 /* Unspecified NUMA socket */
75 #define VEC_NUMA_UNSPECIFIED (0xFF)
76
77 /* Per CPU heaps. */
78 extern void *clib_per_cpu_mheaps[CLIB_MAX_MHEAPS];
79 extern void *clib_per_numa_mheaps[CLIB_MAX_NUMAS];
80
81 always_inline void *
82 clib_mem_get_per_cpu_heap (void)
83 {
84   int cpu = os_get_thread_index ();
85   return clib_per_cpu_mheaps[cpu];
86 }
87
88 always_inline void *
89 clib_mem_set_per_cpu_heap (u8 * new_heap)
90 {
91   int cpu = os_get_thread_index ();
92   void *old = clib_per_cpu_mheaps[cpu];
93   clib_per_cpu_mheaps[cpu] = new_heap;
94   return old;
95 }
96
97 always_inline void *
98 clib_mem_get_per_numa_heap (u32 numa_id)
99 {
100   ASSERT (numa_id < ARRAY_LEN (clib_per_numa_mheaps));
101   return clib_per_numa_mheaps[numa_id];
102 }
103
104 always_inline void *
105 clib_mem_set_per_numa_heap (u8 * new_heap)
106 {
107   int numa = os_get_numa_index ();
108   void *old = clib_per_numa_mheaps[numa];
109   clib_per_numa_mheaps[numa] = new_heap;
110   return old;
111 }
112
113 always_inline void
114 clib_mem_set_thread_index (void)
115 {
116   /*
117    * Find an unused slot in the per-cpu-mheaps array,
118    * and grab it for this thread. We need to be able to
119    * push/pop the thread heap without affecting other thread(s).
120    */
121   int i;
122   if (__os_thread_index != 0)
123     return;
124   for (i = 0; i < ARRAY_LEN (clib_per_cpu_mheaps); i++)
125     if (clib_atomic_bool_cmp_and_swap (&clib_per_cpu_mheaps[i],
126                                        0, clib_per_cpu_mheaps[0]))
127       {
128         os_set_thread_index (i);
129         break;
130       }
131   ASSERT (__os_thread_index > 0);
132 }
133
134 always_inline uword
135 clib_mem_size_nocheck (void *p)
136 {
137   return mspace_usable_size_with_delta (p);
138 }
139
140 /* Memory allocator which may call os_out_of_memory() if it fails */
141 always_inline void *
142 clib_mem_alloc_aligned_at_offset (uword size, uword align, uword align_offset,
143                                   int os_out_of_memory_on_failure)
144 {
145   void *heap, *p;
146   uword cpu;
147
148   if (align_offset > align)
149     {
150       if (align > 0)
151         align_offset %= align;
152       else
153         align_offset = align;
154     }
155
156   cpu = os_get_thread_index ();
157   heap = clib_per_cpu_mheaps[cpu];
158
159   p = mspace_get_aligned (heap, size, align, align_offset);
160
161   if (PREDICT_FALSE (0 == p))
162     {
163       if (os_out_of_memory_on_failure)
164         os_out_of_memory ();
165       return 0;
166     }
167
168   CLIB_MEM_UNPOISON (p, size);
169   return p;
170 }
171
172 /* Memory allocator which calls os_out_of_memory() when it fails */
173 always_inline void *
174 clib_mem_alloc (uword size)
175 {
176   return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
177                                            /* align_offset */ 0,
178                                            /* os_out_of_memory */ 1);
179 }
180
181 always_inline void *
182 clib_mem_alloc_aligned (uword size, uword align)
183 {
184   return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
185                                            /* os_out_of_memory */ 1);
186 }
187
188 /* Memory allocator which calls os_out_of_memory() when it fails */
189 always_inline void *
190 clib_mem_alloc_or_null (uword size)
191 {
192   return clib_mem_alloc_aligned_at_offset (size, /* align */ 1,
193                                            /* align_offset */ 0,
194                                            /* os_out_of_memory */ 0);
195 }
196
197 always_inline void *
198 clib_mem_alloc_aligned_or_null (uword size, uword align)
199 {
200   return clib_mem_alloc_aligned_at_offset (size, align, /* align_offset */ 0,
201                                            /* os_out_of_memory */ 0);
202 }
203
204
205
206 /* Memory allocator which panics when it fails.
207    Use macro so that clib_panic macro can expand __FUNCTION__ and __LINE__. */
208 #define clib_mem_alloc_aligned_no_fail(size,align)                              \
209 ({                                                                              \
210   uword _clib_mem_alloc_size = (size);                                          \
211   void * _clib_mem_alloc_p;                                                     \
212   _clib_mem_alloc_p = clib_mem_alloc_aligned (_clib_mem_alloc_size, (align));   \
213   if (! _clib_mem_alloc_p)                                                      \
214     clib_panic ("failed to allocate %d bytes", _clib_mem_alloc_size);           \
215   _clib_mem_alloc_p;                                                            \
216 })
217
218 #define clib_mem_alloc_no_fail(size) clib_mem_alloc_aligned_no_fail(size,1)
219
220 /* Alias to stack allocator for naming consistency. */
221 #define clib_mem_alloc_stack(bytes) __builtin_alloca(bytes)
222
223 always_inline uword
224 clib_mem_is_heap_object (void *p)
225 {
226   void *heap = clib_mem_get_per_cpu_heap ();
227
228   return mspace_is_heap_object (heap, p);
229 }
230
231 always_inline void
232 clib_mem_free (void *p)
233 {
234   u8 *heap = clib_mem_get_per_cpu_heap ();
235
236   /* Make sure object is in the correct heap. */
237   ASSERT (clib_mem_is_heap_object (p));
238
239   CLIB_MEM_POISON (p, clib_mem_size_nocheck (p));
240
241   mspace_put (heap, p);
242 }
243
244 always_inline void *
245 clib_mem_realloc (void *p, uword new_size, uword old_size)
246 {
247   /* By default use alloc, copy and free to emulate realloc. */
248   void *q = clib_mem_alloc (new_size);
249   if (q)
250     {
251       uword copy_size;
252       if (old_size < new_size)
253         copy_size = old_size;
254       else
255         copy_size = new_size;
256       clib_memcpy_fast (q, p, copy_size);
257       clib_mem_free (p);
258     }
259   return q;
260 }
261
262 always_inline uword
263 clib_mem_size (void *p)
264 {
265   ASSERT (clib_mem_is_heap_object (p));
266   return clib_mem_size_nocheck (p);
267 }
268
269 always_inline void
270 clib_mem_free_s (void *p)
271 {
272   uword size = clib_mem_size (p);
273   CLIB_MEM_UNPOISON (p, size);
274   memset_s_inline (p, size, 0, size);
275   clib_mem_free (p);
276 }
277
278 always_inline void *
279 clib_mem_get_heap (void)
280 {
281   return clib_mem_get_per_cpu_heap ();
282 }
283
284 always_inline void *
285 clib_mem_set_heap (void *heap)
286 {
287   return clib_mem_set_per_cpu_heap (heap);
288 }
289
290 void *clib_mem_init (void *heap, uword size);
291 void *clib_mem_init_thread_safe (void *memory, uword memory_size);
292 void *clib_mem_init_thread_safe_numa (void *memory, uword memory_size,
293                                       u8 numa);
294
295 void clib_mem_exit (void);
296
297 uword clib_mem_get_page_size (void);
298
299 void clib_mem_validate (void);
300
301 void clib_mem_trace (int enable);
302
303 int clib_mem_is_traced (void);
304
305 typedef struct
306 {
307   /* Total number of objects allocated. */
308   uword object_count;
309
310   /* Total allocated bytes.  Bytes used and free.
311      used + free = total */
312   uword bytes_total, bytes_used, bytes_free;
313
314   /* Number of bytes used by mheap data structure overhead
315      (e.g. free lists, mheap header). */
316   uword bytes_overhead;
317
318   /* Amount of free space returned to operating system. */
319   uword bytes_free_reclaimed;
320
321   /* For malloc which puts small objects in sbrk region and
322      large objects in mmap'ed regions. */
323   uword bytes_used_sbrk;
324   uword bytes_used_mmap;
325
326   /* Max. number of bytes in this heap. */
327   uword bytes_max;
328 } clib_mem_usage_t;
329
330 void clib_mem_usage (clib_mem_usage_t * usage);
331
332 u8 *format_clib_mem_usage (u8 * s, va_list * args);
333
334 /* Allocate virtual address space. */
335 always_inline void *
336 clib_mem_vm_alloc (uword size)
337 {
338   void *mmap_addr;
339   uword flags = MAP_PRIVATE;
340
341 #ifdef MAP_ANONYMOUS
342   flags |= MAP_ANONYMOUS;
343 #endif
344
345   mmap_addr = mmap (0, size, PROT_READ | PROT_WRITE, flags, -1, 0);
346   if (mmap_addr == (void *) -1)
347     mmap_addr = 0;
348   else
349     CLIB_MEM_UNPOISON (mmap_addr, size);
350
351   return mmap_addr;
352 }
353
354 always_inline void
355 clib_mem_vm_free (void *addr, uword size)
356 {
357   munmap (addr, size);
358 }
359
360 always_inline void *
361 clib_mem_vm_unmap (void *addr, uword size)
362 {
363   void *mmap_addr;
364   uword flags = MAP_PRIVATE | MAP_FIXED;
365
366   /* To unmap we "map" with no protection.  If we actually called
367      munmap then other callers could steal the address space.  By
368      changing to PROT_NONE the kernel can free up the pages which is
369      really what we want "unmap" to mean. */
370   mmap_addr = mmap (addr, size, PROT_NONE, flags, -1, 0);
371   if (mmap_addr == (void *) -1)
372     mmap_addr = 0;
373   else
374     CLIB_MEM_UNPOISON (mmap_addr, size);
375
376   return mmap_addr;
377 }
378
379 always_inline void *
380 clib_mem_vm_map (void *addr, uword size)
381 {
382   void *mmap_addr;
383   uword flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
384
385   mmap_addr = mmap (addr, size, (PROT_READ | PROT_WRITE), flags, -1, 0);
386   if (mmap_addr == (void *) -1)
387     mmap_addr = 0;
388   else
389     CLIB_MEM_UNPOISON (mmap_addr, size);
390
391   return mmap_addr;
392 }
393
394 typedef struct
395 {
396 #define CLIB_MEM_VM_F_SHARED (1 << 0)
397 #define CLIB_MEM_VM_F_HUGETLB (1 << 1)
398 #define CLIB_MEM_VM_F_NUMA_PREFER (1 << 2)
399 #define CLIB_MEM_VM_F_NUMA_FORCE (1 << 3)
400 #define CLIB_MEM_VM_F_HUGETLB_PREALLOC (1 << 4)
401 #define CLIB_MEM_VM_F_LOCKED (1 << 5)
402   u32 flags; /**< vm allocation flags:
403                 <br> CLIB_MEM_VM_F_SHARED: request shared memory, file
404                 descriptor will be provided on successful allocation.
405                 <br> CLIB_MEM_VM_F_HUGETLB: request hugepages.
406                 <br> CLIB_MEM_VM_F_NUMA_PREFER: numa_node field contains valid
407                 numa node preference.
408                 <br> CLIB_MEM_VM_F_NUMA_FORCE: fail if setting numa policy fails.
409                 <br> CLIB_MEM_VM_F_HUGETLB_PREALLOC: pre-allocate hugepages if
410                 number of available pages is not sufficient.
411                 <br> CLIB_MEM_VM_F_LOCKED: request locked memory.
412              */
413   char *name; /**< Name for memory allocation, set by caller. */
414   uword size; /**< Allocation size, set by caller. */
415   int numa_node; /**< numa node preference. Valid if CLIB_MEM_VM_F_NUMA_PREFER set. */
416   void *addr; /**< Pointer to allocated memory, set on successful allocation. */
417   int fd; /**< File descriptor, set on successful allocation if CLIB_MEM_VM_F_SHARED is set. */
418   int log2_page_size;           /* Page size in log2 format, set on successful allocation. */
419   int n_pages;                  /* Number of pages. */
420   uword requested_va;           /**< Request fixed position mapping */
421 } clib_mem_vm_alloc_t;
422
423 clib_error_t *clib_mem_create_fd (char *name, int *fdp);
424 clib_error_t *clib_mem_create_hugetlb_fd (char *name, int *fdp);
425 clib_error_t *clib_mem_vm_ext_alloc (clib_mem_vm_alloc_t * a);
426 void clib_mem_vm_ext_free (clib_mem_vm_alloc_t * a);
427 u64 clib_mem_get_fd_page_size (int fd);
428 uword clib_mem_get_default_hugepage_size (void);
429 int clib_mem_get_fd_log2_page_size (int fd);
430 uword clib_mem_vm_reserve (uword start, uword size,
431                            clib_mem_page_sz_t log2_page_sz);
432 u64 *clib_mem_vm_get_paddr (void *mem, int log2_page_size, int n_pages);
433 void clib_mem_destroy_mspace (void *mspace);
434 void clib_mem_destroy (void);
435
436 typedef struct
437 {
438   uword size;           /**< Map size */
439   int fd;               /**< File descriptor to be mapped */
440   uword requested_va;   /**< Request fixed position mapping */
441   void *addr;           /**< Pointer to mapped memory, if successful */
442   u8 numa_node;
443 } clib_mem_vm_map_t;
444
445 clib_error_t *clib_mem_vm_ext_map (clib_mem_vm_map_t * a);
446 void clib_mem_vm_randomize_va (uword * requested_va,
447                                clib_mem_page_sz_t log2_page_size);
448 void mheap_trace (void *v, int enable);
449 uword clib_mem_trace_enable_disable (uword enable);
450 void clib_mem_trace (int enable);
451
452 #include <vppinfra/error.h>     /* clib_panic */
453
454 #endif /* _included_clib_mem_h */
455
456 /*
457  * fd.io coding-style-patch-verification: ON
458  *
459  * Local Variables:
460  * eval: (c-set-style "gnu")
461  * End:
462  */