api: Remove deprecated message from API
[vpp.git] / src / vppinfra / mem_dlmalloc.c
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 #include <vppinfra/format.h>
17 #include <vppinfra/dlmalloc.h>
18 #include <vppinfra/os.h>
19 #include <vppinfra/lock.h>
20 #include <vppinfra/hash.h>
21 #include <vppinfra/elf_clib.h>
22
23 typedef struct
24 {
25   /* Address of callers: outer first, inner last. */
26   uword callers[12];
27
28   /* Count of allocations with this traceback. */
29   u32 n_allocations;
30
31   /* Count of bytes allocated with this traceback. */
32   u32 n_bytes;
33
34   /* Offset of this item */
35   uword offset;
36 } mheap_trace_t;
37
38 typedef struct
39 {
40   clib_spinlock_t lock;
41
42   mheap_trace_t *traces;
43
44   /* Indices of free traces. */
45   u32 *trace_free_list;
46
47   /* Hash table mapping callers to trace index. */
48   uword *trace_by_callers;
49
50   /* Hash table mapping mheap offset to trace index. */
51   uword *trace_index_by_offset;
52
53   /* So we can easily shut off current segment trace, if any */
54   const clib_mem_heap_t *current_traced_mheap;
55
56 } mheap_trace_main_t;
57
58 mheap_trace_main_t mheap_trace_main;
59
60 static __thread int mheap_trace_thread_disable;
61
62 static void
63 mheap_get_trace_internal (const clib_mem_heap_t *heap, uword offset,
64                           uword size)
65 {
66   mheap_trace_main_t *tm = &mheap_trace_main;
67   mheap_trace_t *t;
68   uword i, n_callers, trace_index, *p;
69   mheap_trace_t trace;
70
71   if (heap != tm->current_traced_mheap || mheap_trace_thread_disable)
72     return;
73
74   /* Spurious Coverity warnings be gone. */
75   clib_memset (&trace, 0, sizeof (trace));
76
77   clib_spinlock_lock (&tm->lock);
78
79   /* heap could have changed while we were waiting on the lock */
80   if (heap != tm->current_traced_mheap)
81     goto out;
82
83   /* Turn off tracing for this thread to avoid embarrassment... */
84   mheap_trace_thread_disable = 1;
85
86   /* Skip our frame and mspace_get_aligned's frame */
87   n_callers = clib_backtrace (trace.callers, ARRAY_LEN (trace.callers), 2);
88   if (n_callers == 0)
89     goto out;
90
91   if (!tm->trace_by_callers)
92     tm->trace_by_callers =
93       hash_create_shmem (0, sizeof (trace.callers), sizeof (uword));
94
95   p = hash_get_mem (tm->trace_by_callers, &trace.callers);
96   if (p)
97     {
98       trace_index = p[0];
99       t = tm->traces + trace_index;
100     }
101   else
102     {
103       i = vec_len (tm->trace_free_list);
104       if (i > 0)
105         {
106           trace_index = tm->trace_free_list[i - 1];
107           vec_set_len (tm->trace_free_list, i - 1);
108         }
109       else
110         {
111           mheap_trace_t *old_start = tm->traces;
112           mheap_trace_t *old_end = vec_end (tm->traces);
113
114           vec_add2 (tm->traces, t, 1);
115
116           if (tm->traces != old_start)
117             {
118               hash_pair_t *p;
119               mheap_trace_t *q;
120             /* *INDENT-OFF* */
121             hash_foreach_pair (p, tm->trace_by_callers,
122             ({
123               q = uword_to_pointer (p->key, mheap_trace_t *);
124               ASSERT (q >= old_start && q < old_end);
125               p->key = pointer_to_uword (tm->traces + (q - old_start));
126             }));
127             /* *INDENT-ON* */
128             }
129           trace_index = t - tm->traces;
130         }
131
132       t = tm->traces + trace_index;
133       t[0] = trace;
134       t->n_allocations = 0;
135       t->n_bytes = 0;
136       hash_set_mem (tm->trace_by_callers, t->callers, trace_index);
137     }
138
139   t->n_allocations += 1;
140   t->n_bytes += size;
141   t->offset = offset;           /* keep a sample to autopsy */
142   hash_set (tm->trace_index_by_offset, offset, t - tm->traces);
143
144 out:
145   mheap_trace_thread_disable = 0;
146   clib_spinlock_unlock (&tm->lock);
147 }
148
149 static void
150 mheap_put_trace_internal (const clib_mem_heap_t *heap, uword offset,
151                           uword size)
152 {
153   mheap_trace_t *t;
154   uword trace_index, *p;
155   mheap_trace_main_t *tm = &mheap_trace_main;
156
157   if (heap != tm->current_traced_mheap || mheap_trace_thread_disable)
158     return;
159
160   clib_spinlock_lock (&tm->lock);
161
162   /* heap could have changed while we were waiting on the lock */
163   if (heap != tm->current_traced_mheap)
164     goto out;
165
166   /* Turn off tracing for this thread for a moment */
167   mheap_trace_thread_disable = 1;
168
169   p = hash_get (tm->trace_index_by_offset, offset);
170   if (!p)
171     goto out;
172
173   trace_index = p[0];
174   hash_unset (tm->trace_index_by_offset, offset);
175   ASSERT (trace_index < vec_len (tm->traces));
176
177   t = tm->traces + trace_index;
178   ASSERT (t->n_allocations > 0);
179   ASSERT (t->n_bytes >= size);
180   t->n_allocations -= 1;
181   t->n_bytes -= size;
182   if (t->n_allocations == 0)
183     {
184       hash_unset_mem (tm->trace_by_callers, t->callers);
185       vec_add1 (tm->trace_free_list, trace_index);
186       clib_memset (t, 0, sizeof (t[0]));
187     }
188
189 out:
190   mheap_trace_thread_disable = 0;
191   clib_spinlock_unlock (&tm->lock);
192 }
193
194 void
195 mheap_get_trace (uword offset, uword size)
196 {
197   mheap_get_trace_internal (clib_mem_get_heap (), offset, size);
198 }
199
200 void
201 mheap_put_trace (uword offset, uword size)
202 {
203   mheap_put_trace_internal (clib_mem_get_heap (), offset, size);
204 }
205
206 always_inline void
207 mheap_trace_main_free (mheap_trace_main_t * tm)
208 {
209   CLIB_SPINLOCK_ASSERT_LOCKED (&tm->lock);
210   tm->current_traced_mheap = 0;
211   vec_free (tm->traces);
212   vec_free (tm->trace_free_list);
213   hash_free (tm->trace_by_callers);
214   hash_free (tm->trace_index_by_offset);
215   mheap_trace_thread_disable = 0;
216 }
217
218 static clib_mem_heap_t *
219 clib_mem_create_heap_internal (void *base, uword size,
220                                clib_mem_page_sz_t log2_page_sz, int is_locked,
221                                char *name)
222 {
223   clib_mem_heap_t *h;
224   u8 flags = 0;
225   int sz = sizeof (clib_mem_heap_t);
226
227   if (base == 0)
228     {
229       log2_page_sz = clib_mem_log2_page_size_validate (log2_page_sz);
230       size = round_pow2 (size, clib_mem_page_bytes (log2_page_sz));
231       base = clib_mem_vm_map_internal (0, log2_page_sz, size, -1, 0,
232                                        "main heap");
233
234       if (base == CLIB_MEM_VM_MAP_FAILED)
235         return 0;
236
237       flags = CLIB_MEM_HEAP_F_UNMAP_ON_DESTROY;
238     }
239   else
240     log2_page_sz = CLIB_MEM_PAGE_SZ_UNKNOWN;
241
242   if (is_locked)
243     flags |= CLIB_MEM_HEAP_F_LOCKED;
244
245   h = base;
246   h->base = base;
247   h->size = size;
248   h->log2_page_sz = log2_page_sz;
249   h->flags = flags;
250   sz = strlen (name);
251   strcpy (h->name, name);
252   sz = round_pow2 (sz + sizeof (clib_mem_heap_t), 16);
253   h->mspace = create_mspace_with_base (base + sz, size - sz, is_locked);
254
255   mspace_disable_expand (h->mspace);
256
257   clib_mem_poison (mspace_least_addr (h->mspace),
258                    mspace_footprint (h->mspace));
259
260   return h;
261 }
262
263 /* Initialize CLIB heap based on memory/size given by user.
264    Set memory to 0 and CLIB will try to allocate its own heap. */
265 static void *
266 clib_mem_init_internal (void *base, uword size,
267                         clib_mem_page_sz_t log2_page_sz)
268 {
269   clib_mem_heap_t *h;
270
271   clib_mem_main_init ();
272
273   h = clib_mem_create_heap_internal (base, size, log2_page_sz,
274                                      1 /*is_locked */ , "main heap");
275
276   clib_mem_set_heap (h);
277
278   if (mheap_trace_main.lock == 0)
279     {
280       /* clib_spinlock_init() dynamically allocates the spinlock in the current
281        * per-cpu heap, but it is used for all traces accross all heaps and
282        * hence we can't really allocate it in the current per-cpu heap as it
283        * could be destroyed later */
284       static struct clib_spinlock_s mheap_trace_main_lock = {};
285       mheap_trace_main.lock = &mheap_trace_main_lock;
286     }
287
288   return h;
289 }
290
291 __clib_export void *
292 clib_mem_init (void *memory, uword memory_size)
293 {
294   return clib_mem_init_internal (memory, memory_size,
295                                  CLIB_MEM_PAGE_SZ_DEFAULT);
296 }
297
298 __clib_export void *
299 clib_mem_init_with_page_size (uword memory_size,
300                               clib_mem_page_sz_t log2_page_sz)
301 {
302   return clib_mem_init_internal (0, memory_size, log2_page_sz);
303 }
304
305 __clib_export void *
306 clib_mem_init_thread_safe (void *memory, uword memory_size)
307 {
308   return clib_mem_init_internal (memory, memory_size,
309                                  CLIB_MEM_PAGE_SZ_DEFAULT);
310 }
311
312 __clib_export void
313 clib_mem_destroy (void)
314 {
315   mheap_trace_main_t *tm = &mheap_trace_main;
316   clib_mem_heap_t *heap = clib_mem_get_heap ();
317
318   if (heap->mspace == tm->current_traced_mheap)
319     mheap_trace (heap, 0);
320
321   destroy_mspace (heap->mspace);
322   clib_mem_vm_unmap (heap);
323 }
324
325 __clib_export u8 *
326 format_clib_mem_usage (u8 *s, va_list *va)
327 {
328   int verbose = va_arg (*va, int);
329   return format (s, "$$$$ heap at %llx verbose %d", clib_mem_get_heap (),
330                  verbose);
331 }
332
333 /*
334  * Magic decoder ring for mallinfo stats (ala dlmalloc):
335  *
336  * size_t arena;     / * Non-mmapped space allocated (bytes) * /
337  * size_t ordblks;   / * Number of free chunks * /
338  * size_t smblks;    / * Number of free fastbin blocks * /
339  * size_t hblks;     / * Number of mmapped regions * /
340  * size_t hblkhd;    / * Space allocated in mmapped regions (bytes) * /
341  * size_t usmblks;   / * Maximum total allocated space (bytes) * /
342  * size_t fsmblks;   / * Space in freed fastbin blocks (bytes) * /
343  * size_t uordblks;  / * Total allocated space (bytes) * /
344  * size_t fordblks;  / * Total free space (bytes) * /
345  * size_t keepcost;  / * Top-most, releasable space (bytes) * /
346  *
347  */
348
349 u8 *
350 format_msize (u8 * s, va_list * va)
351 {
352   uword a = va_arg (*va, uword);
353
354   if (a >= 1ULL << 30)
355     s = format (s, "%.2fG", (((f64) a) / ((f64) (1ULL << 30))));
356   else if (a >= 1ULL << 20)
357     s = format (s, "%.2fM", (((f64) a) / ((f64) (1ULL << 20))));
358   else if (a >= 1ULL << 10)
359     s = format (s, "%.2fK", (((f64) a) / ((f64) (1ULL << 10))));
360   else
361     s = format (s, "%lld", a);
362   return s;
363 }
364
365 static int
366 mheap_trace_sort (const void *_t1, const void *_t2)
367 {
368   const mheap_trace_t *t1 = _t1;
369   const mheap_trace_t *t2 = _t2;
370   word cmp;
371
372   cmp = (word) t2->n_bytes - (word) t1->n_bytes;
373   if (!cmp)
374     cmp = (word) t2->n_allocations - (word) t1->n_allocations;
375   return cmp;
376 }
377
378 u8 *
379 format_mheap_trace (u8 * s, va_list * va)
380 {
381   mheap_trace_main_t *tm = va_arg (*va, mheap_trace_main_t *);
382   int verbose = va_arg (*va, int);
383   int have_traces = 0;
384   int i;
385   int n = 0;
386
387   clib_spinlock_lock (&tm->lock);
388   if (vec_len (tm->traces) > 0 &&
389       clib_mem_get_heap () == tm->current_traced_mheap)
390     {
391       have_traces = 1;
392
393       /* Make a copy of traces since we'll be sorting them. */
394       mheap_trace_t *t, *traces_copy;
395       u32 indent, total_objects_traced;
396
397       traces_copy = vec_dup (tm->traces);
398
399       qsort (traces_copy, vec_len (traces_copy), sizeof (traces_copy[0]),
400              mheap_trace_sort);
401
402       total_objects_traced = 0;
403       s = format (s, "\n");
404       vec_foreach (t, traces_copy)
405       {
406         /* Skip over free elements. */
407         if (t->n_allocations == 0)
408           continue;
409
410         total_objects_traced += t->n_allocations;
411
412         /* When not verbose only report the 50 biggest allocations */
413         if (!verbose && n >= 50)
414           continue;
415         n++;
416
417         if (t == traces_copy)
418           s = format (s, "%=9s%=9s %=10s Traceback\n", "Bytes", "Count",
419                       "Sample");
420         s = format (s, "%9d%9d %p", t->n_bytes, t->n_allocations, t->offset);
421         indent = format_get_indent (s);
422         for (i = 0; i < ARRAY_LEN (t->callers) && t->callers[i]; i++)
423           {
424             if (i > 0)
425               s = format (s, "%U", format_white_space, indent);
426 #if defined(CLIB_UNIX) && !defined(__APPLE__)
427             /* $$$$ does this actually work? */
428             s =
429               format (s, " %U\n", format_clib_elf_symbol_with_address,
430                       t->callers[i]);
431 #else
432             s = format (s, " %p\n", t->callers[i]);
433 #endif
434           }
435       }
436
437       s = format (s, "%d total traced objects\n", total_objects_traced);
438
439       vec_free (traces_copy);
440     }
441   clib_spinlock_unlock (&tm->lock);
442   if (have_traces == 0)
443     s = format (s, "no traced allocations\n");
444
445   return s;
446 }
447
448 __clib_export u8 *
449 format_clib_mem_heap (u8 * s, va_list * va)
450 {
451   clib_mem_heap_t *heap = va_arg (*va, clib_mem_heap_t *);
452   int verbose = va_arg (*va, int);
453   struct dlmallinfo mi;
454   mheap_trace_main_t *tm = &mheap_trace_main;
455   u32 indent = format_get_indent (s) + 2;
456
457   if (heap == 0)
458     heap = clib_mem_get_heap ();
459
460   mi = mspace_mallinfo (heap->mspace);
461
462   s = format (s, "base %p, size %U",
463               heap->base, format_memory_size, heap->size);
464
465 #define _(i,v,str) \
466   if (heap->flags & CLIB_MEM_HEAP_F_##v) s = format (s, ", %s", str);
467   foreach_clib_mem_heap_flag;
468 #undef _
469
470   s = format (s, ", name '%s'", heap->name);
471
472   if (heap->log2_page_sz != CLIB_MEM_PAGE_SZ_UNKNOWN)
473     {
474       clib_mem_page_stats_t stats;
475       clib_mem_get_page_stats (heap->base, heap->log2_page_sz,
476                                heap->size >> heap->log2_page_sz, &stats);
477       s = format (s, "\n%U%U", format_white_space, indent,
478                   format_clib_mem_page_stats, &stats);
479     }
480
481   s = format (s, "\n%Utotal: %U, used: %U, free: %U, trimmable: %U",
482               format_white_space, indent,
483               format_msize, mi.arena,
484               format_msize, mi.uordblks,
485               format_msize, mi.fordblks, format_msize, mi.keepcost);
486   if (verbose > 0)
487     {
488       s = format (s, "\n%Ufree chunks %llu free fastbin blks %llu",
489                   format_white_space, indent + 2, mi.ordblks, mi.smblks);
490       s = format (s, "\n%Umax total allocated %U",
491                   format_white_space, indent + 2, format_msize, mi.usmblks);
492     }
493
494   if (heap->flags & CLIB_MEM_HEAP_F_TRACED)
495     s = format (s, "\n%U", format_mheap_trace, tm, verbose);
496   return s;
497 }
498
499 __clib_export __clib_flatten void
500 clib_mem_get_heap_usage (clib_mem_heap_t *heap, clib_mem_usage_t *usage)
501 {
502   struct dlmallinfo mi = mspace_mallinfo (heap->mspace);
503
504   usage->bytes_total = mi.arena; /* non-mmapped space allocated from system */
505   usage->bytes_used = mi.uordblks;          /* total allocated space */
506   usage->bytes_free = mi.fordblks;          /* total free space */
507   usage->bytes_used_mmap = mi.hblkhd;       /* space in mmapped regions */
508   usage->bytes_max = mi.usmblks;            /* maximum total allocated space */
509   usage->bytes_free_reclaimed = mi.ordblks; /* number of free chunks */
510   usage->bytes_overhead = mi.keepcost; /* releasable (via malloc_trim) space */
511
512   /* Not supported */
513   usage->bytes_used_sbrk = 0;
514   usage->object_count = 0;
515 }
516
517 /* Call serial number for debugger breakpoints. */
518 uword clib_mem_validate_serial = 0;
519
520 __clib_export void
521 mheap_trace (clib_mem_heap_t * h, int enable)
522 {
523   mheap_trace_main_t *tm = &mheap_trace_main;
524
525   clib_spinlock_lock (&tm->lock);
526
527   if (tm->current_traced_mheap != 0 && tm->current_traced_mheap != h)
528     {
529       clib_warning ("tracing already enabled for another heap, ignoring");
530       goto out;
531     }
532
533   if (enable)
534     {
535       h->flags |= CLIB_MEM_HEAP_F_TRACED;
536       tm->current_traced_mheap = h;
537     }
538   else
539     {
540       h->flags &= ~CLIB_MEM_HEAP_F_TRACED;
541       mheap_trace_main_free (&mheap_trace_main);
542     }
543
544 out:
545   clib_spinlock_unlock (&tm->lock);
546 }
547
548 __clib_export void
549 clib_mem_trace (int enable)
550 {
551   void *current_heap = clib_mem_get_heap ();
552   mheap_trace (current_heap, enable);
553 }
554
555 int
556 clib_mem_is_traced (void)
557 {
558   clib_mem_heap_t *h = clib_mem_get_heap ();
559   return (h->flags &= CLIB_MEM_HEAP_F_TRACED) != 0;
560 }
561
562 __clib_export uword
563 clib_mem_trace_enable_disable (uword enable)
564 {
565   uword rv = !mheap_trace_thread_disable;
566   mheap_trace_thread_disable = !enable;
567   return rv;
568 }
569
570 __clib_export clib_mem_heap_t *
571 clib_mem_create_heap (void *base, uword size, int is_locked, char *fmt, ...)
572 {
573   clib_mem_page_sz_t log2_page_sz = clib_mem_get_log2_page_size ();
574   clib_mem_heap_t *h;
575   char *name;
576   u8 *s = 0;
577
578   if (fmt == 0)
579     {
580       name = "";
581     }
582   else if (strchr (fmt, '%'))
583     {
584       va_list va;
585       va_start (va, fmt);
586       s = va_format (0, fmt, &va);
587       vec_add1 (s, 0);
588       va_end (va);
589       name = (char *) s;
590     }
591   else
592     name = fmt;
593
594   h = clib_mem_create_heap_internal (base, size, log2_page_sz, is_locked,
595                                      name);
596   vec_free (s);
597   return h;
598 }
599
600 __clib_export void
601 clib_mem_destroy_heap (clib_mem_heap_t * h)
602 {
603   mheap_trace_main_t *tm = &mheap_trace_main;
604
605   if (h->mspace == tm->current_traced_mheap)
606     mheap_trace (h, 0);
607
608   destroy_mspace (h->mspace);
609   if (h->flags & CLIB_MEM_HEAP_F_UNMAP_ON_DESTROY)
610     clib_mem_vm_unmap (h->base);
611 }
612
613 __clib_export __clib_flatten uword
614 clib_mem_get_heap_free_space (clib_mem_heap_t *h)
615 {
616   struct dlmallinfo dlminfo = mspace_mallinfo (h->mspace);
617   return dlminfo.fordblks;
618 }
619
620 __clib_export __clib_flatten void *
621 clib_mem_get_heap_base (clib_mem_heap_t *h)
622 {
623   return h->base;
624 }
625
626 __clib_export __clib_flatten uword
627 clib_mem_get_heap_size (clib_mem_heap_t *heap)
628 {
629   return heap->size;
630 }
631
632 /* Memory allocator which may call os_out_of_memory() if it fails */
633 static inline void *
634 clib_mem_heap_alloc_inline (void *heap, uword size, uword align,
635                             int os_out_of_memory_on_failure)
636 {
637   clib_mem_heap_t *h = heap ? heap : clib_mem_get_per_cpu_heap ();
638   void *p;
639
640   align = clib_max (CLIB_MEM_MIN_ALIGN, align);
641
642   p = mspace_memalign (h->mspace, align, size);
643
644   if (PREDICT_FALSE (0 == p))
645     {
646       if (os_out_of_memory_on_failure)
647         os_out_of_memory ();
648       return 0;
649     }
650
651   if (PREDICT_FALSE (h->flags & CLIB_MEM_HEAP_F_TRACED))
652     mheap_get_trace_internal (h, pointer_to_uword (p), clib_mem_size (p));
653
654   clib_mem_unpoison (p, size);
655   return p;
656 }
657
658 /* Memory allocator which calls os_out_of_memory() when it fails */
659 __clib_export __clib_flatten void *
660 clib_mem_alloc (uword size)
661 {
662   return clib_mem_heap_alloc_inline (0, size, CLIB_MEM_MIN_ALIGN,
663                                      /* os_out_of_memory */ 1);
664 }
665
666 __clib_export __clib_flatten void *
667 clib_mem_alloc_aligned (uword size, uword align)
668 {
669   return clib_mem_heap_alloc_inline (0, size, align,
670                                      /* os_out_of_memory */ 1);
671 }
672
673 /* Memory allocator which calls os_out_of_memory() when it fails */
674 __clib_export __clib_flatten void *
675 clib_mem_alloc_or_null (uword size)
676 {
677   return clib_mem_heap_alloc_inline (0, size, CLIB_MEM_MIN_ALIGN,
678                                      /* os_out_of_memory */ 0);
679 }
680
681 __clib_export __clib_flatten void *
682 clib_mem_alloc_aligned_or_null (uword size, uword align)
683 {
684   return clib_mem_heap_alloc_inline (0, size, align,
685                                      /* os_out_of_memory */ 0);
686 }
687
688 __clib_export __clib_flatten void *
689 clib_mem_heap_alloc (void *heap, uword size)
690 {
691   return clib_mem_heap_alloc_inline (heap, size, CLIB_MEM_MIN_ALIGN,
692                                      /* os_out_of_memory */ 1);
693 }
694
695 __clib_export __clib_flatten void *
696 clib_mem_heap_alloc_aligned (void *heap, uword size, uword align)
697 {
698   return clib_mem_heap_alloc_inline (heap, size, align,
699                                      /* os_out_of_memory */ 1);
700 }
701
702 __clib_export __clib_flatten void *
703 clib_mem_heap_alloc_or_null (void *heap, uword size)
704 {
705   return clib_mem_heap_alloc_inline (heap, size, CLIB_MEM_MIN_ALIGN,
706                                      /* os_out_of_memory */ 0);
707 }
708
709 __clib_export __clib_flatten void *
710 clib_mem_heap_alloc_aligned_or_null (void *heap, uword size, uword align)
711 {
712   return clib_mem_heap_alloc_inline (heap, size, align,
713                                      /* os_out_of_memory */ 0);
714 }
715
716 __clib_export __clib_flatten void *
717 clib_mem_heap_realloc_aligned (void *heap, void *p, uword new_size,
718                                uword align)
719 {
720   uword old_alloc_size;
721   clib_mem_heap_t *h = heap ? heap : clib_mem_get_per_cpu_heap ();
722   void *new;
723
724   ASSERT (count_set_bits (align) == 1);
725
726   old_alloc_size = p ? mspace_usable_size (p) : 0;
727
728   if (new_size == old_alloc_size)
729     return p;
730
731   if (p && pointer_is_aligned (p, align) &&
732       mspace_realloc_in_place (h->mspace, p, new_size))
733     {
734       clib_mem_unpoison (p, new_size);
735       if (PREDICT_FALSE (h->flags & CLIB_MEM_HEAP_F_TRACED))
736         {
737           mheap_put_trace_internal (h, pointer_to_uword (p), old_alloc_size);
738           mheap_get_trace_internal (h, pointer_to_uword (p),
739                                     clib_mem_size (p));
740         }
741     }
742   else
743     {
744       new = clib_mem_heap_alloc_inline (h, new_size, align, 1);
745
746       clib_mem_unpoison (new, new_size);
747       if (old_alloc_size)
748         {
749           clib_mem_unpoison (p, old_alloc_size);
750           clib_memcpy_fast (new, p, clib_min (new_size, old_alloc_size));
751           clib_mem_heap_free (h, p);
752         }
753       p = new;
754     }
755
756   return p;
757 }
758
759 __clib_export __clib_flatten void *
760 clib_mem_heap_realloc (void *heap, void *p, uword new_size)
761 {
762   return clib_mem_heap_realloc_aligned (heap, p, new_size, CLIB_MEM_MIN_ALIGN);
763 }
764
765 __clib_export __clib_flatten void *
766 clib_mem_realloc_aligned (void *p, uword new_size, uword align)
767 {
768   return clib_mem_heap_realloc_aligned (0, p, new_size, align);
769 }
770
771 __clib_export __clib_flatten void *
772 clib_mem_realloc (void *p, uword new_size)
773 {
774   return clib_mem_heap_realloc_aligned (0, p, new_size, CLIB_MEM_MIN_ALIGN);
775 }
776
777 __clib_export __clib_flatten uword
778 clib_mem_heap_is_heap_object (void *heap, void *p)
779 {
780   clib_mem_heap_t *h = heap ? heap : clib_mem_get_per_cpu_heap ();
781   return mspace_is_heap_object (h->mspace, p);
782 }
783
784 __clib_export __clib_flatten uword
785 clib_mem_is_heap_object (void *p)
786 {
787   return clib_mem_heap_is_heap_object (0, p);
788 }
789
790 __clib_export __clib_flatten void
791 clib_mem_heap_free (void *heap, void *p)
792 {
793   clib_mem_heap_t *h = heap ? heap : clib_mem_get_per_cpu_heap ();
794   uword size = clib_mem_size (p);
795
796   /* Make sure object is in the correct heap. */
797   ASSERT (clib_mem_heap_is_heap_object (h, p));
798
799   if (PREDICT_FALSE (h->flags & CLIB_MEM_HEAP_F_TRACED))
800     mheap_put_trace_internal (h, pointer_to_uword (p), size);
801   clib_mem_poison (p, clib_mem_size (p));
802
803   mspace_free (h->mspace, p);
804 }
805
806 __clib_export __clib_flatten void
807 clib_mem_free (void *p)
808 {
809   clib_mem_heap_free (0, p);
810 }
811
812 __clib_export __clib_flatten uword
813 clib_mem_size (void *p)
814 {
815   return mspace_usable_size (p);
816 }
817
818 __clib_export void
819 clib_mem_free_s (void *p)
820 {
821   uword size = clib_mem_size (p);
822   clib_mem_unpoison (p, size);
823   memset_s_inline (p, size, 0, size);
824   clib_mem_free (p);
825 }