vppinfra: use clib_mem_create_heap() to create numa heap(s)
[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
349 void clib_mem_exit (void);
350
351 void clib_mem_trace (int enable);
352
353 int clib_mem_is_traced (void);
354
355 typedef struct
356 {
357   /* Total number of objects allocated. */
358   uword object_count;
359
360   /* Total allocated bytes.  Bytes used and free.
361      used + free = total */
362   uword bytes_total, bytes_used, bytes_free;
363
364   /* Number of bytes used by mheap data structure overhead
365      (e.g. free lists, mheap header). */
366   uword bytes_overhead;
367
368   /* Amount of free space returned to operating system. */
369   uword bytes_free_reclaimed;
370
371   /* For malloc which puts small objects in sbrk region and
372      large objects in mmap'ed regions. */
373   uword bytes_used_sbrk;
374   uword bytes_used_mmap;
375
376   /* Max. number of bytes in this heap. */
377   uword bytes_max;
378 } clib_mem_usage_t;
379
380 void clib_mem_get_heap_usage (void *heap, clib_mem_usage_t * usage);
381
382 void *clib_mem_get_heap_base (void *heap);
383 uword clib_mem_get_heap_size (void *heap);
384 uword clib_mem_get_heap_free_space (void *heap);
385
386 u8 *format_clib_mem_usage (u8 * s, va_list * args);
387 u8 *format_clib_mem_heap (u8 * s, va_list * va);
388
389 /* Allocate virtual address space. */
390 always_inline void *
391 clib_mem_vm_alloc (uword size)
392 {
393   void *mmap_addr;
394   uword flags = MAP_PRIVATE;
395
396 #ifdef MAP_ANONYMOUS
397   flags |= MAP_ANONYMOUS;
398 #endif
399
400   mmap_addr = mmap (0, size, PROT_READ | PROT_WRITE, flags, -1, 0);
401   if (mmap_addr == (void *) -1)
402     mmap_addr = 0;
403   else
404     CLIB_MEM_UNPOISON (mmap_addr, size);
405
406   return mmap_addr;
407 }
408
409 always_inline void
410 clib_mem_vm_free (void *addr, uword size)
411 {
412   munmap (addr, size);
413 }
414
415 void *clib_mem_vm_map_internal (void *base, clib_mem_page_sz_t log2_page_sz,
416                                 uword size, int fd, uword offset, char *name);
417
418 void *clib_mem_vm_map (void *start, uword size,
419                        clib_mem_page_sz_t log2_page_size, char *fmt, ...);
420 void *clib_mem_vm_map_stack (uword size, clib_mem_page_sz_t log2_page_size,
421                              char *fmt, ...);
422 void *clib_mem_vm_map_shared (void *start, uword size, int fd, uword offset,
423                               char *fmt, ...);
424 int clib_mem_vm_unmap (void *base);
425 clib_mem_vm_map_hdr_t *clib_mem_vm_get_next_map_hdr (clib_mem_vm_map_hdr_t *
426                                                      hdr);
427
428 static_always_inline clib_mem_page_sz_t
429 clib_mem_get_log2_page_size (void)
430 {
431   return clib_mem_main.log2_page_sz;
432 }
433
434 static_always_inline uword
435 clib_mem_get_page_size (void)
436 {
437   return 1ULL << clib_mem_main.log2_page_sz;
438 }
439
440 static_always_inline clib_mem_page_sz_t
441 clib_mem_get_log2_default_hugepage_size ()
442 {
443   return clib_mem_main.log2_default_hugepage_sz;
444 }
445
446 int clib_mem_vm_create_fd (clib_mem_page_sz_t log2_page_size, char *fmt, ...);
447 uword clib_mem_get_fd_page_size (int fd);
448 uword clib_mem_get_default_hugepage_size (void);
449 clib_mem_page_sz_t clib_mem_get_fd_log2_page_size (int fd);
450 uword clib_mem_vm_reserve (uword start, uword size,
451                            clib_mem_page_sz_t log2_page_sz);
452 u64 *clib_mem_vm_get_paddr (void *mem, clib_mem_page_sz_t log2_page_size,
453                             int n_pages);
454 void clib_mem_destroy (void);
455 int clib_mem_set_numa_affinity (u8 numa_node, int force);
456 int clib_mem_set_default_numa_affinity ();
457
458 typedef struct
459 {
460   uword size;           /**< Map size */
461   int fd;               /**< File descriptor to be mapped */
462   uword requested_va;   /**< Request fixed position mapping */
463   void *addr;           /**< Pointer to mapped memory, if successful */
464   u8 numa_node;
465 } clib_mem_vm_map_t;
466
467 void clib_mem_vm_randomize_va (uword * requested_va,
468                                clib_mem_page_sz_t log2_page_size);
469 void mheap_trace (void *v, int enable);
470 uword clib_mem_trace_enable_disable (uword enable);
471 void clib_mem_trace (int enable);
472
473 always_inline uword
474 clib_mem_round_to_page_size (uword size, clib_mem_page_sz_t log2_page_size)
475 {
476   ASSERT (log2_page_size != CLIB_MEM_PAGE_SZ_UNKNOWN);
477
478   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT)
479     log2_page_size = clib_mem_get_log2_page_size ();
480   else if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT_HUGE)
481     log2_page_size = clib_mem_get_log2_default_hugepage_size ();
482
483   return round_pow2 (size, 1ULL << log2_page_size);
484 }
485
486 typedef struct
487 {
488   uword mapped;
489   uword not_mapped;
490   uword per_numa[CLIB_MAX_NUMAS];
491   uword unknown;
492 } clib_mem_page_stats_t;
493
494 void clib_mem_get_page_stats (void *start, clib_mem_page_sz_t log2_page_size,
495                               uword n_pages, clib_mem_page_stats_t * stats);
496
497 static_always_inline int
498 vlib_mem_get_next_numa_node (int numa)
499 {
500   clib_mem_main_t *mm = &clib_mem_main;
501   u32 bitmap = mm->numa_node_bitmap;
502
503   if (numa >= 0)
504     bitmap &= ~pow2_mask (numa + 1);
505   if (bitmap == 0)
506     return -1;
507
508   return count_trailing_zeros (bitmap);
509 }
510
511 static_always_inline clib_mem_page_sz_t
512 clib_mem_log2_page_size_validate (clib_mem_page_sz_t log2_page_size)
513 {
514   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT)
515     return clib_mem_get_log2_page_size ();
516   if (log2_page_size == CLIB_MEM_PAGE_SZ_DEFAULT_HUGE)
517     return clib_mem_get_log2_default_hugepage_size ();
518   return log2_page_size;
519 }
520
521 static_always_inline uword
522 clib_mem_page_bytes (clib_mem_page_sz_t log2_page_size)
523 {
524   return 1 << clib_mem_log2_page_size_validate (log2_page_size);
525 }
526
527 static_always_inline clib_error_t *
528 clib_mem_get_last_error (void)
529 {
530   return clib_mem_main.error;
531 }
532
533
534 #include <vppinfra/error.h>     /* clib_panic */
535
536 #endif /* _included_clib_mem_h */
537
538 /*
539  * fd.io coding-style-patch-verification: ON
540  *
541  * Local Variables:
542  * eval: (c-set-style "gnu")
543  * End:
544  */