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