f2930a11253310ac2b807c027627a71a342a69dc
[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/valgrind.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 /* Memory allocator which may call os_out_of_memory() if it fails */
101 always_inline void *
102 clib_mem_alloc_aligned_at_offset (uword size, uword align, uword align_offset,
103                                   int os_out_of_memory_on_failure)
104 {
105   void *heap, *p;
106   uword cpu;
107
108   if (align_offset > align)
109     {
110       if (align > 0)
111         align_offset %= align;
112       else
113         align_offset = align;
114     }
115
116   cpu = os_get_thread_index ();
117   heap = clib_per_cpu_mheaps[cpu];
118
119 #if USE_DLMALLOC == 0
120   uword offset;
121   heap = mheap_get_aligned (heap, size, align, align_offset, &offset);
122   clib_per_cpu_mheaps[cpu] = heap;
123
124   if (offset != ~0)
125     {
126       p = heap + offset;
127 #if CLIB_DEBUG > 0
128       VALGRIND_MALLOCLIKE_BLOCK (p, mheap_data_bytes (heap, offset), 0, 0);
129 #endif
130       return p;
131     }
132   else
133     {
134       if (os_out_of_memory_on_failure)
135         os_out_of_memory ();
136       return 0;
137     }
138 #else
139   p = mspace_get_aligned (heap, size, align, align_offset);
140   if (PREDICT_FALSE (p == 0))
141     {
142       if (os_out_of_memory_on_failure)
143         os_out_of_memory ();
144       return 0;
145     }
146
147   return p;
148 #endif /* USE_DLMALLOC */
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 #if USE_DLMALLOC == 0
234   mheap_put (heap, (u8 *) p - heap);
235 #else
236   mspace_put (heap, p);
237 #endif
238
239 #if CLIB_DEBUG > 0
240   VALGRIND_FREELIKE_BLOCK (p, 0);
241 #endif
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 #if USE_DLMALLOC == 0
266   mheap_elt_t *e = mheap_user_pointer_to_elt (p);
267   ASSERT (clib_mem_is_heap_object (p));
268   return mheap_elt_data_bytes (e);
269 #else
270   ASSERT (clib_mem_is_heap_object (p));
271   return mspace_usable_size_with_delta (p);
272 #endif
273 }
274
275 always_inline void *
276 clib_mem_get_heap (void)
277 {
278   return clib_mem_get_per_cpu_heap ();
279 }
280
281 always_inline void *
282 clib_mem_set_heap (void *heap)
283 {
284   return clib_mem_set_per_cpu_heap (heap);
285 }
286
287 void *clib_mem_init (void *heap, uword size);
288 void *clib_mem_init_thread_safe (void *memory, uword memory_size);
289
290 void clib_mem_exit (void);
291
292 uword clib_mem_get_page_size (void);
293
294 void clib_mem_validate (void);
295
296 void clib_mem_trace (int enable);
297
298 int clib_mem_is_traced (void);
299
300 typedef struct
301 {
302   /* Total number of objects allocated. */
303   uword object_count;
304
305   /* Total allocated bytes.  Bytes used and free.
306      used + free = total */
307   uword bytes_total, bytes_used, bytes_free;
308
309   /* Number of bytes used by mheap data structure overhead
310      (e.g. free lists, mheap header). */
311   uword bytes_overhead;
312
313   /* Amount of free space returned to operating system. */
314   uword bytes_free_reclaimed;
315
316   /* For malloc which puts small objects in sbrk region and
317      large objects in mmap'ed regions. */
318   uword bytes_used_sbrk;
319   uword bytes_used_mmap;
320
321   /* Max. number of bytes in this heap. */
322   uword bytes_max;
323 } clib_mem_usage_t;
324
325 void clib_mem_usage (clib_mem_usage_t * usage);
326
327 u8 *format_clib_mem_usage (u8 * s, va_list * args);
328
329 /* Allocate virtual address space. */
330 always_inline void *
331 clib_mem_vm_alloc (uword size)
332 {
333   void *mmap_addr;
334   uword flags = MAP_PRIVATE;
335
336 #ifdef MAP_ANONYMOUS
337   flags |= MAP_ANONYMOUS;
338 #endif
339
340   mmap_addr = mmap (0, size, PROT_READ | PROT_WRITE, flags, -1, 0);
341   if (mmap_addr == (void *) -1)
342     mmap_addr = 0;
343
344   return mmap_addr;
345 }
346
347 always_inline void
348 clib_mem_vm_free (void *addr, uword size)
349 {
350   munmap (addr, size);
351 }
352
353 always_inline void *
354 clib_mem_vm_unmap (void *addr, uword size)
355 {
356   void *mmap_addr;
357   uword flags = MAP_PRIVATE | MAP_FIXED;
358
359   /* To unmap we "map" with no protection.  If we actually called
360      munmap then other callers could steal the address space.  By
361      changing to PROT_NONE the kernel can free up the pages which is
362      really what we want "unmap" to mean. */
363   mmap_addr = mmap (addr, size, PROT_NONE, flags, -1, 0);
364   if (mmap_addr == (void *) -1)
365     mmap_addr = 0;
366
367   return mmap_addr;
368 }
369
370 always_inline void *
371 clib_mem_vm_map (void *addr, uword size)
372 {
373   void *mmap_addr;
374   uword flags = MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS;
375
376   mmap_addr = mmap (addr, size, (PROT_READ | PROT_WRITE), flags, -1, 0);
377   if (mmap_addr == (void *) -1)
378     mmap_addr = 0;
379
380   return mmap_addr;
381 }
382
383 typedef struct
384 {
385 #define CLIB_MEM_VM_F_SHARED (1 << 0)
386 #define CLIB_MEM_VM_F_HUGETLB (1 << 1)
387 #define CLIB_MEM_VM_F_NUMA_PREFER (1 << 2)
388 #define CLIB_MEM_VM_F_NUMA_FORCE (1 << 3)
389 #define CLIB_MEM_VM_F_HUGETLB_PREALLOC (1 << 4)
390 #define CLIB_MEM_VM_F_LOCKED (1 << 5)
391   u32 flags; /**< vm allocation flags:
392                 <br> CLIB_MEM_VM_F_SHARED: request shared memory, file
393                 descriptor will be provided on successful allocation.
394                 <br> CLIB_MEM_VM_F_HUGETLB: request hugepages.
395                 <br> CLIB_MEM_VM_F_NUMA_PREFER: numa_node field contains valid
396                 numa node preference.
397                 <br> CLIB_MEM_VM_F_NUMA_FORCE: fail if setting numa policy fails.
398                 <br> CLIB_MEM_VM_F_HUGETLB_PREALLOC: pre-allocate hugepages if
399                 number of available pages is not sufficient.
400                 <br> CLIB_MEM_VM_F_LOCKED: request locked memory.
401              */
402   char *name; /**< Name for memory allocation, set by caller. */
403   uword size; /**< Allocation size, set by caller. */
404   int numa_node; /**< numa node preference. Valid if CLIB_MEM_VM_F_NUMA_PREFER set. */
405   void *addr; /**< Pointer to allocated memory, set on successful allocation. */
406   int fd; /**< File descriptor, set on successful allocation if CLIB_MEM_VM_F_SHARED is set. */
407   int log2_page_size;           /* Page size in log2 format, set on successful allocation. */
408   int n_pages;                  /* Number of pages. */
409   uword requested_va;           /**< Request fixed position mapping */
410 } clib_mem_vm_alloc_t;
411
412 clib_error_t *clib_mem_create_fd (char *name, int *fdp);
413 clib_error_t *clib_mem_create_hugetlb_fd (char *name, int *fdp);
414 clib_error_t *clib_mem_vm_ext_alloc (clib_mem_vm_alloc_t * a);
415 void clib_mem_vm_ext_free (clib_mem_vm_alloc_t * a);
416 u64 clib_mem_get_fd_page_size (int fd);
417 uword clib_mem_get_default_hugepage_size (void);
418 int clib_mem_get_fd_log2_page_size (int fd);
419 u64 *clib_mem_vm_get_paddr (void *mem, int log2_page_size, int n_pages);
420
421 typedef struct
422 {
423   uword size;           /**< Map size */
424   int fd;               /**< File descriptor to be mapped */
425   uword requested_va;   /**< Request fixed position mapping */
426   void *addr;           /**< Pointer to mapped memory, if successful */
427 } clib_mem_vm_map_t;
428
429 clib_error_t *clib_mem_vm_ext_map (clib_mem_vm_map_t * a);
430 void clib_mem_vm_randomize_va (uword * requested_va, u32 log2_page_size);
431 void mheap_trace (void *v, int enable);
432 uword clib_mem_trace_enable_disable (uword enable);
433 void clib_mem_trace (int enable);
434
435 #include <vppinfra/error.h>     /* clib_panic */
436
437 #endif /* _included_clib_mem_h */
438
439 /*
440  * fd.io coding-style-patch-verification: ON
441  *
442  * Local Variables:
443  * eval: (c-set-style "gnu")
444  * End:
445  */