blob: 2769838d4ba6acd3bba9f552e759705f45b413e9 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
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#include <vppinfra/bitops.h>
39#include <vppinfra/hash.h>
40#include <vppinfra/format.h>
41#include <vppinfra/mheap.h>
42#include <vppinfra/os.h>
43#include <vppinfra/time.h>
jaszha030455c432019-06-12 16:01:19 -050044#include <vppinfra/lock.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070045
46#ifdef CLIB_UNIX
47#include <vppinfra/elf_clib.h>
48#endif
49
Dave Barachc3799992016-08-15 11:12:27 -040050static void mheap_get_trace (void *v, uword offset, uword size);
51static void mheap_put_trace (void *v, uword offset, uword size);
52static int mheap_trace_sort (const void *t1, const void *t2);
Ed Warnickecb9cada2015-12-08 15:45:58 -070053
Dave Barachc3799992016-08-15 11:12:27 -040054always_inline void
55mheap_maybe_lock (void *v)
Ed Warnickecb9cada2015-12-08 15:45:58 -070056{
Dave Barachc3799992016-08-15 11:12:27 -040057 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -070058 if (v && (h->flags & MHEAP_FLAG_THREAD_SAFE))
59 {
Damjan Marionf55f9b82017-05-10 21:06:28 +020060 u32 my_cpu = os_get_thread_index ();
Ed Warnickecb9cada2015-12-08 15:45:58 -070061 if (h->owner_cpu == my_cpu)
Dave Barachc3799992016-08-15 11:12:27 -040062 {
63 h->recursion_count++;
64 return;
65 }
66
Sirshak Das2f6d7bb2018-10-03 22:53:51 +000067 while (clib_atomic_test_and_set (&h->lock))
jaszha030455c432019-06-12 16:01:19 -050068 CLIB_PAUSE ();
Ed Warnickecb9cada2015-12-08 15:45:58 -070069
70 h->owner_cpu = my_cpu;
71 h->recursion_count = 1;
72 }
73}
74
Dave Barachc3799992016-08-15 11:12:27 -040075always_inline void
76mheap_maybe_unlock (void *v)
Ed Warnickecb9cada2015-12-08 15:45:58 -070077{
Dave Barachc3799992016-08-15 11:12:27 -040078 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -070079 if (v && h->flags & MHEAP_FLAG_THREAD_SAFE)
80 {
Damjan Marionf55f9b82017-05-10 21:06:28 +020081 ASSERT (os_get_thread_index () == h->owner_cpu);
Ed Warnickecb9cada2015-12-08 15:45:58 -070082 if (--h->recursion_count == 0)
Dave Barachc3799992016-08-15 11:12:27 -040083 {
84 h->owner_cpu = ~0;
85 CLIB_MEMORY_BARRIER ();
86 h->lock = 0;
87 }
Ed Warnickecb9cada2015-12-08 15:45:58 -070088 }
89}
90
91/* Find bin for objects with size at least n_user_data_bytes. */
92always_inline uword
93user_data_size_to_bin_index (uword n_user_data_bytes)
94{
95 uword n_user_data_words;
96 word small_bin, large_bin;
97
98 /* User size must be at least big enough to hold free elt. */
99 n_user_data_bytes = clib_max (n_user_data_bytes, MHEAP_MIN_USER_DATA_BYTES);
100
101 /* Round to words. */
Dave Barachc3799992016-08-15 11:12:27 -0400102 n_user_data_words =
103 (round_pow2 (n_user_data_bytes, MHEAP_USER_DATA_WORD_BYTES) /
104 MHEAP_USER_DATA_WORD_BYTES);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700105
106 ASSERT (n_user_data_words > 0);
Dave Barachc3799992016-08-15 11:12:27 -0400107 small_bin =
108 n_user_data_words -
109 (MHEAP_MIN_USER_DATA_BYTES / MHEAP_USER_DATA_WORD_BYTES);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700110 ASSERT (small_bin >= 0);
111
Dave Barachc3799992016-08-15 11:12:27 -0400112 large_bin =
113 MHEAP_N_SMALL_OBJECT_BINS + max_log2 (n_user_data_bytes) -
114 MHEAP_LOG2_N_SMALL_OBJECT_BINS;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700115
116 return small_bin < MHEAP_N_SMALL_OBJECT_BINS ? small_bin : large_bin;
117}
118
119always_inline uword
120mheap_elt_size_to_user_n_bytes (uword n_bytes)
121{
122 ASSERT (n_bytes >= sizeof (mheap_elt_t));
123 return (n_bytes - STRUCT_OFFSET_OF (mheap_elt_t, user_data));
124}
125
Dave Barachc3799992016-08-15 11:12:27 -0400126always_inline uword __attribute__ ((unused))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700127mheap_elt_size_to_user_n_words (uword n_bytes)
128{
129 ASSERT (n_bytes % MHEAP_USER_DATA_WORD_BYTES == 0);
Dave Barachc3799992016-08-15 11:12:27 -0400130 return mheap_elt_size_to_user_n_bytes (n_bytes) /
131 MHEAP_USER_DATA_WORD_BYTES;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700132}
133
134always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400135mheap_elt_set_size (void *v,
136 uword uoffset, uword n_user_data_bytes, uword is_free)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700137{
Dave Barachc3799992016-08-15 11:12:27 -0400138 mheap_elt_t *e, *n;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700139
140 e = mheap_elt_at_uoffset (v, uoffset);
141
142 ASSERT (n_user_data_bytes % MHEAP_USER_DATA_WORD_BYTES == 0);
Dave Barachb3d93da2016-08-03 14:34:38 -0400143
Ed Warnickecb9cada2015-12-08 15:45:58 -0700144 e->n_user_data = n_user_data_bytes / MHEAP_USER_DATA_WORD_BYTES;
145 e->is_free = is_free;
Dave Barachc3799992016-08-15 11:12:27 -0400146 ASSERT (e->prev_n_user_data * sizeof (e->user_data[0]) >=
147 MHEAP_MIN_USER_DATA_BYTES);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700148
149 n = mheap_next_elt (e);
150 n->prev_n_user_data = e->n_user_data;
151 n->prev_is_free = is_free;
152}
153
Dave Barachc3799992016-08-15 11:12:27 -0400154always_inline void
155set_first_free_elt_offset (mheap_t * h, uword bin, uword uoffset)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700156{
157 uword i0, i1;
158
159 h->first_free_elt_uoffset_by_bin[bin] = uoffset;
160
161 i0 = bin / BITS (h->non_empty_free_elt_heads[0]);
162 i1 = (uword) 1 << (uword) (bin % BITS (h->non_empty_free_elt_heads[0]));
163
164 ASSERT (i0 < ARRAY_LEN (h->non_empty_free_elt_heads));
Dave Barachb3d93da2016-08-03 14:34:38 -0400165 if (h->first_free_elt_uoffset_by_bin[bin] == MHEAP_GROUNDED)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700166 h->non_empty_free_elt_heads[i0] &= ~i1;
167 else
168 h->non_empty_free_elt_heads[i0] |= i1;
169}
170
171always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400172set_free_elt (void *v, uword uoffset, uword n_user_data_bytes)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700173{
Dave Barachc3799992016-08-15 11:12:27 -0400174 mheap_t *h = mheap_header (v);
175 mheap_elt_t *e = mheap_elt_at_uoffset (v, uoffset);
176 mheap_elt_t *n = mheap_next_elt (e);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700177 uword bin = user_data_size_to_bin_index (n_user_data_bytes);
178
179 ASSERT (n->prev_is_free);
180 ASSERT (e->is_free);
181
Dave Barachb3d93da2016-08-03 14:34:38 -0400182 e->free_elt.prev_uoffset = MHEAP_GROUNDED;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700183 e->free_elt.next_uoffset = h->first_free_elt_uoffset_by_bin[bin];
184
185 /* Fill in next free elt's previous pointer. */
Dave Barachb3d93da2016-08-03 14:34:38 -0400186 if (e->free_elt.next_uoffset != MHEAP_GROUNDED)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700187 {
Dave Barachc3799992016-08-15 11:12:27 -0400188 mheap_elt_t *nf = mheap_elt_at_uoffset (v, e->free_elt.next_uoffset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700189 ASSERT (nf->is_free);
190 nf->free_elt.prev_uoffset = uoffset;
191 }
192
193 set_first_free_elt_offset (h, bin, uoffset);
194}
195
196always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400197new_free_elt (void *v, uword uoffset, uword n_user_data_bytes)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700198{
199 mheap_elt_set_size (v, uoffset, n_user_data_bytes, /* is_free */ 1);
200 set_free_elt (v, uoffset, n_user_data_bytes);
201}
202
203always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400204remove_free_elt (void *v, mheap_elt_t * e, uword bin)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700205{
Dave Barachc3799992016-08-15 11:12:27 -0400206 mheap_t *h = mheap_header (v);
207 mheap_elt_t *p, *n;
Dave Barachb3d93da2016-08-03 14:34:38 -0400208#if CLIB_VEC64 > 0
209 u64 no, po;
210#else
Ed Warnickecb9cada2015-12-08 15:45:58 -0700211 u32 no, po;
Dave Barachb3d93da2016-08-03 14:34:38 -0400212#endif
Ed Warnickecb9cada2015-12-08 15:45:58 -0700213
214 no = e->free_elt.next_uoffset;
Dave Barachb3d93da2016-08-03 14:34:38 -0400215
216 n = no != MHEAP_GROUNDED ? mheap_elt_at_uoffset (v, no) : 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700217 po = e->free_elt.prev_uoffset;
Dave Barachb3d93da2016-08-03 14:34:38 -0400218 p = po != MHEAP_GROUNDED ? mheap_elt_at_uoffset (v, po) : 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700219
Dave Barachc3799992016-08-15 11:12:27 -0400220 if (!p)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700221 set_first_free_elt_offset (h, bin, no);
222 else
223 p->free_elt.next_uoffset = no;
224
225 if (n)
226 n->free_elt.prev_uoffset = po;
227}
228
229always_inline void
Dave Barachc3799992016-08-15 11:12:27 -0400230remove_free_elt2 (void *v, mheap_elt_t * e)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700231{
232 uword bin;
233 bin = user_data_size_to_bin_index (mheap_elt_data_bytes (e));
234 remove_free_elt (v, e, bin);
235}
236
237#define MHEAP_VM_MAP (1 << 0)
238#define MHEAP_VM_UNMAP (1 << 1)
239#define MHEAP_VM_NOMAP (0 << 1)
240#define MHEAP_VM_ROUND (1 << 2)
241#define MHEAP_VM_ROUND_UP MHEAP_VM_ROUND
242#define MHEAP_VM_ROUND_DOWN (0 << 2)
243
244static uword mheap_page_size;
245
Dave Barachc3799992016-08-15 11:12:27 -0400246static_always_inline uword
247mheap_page_round (uword addr)
248{
249 return (addr + mheap_page_size - 1) & ~(mheap_page_size - 1);
250}
Ed Warnickecb9cada2015-12-08 15:45:58 -0700251
252static_always_inline uword
Dave Barachc3799992016-08-15 11:12:27 -0400253mheap_page_truncate (uword addr)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700254{
Dave Barachc3799992016-08-15 11:12:27 -0400255 return addr & ~(mheap_page_size - 1);
256}
257
258static_always_inline uword
259mheap_vm (void *v, uword flags, clib_address_t start_addr, uword size)
260{
261 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700262 clib_address_t start_page, end_page, end_addr;
263 uword mapped_bytes;
264
Dave Barachc3799992016-08-15 11:12:27 -0400265 ASSERT (!(h->flags & MHEAP_FLAG_DISABLE_VM));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700266
267 end_addr = start_addr + size;
268
269 /* Round start/end address up to page boundary. */
270 start_page = mheap_page_round (start_addr);
271
272 if ((flags & MHEAP_VM_ROUND) == MHEAP_VM_ROUND_UP)
273 end_page = mheap_page_round (end_addr);
274 else
275 end_page = mheap_page_truncate (end_addr);
276
277 mapped_bytes = 0;
278 if (end_page > start_page)
279 {
280 mapped_bytes = end_page - start_page;
281 if (flags & MHEAP_VM_MAP)
282 clib_mem_vm_map ((void *) start_page, end_page - start_page);
283 else if (flags & MHEAP_VM_UNMAP)
284 clib_mem_vm_unmap ((void *) start_page, end_page - start_page);
285 }
286
287 return mapped_bytes;
288}
289
290static_always_inline uword
Dave Barachc3799992016-08-15 11:12:27 -0400291mheap_vm_elt (void *v, uword flags, uword offset)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700292{
Dave Barachc3799992016-08-15 11:12:27 -0400293 mheap_elt_t *e;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700294 clib_address_t start_addr, end_addr;
295
296 e = mheap_elt_at_uoffset (v, offset);
297 start_addr = (clib_address_t) ((void *) e->user_data);
298 end_addr = (clib_address_t) mheap_next_elt (e);
299 return mheap_vm (v, flags, start_addr, end_addr - start_addr);
300}
301
302always_inline uword
303mheap_small_object_cache_mask (mheap_small_object_cache_t * c, uword bin)
304{
305 uword mask;
306
307/* $$$$ ELIOT FIXME: add Altivec version of this routine */
Damjan Marion7bee80c2017-04-26 15:32:12 +0200308#if !defined (CLIB_HAVE_VEC128) || defined (__ALTIVEC__) || defined (__i386__)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700309 mask = 0;
310#else
311 u8x16 b = u8x16_splat (bin);
312
313 ASSERT (bin < 256);
314
Damjan Mariona52e1662018-05-19 00:04:23 +0200315#define _(i) ((uword) u8x16_compare_byte_mask ((b == c->bins.as_u8x16[i])) << (uword) ((i)*16))
Dave Barachc3799992016-08-15 11:12:27 -0400316 mask = _(0) | _(1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700317 if (BITS (uword) > 32)
Dave Barachc3799992016-08-15 11:12:27 -0400318 mask |= _(2) | _(3);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700319#undef _
320
321#endif
322 return mask;
323}
324
325always_inline uword
326mheap_get_small_object (mheap_t * h, uword bin)
327{
Dave Barachc3799992016-08-15 11:12:27 -0400328 mheap_small_object_cache_t *c = &h->small_object_cache;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700329 uword mask = mheap_small_object_cache_mask (c, bin + 1);
Dave Barachb3d93da2016-08-03 14:34:38 -0400330 uword offset = MHEAP_GROUNDED;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700331
332 if (mask)
333 {
334 uword i = min_log2 (mask);
335 uword o = c->offsets[i];
Dave Barachb3d93da2016-08-03 14:34:38 -0400336 ASSERT (o != MHEAP_GROUNDED);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700337 c->bins.as_u8[i] = 0;
338 offset = o;
339 }
340
341 return offset;
342}
343
344always_inline uword
345mheap_put_small_object (mheap_t * h, uword bin, uword offset)
346{
Dave Barachc3799992016-08-15 11:12:27 -0400347 mheap_small_object_cache_t *c = &h->small_object_cache;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700348 uword free_mask = mheap_small_object_cache_mask (c, 0);
349 uword b = bin + 1;
350 uword i;
351
352 if (free_mask != 0)
353 {
354 i = min_log2 (free_mask);
355 c->bins.as_u8[i] = b;
356 c->offsets[i] = offset;
357 return 0;
358 }
359 else
360 /* Nothing free with right size: cyclic replacement. */
361 {
362 uword old_offset;
363
364 i = c->replacement_index++;
365 i %= BITS (uword);
366 c->bins.as_u8[i] = b;
367 old_offset = c->offsets[i];
368 c->offsets[i] = offset;
369
370 /* Return old offset so it can be freed. */
371 return old_offset;
372 }
373}
374
375static uword
Dave Barachc3799992016-08-15 11:12:27 -0400376mheap_get_search_free_bin (void *v,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700377 uword bin,
378 uword * n_user_data_bytes_arg,
Dave Barachc3799992016-08-15 11:12:27 -0400379 uword align, uword align_offset)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700380{
Dave Barachc3799992016-08-15 11:12:27 -0400381 mheap_t *h = mheap_header (v);
382 mheap_elt_t *e;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700383
384 /* Free object is at offset f0 ... f1;
385 Allocatted object is at offset o0 ... o1. */
386 word o0, o1, f0, f1, search_n_user_data_bytes;
387 word lo_free_usize, hi_free_usize;
388
Dave Barachb3d93da2016-08-03 14:34:38 -0400389 ASSERT (h->first_free_elt_uoffset_by_bin[bin] != MHEAP_GROUNDED);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700390 e = mheap_elt_at_uoffset (v, h->first_free_elt_uoffset_by_bin[bin]);
391
392 search_n_user_data_bytes = *n_user_data_bytes_arg;
393
394 /* Silence compiler warning. */
395 o0 = o1 = f0 = f1 = 0;
396
397 h->stats.free_list.n_search_attempts += 1;
398
399 /* Find an object that is large enough with correct alignment at given alignment offset. */
400 while (1)
401 {
402 uword this_object_n_user_data_bytes = mheap_elt_data_bytes (e);
403
404 ASSERT (e->is_free);
405 if (bin < MHEAP_N_SMALL_OBJECT_BINS)
406 ASSERT (this_object_n_user_data_bytes >= search_n_user_data_bytes);
407
408 h->stats.free_list.n_objects_searched += 1;
409
410 if (this_object_n_user_data_bytes < search_n_user_data_bytes)
411 goto next;
412
413 /* Bounds of free object: from f0 to f1. */
414 f0 = ((void *) e->user_data - v);
415 f1 = f0 + this_object_n_user_data_bytes;
416
417 /* Place candidate object at end of free block and align as requested. */
Dave Barachc3799992016-08-15 11:12:27 -0400418 o0 = ((f1 - search_n_user_data_bytes) & ~(align - 1)) - align_offset;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700419 while (o0 < f0)
420 o0 += align;
421
422 /* Make sure that first free fragment is either empty or
Dave Barachc3799992016-08-15 11:12:27 -0400423 large enough to be valid. */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700424 while (1)
425 {
426 lo_free_usize = o0 != f0 ? o0 - f0 - MHEAP_ELT_OVERHEAD_BYTES : 0;
427 if (o0 <= f0 || lo_free_usize >= (word) MHEAP_MIN_USER_DATA_BYTES)
428 break;
429 o0 -= align;
430 }
431
432 o1 = o0 + search_n_user_data_bytes;
433
434 /* Does it fit? */
435 if (o0 >= f0 && o1 <= f1)
436 goto found;
437
438 next:
439 /* Reached end of free list without finding large enough object. */
Dave Barachb3d93da2016-08-03 14:34:38 -0400440 if (e->free_elt.next_uoffset == MHEAP_GROUNDED)
441 return MHEAP_GROUNDED;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700442
443 /* Otherwise keep searching for large enough object. */
444 e = mheap_elt_at_uoffset (v, e->free_elt.next_uoffset);
445 }
446
Dave Barachc3799992016-08-15 11:12:27 -0400447found:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700448 /* Free fragment at end. */
449 hi_free_usize = f1 != o1 ? f1 - o1 - MHEAP_ELT_OVERHEAD_BYTES : 0;
450
451 /* If fragment at end is too small to be a new object,
452 give user's object a bit more space than requested. */
453 if (hi_free_usize < (word) MHEAP_MIN_USER_DATA_BYTES)
454 {
455 search_n_user_data_bytes += f1 - o1;
456 o1 = f1;
457 hi_free_usize = 0;
458 }
459
460 /* Need to make sure that relevant memory areas are mapped. */
Dave Barachc3799992016-08-15 11:12:27 -0400461 if (!(h->flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700462 {
Dave Barachc3799992016-08-15 11:12:27 -0400463 mheap_elt_t *f0_elt = mheap_elt_at_uoffset (v, f0);
464 mheap_elt_t *f1_elt = mheap_elt_at_uoffset (v, f1);
465 mheap_elt_t *o0_elt = mheap_elt_at_uoffset (v, o0);
466 mheap_elt_t *o1_elt = mheap_elt_at_uoffset (v, o1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700467
468 uword f0_page_start, f0_page_end;
469 uword o0_page_start, o0_page_end;
470
471 /* Free elt is mapped. Addresses after that may not be mapped. */
472 f0_page_start = mheap_page_round (pointer_to_uword (f0_elt->user_data));
Dave Barachc3799992016-08-15 11:12:27 -0400473 f0_page_end = mheap_page_truncate (pointer_to_uword (f1_elt));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700474
475 o0_page_start = mheap_page_truncate (pointer_to_uword (o0_elt));
476 o0_page_end = mheap_page_round (pointer_to_uword (o1_elt->user_data));
477
478 if (o0_page_start < f0_page_start)
479 o0_page_start = f0_page_start;
480 if (o0_page_end > f0_page_end)
481 o0_page_end = f0_page_end;
482
483 if (o0_page_end > o0_page_start)
484 clib_mem_vm_map (uword_to_pointer (o0_page_start, void *),
485 o0_page_end - o0_page_start);
486 }
487
488 /* Remove free object from free list. */
489 remove_free_elt (v, e, bin);
490
491 /* Free fragment at begining. */
492 if (lo_free_usize > 0)
493 {
494 ASSERT (lo_free_usize >= (word) MHEAP_MIN_USER_DATA_BYTES);
495 mheap_elt_set_size (v, f0, lo_free_usize, /* is_free */ 1);
496 new_free_elt (v, f0, lo_free_usize);
497 }
498
499 mheap_elt_set_size (v, o0, search_n_user_data_bytes, /* is_free */ 0);
500
501 if (hi_free_usize > 0)
502 {
503 uword uo = o1 + MHEAP_ELT_OVERHEAD_BYTES;
504 mheap_elt_set_size (v, uo, hi_free_usize, /* is_free */ 1);
505 new_free_elt (v, uo, hi_free_usize);
506 }
507
508 /* Return actual size of block. */
509 *n_user_data_bytes_arg = search_n_user_data_bytes;
510
511 h->stats.free_list.n_objects_found += 1;
512
513 return o0;
514}
515
516/* Search free lists for object with given size and alignment. */
517static uword
Dave Barachc3799992016-08-15 11:12:27 -0400518mheap_get_search_free_list (void *v,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700519 uword * n_user_bytes_arg,
Dave Barachc3799992016-08-15 11:12:27 -0400520 uword align, uword align_offset)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700521{
Dave Barachc3799992016-08-15 11:12:27 -0400522 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700523 uword bin, n_user_bytes, i, bi;
524
525 n_user_bytes = *n_user_bytes_arg;
526 bin = user_data_size_to_bin_index (n_user_bytes);
527
528 if (MHEAP_HAVE_SMALL_OBJECT_CACHE
529 && (h->flags & MHEAP_FLAG_SMALL_OBJECT_CACHE)
530 && bin < 255
531 && align == STRUCT_SIZE_OF (mheap_elt_t, user_data[0])
532 && align_offset == 0)
533 {
534 uword r = mheap_get_small_object (h, bin);
535 h->stats.n_small_object_cache_attempts += 1;
Dave Barachb3d93da2016-08-03 14:34:38 -0400536 if (r != MHEAP_GROUNDED)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700537 {
538 h->stats.n_small_object_cache_hits += 1;
539 return r;
540 }
541 }
542
Dave Barachc3799992016-08-15 11:12:27 -0400543 for (i = bin / BITS (uword); i < ARRAY_LEN (h->non_empty_free_elt_heads);
544 i++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700545 {
546 uword non_empty_bin_mask = h->non_empty_free_elt_heads[i];
547
548 /* No need to search smaller bins. */
549 if (i == bin / BITS (uword))
550 non_empty_bin_mask &= ~pow2_mask (bin % BITS (uword));
551
552 /* Search each occupied free bin which is large enough. */
Dave Barachba7ddfe2017-05-17 20:20:50 -0400553 /* *INDENT-OFF* */
554 foreach_set_bit (bi, non_empty_bin_mask,
555 ({
556 uword r =
557 mheap_get_search_free_bin (v, bi + i * BITS (uword),
558 n_user_bytes_arg,
559 align,
560 align_offset);
561 if (r != MHEAP_GROUNDED) return r;
562 }));
563 /* *INDENT-ON* */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700564 }
565
Dave Barachb3d93da2016-08-03 14:34:38 -0400566 return MHEAP_GROUNDED;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700567}
568
569static never_inline void *
Dave Barachc3799992016-08-15 11:12:27 -0400570mheap_get_extend_vector (void *v,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700571 uword n_user_data_bytes,
572 uword align,
Dave Barachc3799992016-08-15 11:12:27 -0400573 uword align_offset, uword * offset_return)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700574{
575 /* Bounds of free and allocated objects (as above). */
576 uword f0, f1, o0, o1;
577 word free_size;
Dave Barachc3799992016-08-15 11:12:27 -0400578 mheap_t *h = mheap_header (v);
579 mheap_elt_t *e;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700580
581 if (_vec_len (v) == 0)
582 {
583 _vec_len (v) = MHEAP_ELT_OVERHEAD_BYTES;
584
585 /* Create first element of heap. */
586 e = mheap_elt_at_uoffset (v, _vec_len (v));
587 e->prev_n_user_data = MHEAP_N_USER_DATA_INVALID;
588 }
589
590 f0 = _vec_len (v);
591
592 o0 = round_pow2 (f0, align) - align_offset;
593 while (1)
594 {
595 free_size = o0 - f0 - MHEAP_ELT_OVERHEAD_BYTES;
596 if (o0 == f0 || free_size >= (word) sizeof (mheap_elt_t))
597 break;
598
599 o0 += align;
600 }
601
602 o1 = o0 + n_user_data_bytes;
603 f1 = o1 + MHEAP_ELT_OVERHEAD_BYTES;
Dave Barachc3799992016-08-15 11:12:27 -0400604
Ed Warnickecb9cada2015-12-08 15:45:58 -0700605 ASSERT (v != 0);
606 h = mheap_header (v);
607
608 /* Make sure we have space for object plus overhead. */
609 if (f1 > h->max_size)
610 {
Dave Barachb3d93da2016-08-03 14:34:38 -0400611 *offset_return = MHEAP_GROUNDED;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700612 return v;
613 }
614
615 _vec_len (v) = f1;
616
Dave Barachc3799992016-08-15 11:12:27 -0400617 if (!(h->flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700618 {
Dave Barachc3799992016-08-15 11:12:27 -0400619 mheap_elt_t *f0_elt = mheap_elt_at_uoffset (v, f0);
620 mheap_elt_t *f1_elt = mheap_elt_at_uoffset (v, f1);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700621
622 uword f0_page = mheap_page_round (pointer_to_uword (f0_elt->user_data));
623 uword f1_page = mheap_page_round (pointer_to_uword (f1_elt->user_data));
624
625 if (f1_page > f0_page)
626 mheap_vm (v, MHEAP_VM_MAP, f0_page, f1_page - f0_page);
627 }
628
629 if (free_size > 0)
630 new_free_elt (v, f0, free_size);
631
632 mheap_elt_set_size (v, o0, n_user_data_bytes, /* is_free */ 0);
633
634 /* Mark last element. */
635 e = mheap_elt_at_uoffset (v, f1);
636 e->n_user_data = MHEAP_N_USER_DATA_INVALID;
637
638 *offset_return = o0;
639
640 return v;
641}
642
Dave Barachc3799992016-08-15 11:12:27 -0400643void *
644mheap_get_aligned (void *v,
645 uword n_user_data_bytes,
646 uword align, uword align_offset, uword * offset_return)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700647{
Dave Barachc3799992016-08-15 11:12:27 -0400648 mheap_t *h;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700649 uword offset;
650 u64 cpu_times[2];
651
652 cpu_times[0] = clib_cpu_time_now ();
653
654 align = clib_max (align, STRUCT_SIZE_OF (mheap_elt_t, user_data[0]));
655 align = max_pow2 (align);
656
657 /* Correct align offset to be smaller than alignment. */
658 align_offset &= (align - 1);
659
660 /* Align offset must be multiple of minimum object size. */
661 if (align_offset % STRUCT_SIZE_OF (mheap_elt_t, user_data[0]) != 0)
662 {
Dave Barachb3d93da2016-08-03 14:34:38 -0400663 *offset_return = MHEAP_GROUNDED;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700664 return v;
665 }
666
Dave Barach9c949e72018-06-28 10:59:05 -0400667 /*
668 * Round requested size.
669 *
670 * Step 1: round up to the minimum object size.
671 * Step 2: round up to a multiple of the user data size (e.g. 4)
672 * Step 3: if non-trivial alignment requested, round up
673 * so that the object precisely fills a chunk
674 * as big as the alignment request.
675 *
676 * Step 3 prevents the code from going into "bin search hyperspace":
677 * looking at a huge number of fractional remainder chunks, none of which
678 * will satisfy the alignment constraint. This fixes an allocator
679 * performance issue when one requests a large number of 16 byte objects
680 * aligned to 64 bytes, to name one variation on the theme.
681 */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700682 n_user_data_bytes = clib_max (n_user_data_bytes, MHEAP_MIN_USER_DATA_BYTES);
Dave Barachc3799992016-08-15 11:12:27 -0400683 n_user_data_bytes =
684 round_pow2 (n_user_data_bytes,
685 STRUCT_SIZE_OF (mheap_elt_t, user_data[0]));
Dave Barach9c949e72018-06-28 10:59:05 -0400686 if (align > MHEAP_ELT_OVERHEAD_BYTES)
687 n_user_data_bytes = clib_max (n_user_data_bytes,
688 align - MHEAP_ELT_OVERHEAD_BYTES);
Dave Barachc3799992016-08-15 11:12:27 -0400689 if (!v)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700690 v = mheap_alloc (0, 64 << 20);
691
692 mheap_maybe_lock (v);
693
694 h = mheap_header (v);
695
696 if (h->flags & MHEAP_FLAG_VALIDATE)
697 mheap_validate (v);
698
699 /* First search free lists for object. */
Dave Barachc3799992016-08-15 11:12:27 -0400700 offset =
701 mheap_get_search_free_list (v, &n_user_data_bytes, align, align_offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700702
703 h = mheap_header (v);
704
705 /* If that fails allocate object at end of heap by extending vector. */
Dave Barachb3d93da2016-08-03 14:34:38 -0400706 if (offset == MHEAP_GROUNDED && _vec_len (v) < h->max_size)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700707 {
Dave Barachc3799992016-08-15 11:12:27 -0400708 v =
709 mheap_get_extend_vector (v, n_user_data_bytes, align, align_offset,
710 &offset);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700711 h = mheap_header (v);
Dave Barachb3d93da2016-08-03 14:34:38 -0400712 h->stats.n_vector_expands += offset != MHEAP_GROUNDED;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700713 }
714
715 *offset_return = offset;
Dave Barachb3d93da2016-08-03 14:34:38 -0400716 if (offset != MHEAP_GROUNDED)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700717 {
718 h->n_elts += 1;
719
720 if (h->flags & MHEAP_FLAG_TRACE)
721 {
722 /* Recursion block for case when we are traceing main clib heap. */
723 h->flags &= ~MHEAP_FLAG_TRACE;
724
725 mheap_get_trace (v, offset, n_user_data_bytes);
726
727 h->flags |= MHEAP_FLAG_TRACE;
728 }
729 }
730
731 if (h->flags & MHEAP_FLAG_VALIDATE)
732 mheap_validate (v);
733
734 mheap_maybe_unlock (v);
735
736 cpu_times[1] = clib_cpu_time_now ();
737 h->stats.n_clocks_get += cpu_times[1] - cpu_times[0];
738 h->stats.n_gets += 1;
739
740 return v;
741}
742
Dave Barachc3799992016-08-15 11:12:27 -0400743static void
744free_last_elt (void *v, mheap_elt_t * e)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700745{
Dave Barachc3799992016-08-15 11:12:27 -0400746 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700747
748 /* Possibly delete preceeding free element also. */
749 if (e->prev_is_free)
750 {
751 e = mheap_prev_elt (e);
752 remove_free_elt2 (v, e);
753 }
754
755 if (e->prev_n_user_data == MHEAP_N_USER_DATA_INVALID)
756 {
Dave Barachc3799992016-08-15 11:12:27 -0400757 if (!(h->flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700758 mheap_vm_elt (v, MHEAP_VM_UNMAP, mheap_elt_uoffset (v, e));
759 _vec_len (v) = 0;
760 }
761 else
762 {
763 uword uo = mheap_elt_uoffset (v, e);
Dave Barachc3799992016-08-15 11:12:27 -0400764 if (!(h->flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700765 mheap_vm_elt (v, MHEAP_VM_UNMAP, uo);
766 e->n_user_data = MHEAP_N_USER_DATA_INVALID;
767 _vec_len (v) = uo;
768 }
769}
770
Dave Barachc3799992016-08-15 11:12:27 -0400771void
772mheap_put (void *v, uword uoffset)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700773{
Dave Barachc3799992016-08-15 11:12:27 -0400774 mheap_t *h;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700775 uword n_user_data_bytes, bin;
Dave Barachc3799992016-08-15 11:12:27 -0400776 mheap_elt_t *e, *n;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700777 uword trace_uoffset, trace_n_user_data_bytes;
778 u64 cpu_times[2];
779
780 cpu_times[0] = clib_cpu_time_now ();
781
782 h = mheap_header (v);
783
784 mheap_maybe_lock (v);
785
786 if (h->flags & MHEAP_FLAG_VALIDATE)
787 mheap_validate (v);
788
789 ASSERT (h->n_elts > 0);
790 h->n_elts--;
791 h->stats.n_puts += 1;
792
793 e = mheap_elt_at_uoffset (v, uoffset);
794 n = mheap_next_elt (e);
795 n_user_data_bytes = mheap_elt_data_bytes (e);
796
797 trace_uoffset = uoffset;
798 trace_n_user_data_bytes = n_user_data_bytes;
799
800 bin = user_data_size_to_bin_index (n_user_data_bytes);
801 if (MHEAP_HAVE_SMALL_OBJECT_CACHE
Dave Barachc3799992016-08-15 11:12:27 -0400802 && bin < 255 && (h->flags & MHEAP_FLAG_SMALL_OBJECT_CACHE))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700803 {
804 uoffset = mheap_put_small_object (h, bin, uoffset);
Dave Barachc3799992016-08-15 11:12:27 -0400805 if (uoffset == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700806 goto done;
807
808 e = mheap_elt_at_uoffset (v, uoffset);
809 n = mheap_next_elt (e);
810 n_user_data_bytes = mheap_elt_data_bytes (e);
811 }
812
813 /* Assert that forward and back pointers are equal. */
814 if (e->n_user_data != n->prev_n_user_data)
815 os_panic ();
816
817 /* Forward and backwards is_free must agree. */
818 if (e->is_free != n->prev_is_free)
819 os_panic ();
820
821 /* Object was already freed. */
822 if (e->is_free)
823 os_panic ();
824
825 /* Special case: delete last element in heap. */
826 if (n->n_user_data == MHEAP_N_USER_DATA_INVALID)
827 free_last_elt (v, e);
828
829 else
830 {
831 uword f0, f1, n_combine;
832
833 f0 = uoffset;
834 f1 = f0 + n_user_data_bytes;
835 n_combine = 0;
836
837 if (e->prev_is_free)
838 {
Dave Barachc3799992016-08-15 11:12:27 -0400839 mheap_elt_t *p = mheap_prev_elt (e);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700840 f0 = mheap_elt_uoffset (v, p);
841 remove_free_elt2 (v, p);
842 n_combine++;
843 }
844
845 if (n->is_free)
846 {
Dave Barachc3799992016-08-15 11:12:27 -0400847 mheap_elt_t *m = mheap_next_elt (n);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700848 f1 = (void *) m - v;
849 remove_free_elt2 (v, n);
850 n_combine++;
851 }
852
853 if (n_combine)
854 mheap_elt_set_size (v, f0, f1 - f0, /* is_free */ 1);
855 else
856 e->is_free = n->prev_is_free = 1;
857 set_free_elt (v, f0, f1 - f0);
858
Dave Barachc3799992016-08-15 11:12:27 -0400859 if (!(h->flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700860 mheap_vm_elt (v, MHEAP_VM_UNMAP, f0);
861 }
862
Dave Barachc3799992016-08-15 11:12:27 -0400863done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700864 h = mheap_header (v);
865
866 if (h->flags & MHEAP_FLAG_TRACE)
867 {
868 /* Recursion block for case when we are traceing main clib heap. */
869 h->flags &= ~MHEAP_FLAG_TRACE;
870
871 mheap_put_trace (v, trace_uoffset, trace_n_user_data_bytes);
872
873 h->flags |= MHEAP_FLAG_TRACE;
874 }
875
876 if (h->flags & MHEAP_FLAG_VALIDATE)
877 mheap_validate (v);
878
879 mheap_maybe_unlock (v);
880
881 cpu_times[1] = clib_cpu_time_now ();
882 h->stats.n_clocks_put += cpu_times[1] - cpu_times[0];
883}
884
Dave Barachc3799992016-08-15 11:12:27 -0400885void *
886mheap_alloc_with_flags (void *memory, uword memory_size, uword flags)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700887{
Dave Barachc3799992016-08-15 11:12:27 -0400888 mheap_t *h;
889 void *v;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700890 uword size;
891
Dave Barachc3799992016-08-15 11:12:27 -0400892 if (!mheap_page_size)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700893 mheap_page_size = clib_mem_get_page_size ();
894
Dave Barachc3799992016-08-15 11:12:27 -0400895 if (!memory)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700896 {
897 /* No memory given, try to VM allocate some. */
898 memory = clib_mem_vm_alloc (memory_size);
Dave Barachc3799992016-08-15 11:12:27 -0400899 if (!memory)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700900 return 0;
901
902 /* No memory region implies we have virtual memory. */
903 flags &= ~MHEAP_FLAG_DISABLE_VM;
904 }
905
906 /* Make sure that given memory is page aligned. */
907 {
908 uword am, av, ah;
909
910 am = pointer_to_uword (memory);
911 av = mheap_page_round (am);
912 v = uword_to_pointer (av, void *);
913 h = mheap_header (v);
914 ah = pointer_to_uword (h);
915 while (ah < am)
916 ah += mheap_page_size;
917
918 h = uword_to_pointer (ah, void *);
919 v = mheap_vector (h);
920
Dave Barachc3799992016-08-15 11:12:27 -0400921 if (PREDICT_FALSE (memory + memory_size < v))
922 {
Pierre Pfister889178c2016-06-17 13:30:02 +0100923 /*
924 * This will happen when the requested memory_size is too
925 * small to cope with the heap header and/or memory alignment.
926 */
Dave Barachc3799992016-08-15 11:12:27 -0400927 clib_mem_vm_free (memory, memory_size);
Pierre Pfister889178c2016-06-17 13:30:02 +0100928 return 0;
Dave Barachc3799992016-08-15 11:12:27 -0400929 }
Pierre Pfister889178c2016-06-17 13:30:02 +0100930
Ed Warnickecb9cada2015-12-08 15:45:58 -0700931 size = memory + memory_size - v;
932 }
933
934 /* VM map header so we can use memory. */
Dave Barachc3799992016-08-15 11:12:27 -0400935 if (!(flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700936 clib_mem_vm_map (h, sizeof (h[0]));
937
938 /* Zero vector header: both heap header and vector length. */
Dave Barachb7b92992018-10-17 10:38:51 -0400939 clib_memset (h, 0, sizeof (h[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700940 _vec_len (v) = 0;
941
942 h->vm_alloc_offset_from_header = (void *) h - memory;
943 h->vm_alloc_size = memory_size;
944
945 h->max_size = size;
946 h->owner_cpu = ~0;
947
948 /* Set flags based on those given less builtin-flags. */
Dave Barachc3799992016-08-15 11:12:27 -0400949 h->flags |= (flags & ~MHEAP_FLAG_TRACE);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700950
951 /* Unmap remainder of heap until we will be ready to use it. */
Dave Barachc3799992016-08-15 11:12:27 -0400952 if (!(h->flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700953 mheap_vm (v, MHEAP_VM_UNMAP | MHEAP_VM_ROUND_UP,
954 (clib_address_t) v, h->max_size);
955
956 /* Initialize free list heads to empty. */
Dave Barachb7b92992018-10-17 10:38:51 -0400957 clib_memset (h->first_free_elt_uoffset_by_bin, 0xFF,
958 sizeof (h->first_free_elt_uoffset_by_bin));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700959
960 return v;
961}
962
Dave Barachc3799992016-08-15 11:12:27 -0400963void *
964mheap_alloc (void *memory, uword size)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700965{
966 uword flags = 0;
967
968 if (memory != 0)
969 flags |= MHEAP_FLAG_DISABLE_VM;
970
971#ifdef CLIB_HAVE_VEC128
972 flags |= MHEAP_FLAG_SMALL_OBJECT_CACHE;
973#endif
974
975 return mheap_alloc_with_flags (memory, size, flags);
976}
977
Dave Barachc3799992016-08-15 11:12:27 -0400978void *
Dave Barach6a5adc32018-07-04 10:56:23 -0400979mheap_alloc_with_lock (void *memory, uword size, int locked)
980{
981 uword flags = 0;
982 void *rv;
983
984 if (memory != 0)
985 flags |= MHEAP_FLAG_DISABLE_VM;
986
987#ifdef CLIB_HAVE_VEC128
988 flags |= MHEAP_FLAG_SMALL_OBJECT_CACHE;
989#endif
990
991 rv = mheap_alloc_with_flags (memory, size, flags);
992
993 if (rv && locked)
994 {
995 mheap_t *h = mheap_header (rv);
996 h->flags |= MHEAP_FLAG_THREAD_SAFE;
997 }
998 return rv;
999}
1000
1001void *
Dave Barachc3799992016-08-15 11:12:27 -04001002_mheap_free (void *v)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001003{
Dave Barachc3799992016-08-15 11:12:27 -04001004 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001005
1006 if (v)
Dave Barachc3799992016-08-15 11:12:27 -04001007 clib_mem_vm_free ((void *) h - h->vm_alloc_offset_from_header,
1008 h->vm_alloc_size);
1009
Ed Warnickecb9cada2015-12-08 15:45:58 -07001010 return 0;
1011}
1012
1013/* Call user's function with each object in heap. */
Dave Barachc3799992016-08-15 11:12:27 -04001014void
1015mheap_foreach (void *v,
1016 uword (*func) (void *arg, void *v, void *elt_data,
1017 uword elt_size), void *arg)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001018{
Dave Barachc3799992016-08-15 11:12:27 -04001019 mheap_elt_t *e;
1020 u8 *stack_heap, *clib_mem_mheap_save;
1021 u8 tmp_heap_memory[16 * 1024];
Ed Warnickecb9cada2015-12-08 15:45:58 -07001022
1023 mheap_maybe_lock (v);
1024
1025 if (vec_len (v) == 0)
1026 goto done;
1027
1028 clib_mem_mheap_save = 0;
1029 stack_heap = 0;
1030
1031 /* Allocate a new temporary heap on the stack.
1032 This is so that our hash table & user's callback function can
1033 themselves allocate memory somewhere without getting in the way
1034 of the heap we are looking at. */
1035 if (v == clib_mem_get_heap ())
1036 {
1037 stack_heap = mheap_alloc (tmp_heap_memory, sizeof (tmp_heap_memory));
1038 clib_mem_mheap_save = v;
1039 clib_mem_set_heap (stack_heap);
1040 }
1041
1042 for (e = v;
Dave Barachc3799992016-08-15 11:12:27 -04001043 e->n_user_data != MHEAP_N_USER_DATA_INVALID; e = mheap_next_elt (e))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001044 {
Dave Barachc3799992016-08-15 11:12:27 -04001045 void *p = mheap_elt_data (v, e);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001046 if (e->is_free)
1047 continue;
Dave Barachc3799992016-08-15 11:12:27 -04001048 if ((*func) (arg, v, p, mheap_elt_data_bytes (e)))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001049 break;
1050 }
1051
1052 /* Restore main CLIB heap. */
1053 if (clib_mem_mheap_save)
1054 clib_mem_set_heap (clib_mem_mheap_save);
1055
Dave Barachc3799992016-08-15 11:12:27 -04001056done:
Ed Warnickecb9cada2015-12-08 15:45:58 -07001057 mheap_maybe_unlock (v);
1058}
1059
1060/* Bytes in mheap header overhead not including data bytes. */
1061always_inline uword
Dave Barachc3799992016-08-15 11:12:27 -04001062mheap_bytes_overhead (void *v)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001063{
Dave Barachc3799992016-08-15 11:12:27 -04001064 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001065 return v ? sizeof (h[0]) + h->n_elts * sizeof (mheap_elt_t) : 0;
1066}
1067
1068/* Total number of bytes including both data and overhead. */
Dave Barachc3799992016-08-15 11:12:27 -04001069uword
1070mheap_bytes (void *v)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001071{
Dave Barachc3799992016-08-15 11:12:27 -04001072 return mheap_bytes_overhead (v) + vec_bytes (v);
1073}
1074
1075static void
1076mheap_usage_no_lock (void *v, clib_mem_usage_t * usage)
1077{
1078 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001079 uword used = 0, free = 0, free_vm_unmapped = 0;
1080
1081 if (vec_len (v) > 0)
1082 {
Dave Barachc3799992016-08-15 11:12:27 -04001083 mheap_elt_t *e;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001084
1085 for (e = v;
1086 e->n_user_data != MHEAP_N_USER_DATA_INVALID;
1087 e = mheap_next_elt (e))
1088 {
1089 uword size = mheap_elt_data_bytes (e);
1090 if (e->is_free)
1091 {
1092 free += size;
Dave Barachc3799992016-08-15 11:12:27 -04001093 if (!(h->flags & MHEAP_FLAG_DISABLE_VM))
Ed Warnickecb9cada2015-12-08 15:45:58 -07001094 free_vm_unmapped +=
1095 mheap_vm_elt (v, MHEAP_VM_NOMAP, mheap_elt_uoffset (v, e));
1096 }
1097 else
1098 used += size;
1099 }
1100 }
1101
1102 usage->object_count = mheap_elts (v);
1103 usage->bytes_total = mheap_bytes (v);
1104 usage->bytes_overhead = mheap_bytes_overhead (v);
1105 usage->bytes_max = mheap_max_size (v);
1106 usage->bytes_used = used;
1107 usage->bytes_free = free;
1108 usage->bytes_free_reclaimed = free_vm_unmapped;
1109}
1110
Dave Barachc3799992016-08-15 11:12:27 -04001111void
1112mheap_usage (void *v, clib_mem_usage_t * usage)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001113{
1114 mheap_maybe_lock (v);
1115 mheap_usage_no_lock (v, usage);
1116 mheap_maybe_unlock (v);
1117}
1118
Dave Barachc3799992016-08-15 11:12:27 -04001119static u8 *
1120format_mheap_byte_count (u8 * s, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001121{
1122 uword n_bytes = va_arg (*va, uword);
1123 if (n_bytes < 1024)
1124 return format (s, "%wd", n_bytes);
1125 else
1126 return format (s, "%wdk", n_bytes / 1024);
1127}
1128
1129/* Returns first corrupt heap element. */
Dave Barachc3799992016-08-15 11:12:27 -04001130static mheap_elt_t *
1131mheap_first_corrupt (void *v)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001132{
Dave Barachc3799992016-08-15 11:12:27 -04001133 mheap_elt_t *e, *n;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001134
1135 if (vec_len (v) == 0)
1136 return 0;
1137
1138 e = v;
1139 while (1)
1140 {
1141 if (e->n_user_data == MHEAP_N_USER_DATA_INVALID)
1142 break;
1143
1144 n = mheap_next_elt (e);
1145
1146 if (e->n_user_data != n->prev_n_user_data)
1147 return e;
1148
1149 if (e->is_free != n->prev_is_free)
1150 return e;
1151
1152 e = n;
1153 }
1154
1155 return 0;
1156}
1157
Dave Barachc3799992016-08-15 11:12:27 -04001158static u8 *
1159format_mheap_stats (u8 * s, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001160{
Dave Barachc3799992016-08-15 11:12:27 -04001161 mheap_t *h = va_arg (*va, mheap_t *);
1162 mheap_stats_t *st = &h->stats;
Christophe Fontained3c008d2017-10-02 18:10:54 +02001163 u32 indent = format_get_indent (s);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001164
Dave Barachc3799992016-08-15 11:12:27 -04001165 s =
1166 format (s,
1167 "alloc. from small object cache: %Ld hits %Ld attempts (%.2f%%) replacements %d",
1168 st->n_small_object_cache_hits, st->n_small_object_cache_attempts,
1169 (st->n_small_object_cache_attempts !=
1170 0 ? 100. * (f64) st->n_small_object_cache_hits /
1171 (f64) st->n_small_object_cache_attempts : 0.),
1172 h->small_object_cache.replacement_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001173
Dave Barachc3799992016-08-15 11:12:27 -04001174 s =
1175 format (s,
1176 "\n%Ualloc. from free-list: %Ld attempts, %Ld hits (%.2f%%), %Ld considered (per-attempt %.2f)",
1177 format_white_space, indent, st->free_list.n_search_attempts,
1178 st->free_list.n_objects_found,
1179 (st->free_list.n_search_attempts !=
1180 0 ? 100. * (f64) st->free_list.n_objects_found /
1181 (f64) st->free_list.n_search_attempts : 0.),
1182 st->free_list.n_objects_searched,
1183 (st->free_list.n_search_attempts !=
1184 0 ? (f64) st->free_list.n_objects_searched /
1185 (f64) st->free_list.n_search_attempts : 0.));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001186
1187 s = format (s, "\n%Ualloc. from vector-expand: %Ld",
Dave Barachc3799992016-08-15 11:12:27 -04001188 format_white_space, indent, st->n_vector_expands);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001189
1190 s = format (s, "\n%Uallocs: %Ld %.2f clocks/call",
1191 format_white_space, indent,
Dave Barachc3799992016-08-15 11:12:27 -04001192 st->n_gets, (f64) st->n_clocks_get / (f64) st->n_gets);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001193
1194 s = format (s, "\n%Ufrees: %Ld %.2f clocks/call",
1195 format_white_space, indent,
Dave Barachc3799992016-08-15 11:12:27 -04001196 st->n_puts, (f64) st->n_clocks_put / (f64) st->n_puts);
1197
Ed Warnickecb9cada2015-12-08 15:45:58 -07001198 return s;
1199}
1200
Dave Barachc3799992016-08-15 11:12:27 -04001201u8 *
1202format_mheap (u8 * s, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001203{
Dave Barachc3799992016-08-15 11:12:27 -04001204 void *v = va_arg (*va, u8 *);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001205 int verbose = va_arg (*va, int);
1206
Dave Barachc3799992016-08-15 11:12:27 -04001207 mheap_t *h;
Gabriel Gannee3ea7972017-10-12 10:53:31 +02001208 uword i, size;
1209 u32 indent;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001210 clib_mem_usage_t usage;
Dave Barachc3799992016-08-15 11:12:27 -04001211 mheap_elt_t *first_corrupt;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001212
1213 mheap_maybe_lock (v);
1214
1215 h = mheap_header (v);
1216
1217 mheap_usage_no_lock (v, &usage);
1218
1219 indent = format_get_indent (s);
1220
Dave Barachc3799992016-08-15 11:12:27 -04001221 s =
1222 format (s,
1223 "%d objects, %U of %U used, %U free, %U reclaimed, %U overhead",
1224 usage.object_count, format_mheap_byte_count, usage.bytes_used,
1225 format_mheap_byte_count, usage.bytes_total,
1226 format_mheap_byte_count, usage.bytes_free,
1227 format_mheap_byte_count, usage.bytes_free_reclaimed,
1228 format_mheap_byte_count, usage.bytes_overhead);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001229
1230 if (usage.bytes_max != ~0)
1231 s = format (s, ", %U capacity", format_mheap_byte_count, usage.bytes_max);
1232
1233 /* Show histogram of sizes. */
1234 if (verbose > 1)
1235 {
1236 uword hist[MHEAP_N_BINS];
Dave Barachc3799992016-08-15 11:12:27 -04001237 mheap_elt_t *e;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001238 uword i, n_hist;
1239
Dave Barachb7b92992018-10-17 10:38:51 -04001240 clib_memset (hist, 0, sizeof (hist));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001241
1242 n_hist = 0;
1243 for (e = v;
1244 e->n_user_data != MHEAP_N_USER_DATA_INVALID;
1245 e = mheap_next_elt (e))
1246 {
1247 uword n_user_data_bytes = mheap_elt_data_bytes (e);
1248 uword bin = user_data_size_to_bin_index (n_user_data_bytes);
Dave Barachc3799992016-08-15 11:12:27 -04001249 if (!e->is_free)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001250 {
1251 hist[bin] += 1;
1252 n_hist += 1;
1253 }
1254 }
1255
1256 s = format (s, "\n%U%=12s%=12s%=16s",
1257 format_white_space, indent + 2,
1258 "Size", "Count", "Fraction");
1259
1260 for (i = 0; i < ARRAY_LEN (hist); i++)
1261 {
1262 if (hist[i] == 0)
1263 continue;
1264 s = format (s, "\n%U%12d%12wd%16.4f",
1265 format_white_space, indent + 2,
Dave Barachc3799992016-08-15 11:12:27 -04001266 MHEAP_MIN_USER_DATA_BYTES +
1267 i * MHEAP_USER_DATA_WORD_BYTES, hist[i],
Ed Warnickecb9cada2015-12-08 15:45:58 -07001268 (f64) hist[i] / (f64) n_hist);
1269 }
1270 }
1271
1272 if (verbose)
1273 s = format (s, "\n%U%U",
Dave Barachc3799992016-08-15 11:12:27 -04001274 format_white_space, indent + 2, format_mheap_stats, h);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001275
1276 if ((h->flags & MHEAP_FLAG_TRACE) && vec_len (h->trace_main.traces) > 0)
1277 {
1278 /* Make a copy of traces since we'll be sorting them. */
Dave Barachc3799992016-08-15 11:12:27 -04001279 mheap_trace_t *t, *traces_copy;
Christophe Fontained3c008d2017-10-02 18:10:54 +02001280 u32 indent, total_objects_traced;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001281
1282 traces_copy = vec_dup (h->trace_main.traces);
1283 qsort (traces_copy, vec_len (traces_copy), sizeof (traces_copy[0]),
1284 mheap_trace_sort);
1285
1286 total_objects_traced = 0;
1287 s = format (s, "\n");
Dave Barachc3799992016-08-15 11:12:27 -04001288 vec_foreach (t, traces_copy)
1289 {
Ed Warnickecb9cada2015-12-08 15:45:58 -07001290 /* Skip over free elements. */
1291 if (t->n_allocations == 0)
1292 continue;
1293
1294 total_objects_traced += t->n_allocations;
1295
1296 /* When not verbose only report allocations of more than 1k. */
Dave Barachc3799992016-08-15 11:12:27 -04001297 if (!verbose && t->n_bytes < 1024)
1298 continue;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001299
1300 if (t == traces_copy)
Dave Barachc3799992016-08-15 11:12:27 -04001301 s = format (s, "%=9s%=9s %=10s Traceback\n", "Bytes", "Count",
1302 "Sample");
1303 s = format (s, "%9d%9d %p", t->n_bytes, t->n_allocations,
1304 t->offset + v);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001305 indent = format_get_indent (s);
1306 for (i = 0; i < ARRAY_LEN (t->callers) && t->callers[i]; i++)
1307 {
1308 if (i > 0)
1309 s = format (s, "%U", format_white_space, indent);
1310#ifdef CLIB_UNIX
Dave Barachc3799992016-08-15 11:12:27 -04001311 s =
1312 format (s, " %U\n", format_clib_elf_symbol_with_address,
1313 t->callers[i]);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001314#else
1315 s = format (s, " %p\n", t->callers[i]);
1316#endif
1317 }
1318 }
1319
1320 s = format (s, "%d total traced objects\n", total_objects_traced);
1321
1322 vec_free (traces_copy);
Dave Barachc3799992016-08-15 11:12:27 -04001323 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001324
1325 first_corrupt = mheap_first_corrupt (v);
1326 if (first_corrupt)
1327 {
1328 size = mheap_elt_data_bytes (first_corrupt);
1329 s = format (s, "\n first corrupt object: %p, size %wd\n %U",
Dave Barachc3799992016-08-15 11:12:27 -04001330 first_corrupt, size, format_hex_bytes, first_corrupt, size);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001331 }
1332
1333 /* FIXME. This output could be wrong in the unlikely case that format
1334 uses the same mheap as we are currently inspecting. */
1335 if (verbose > 1)
1336 {
Dave Barachc3799992016-08-15 11:12:27 -04001337 mheap_elt_t *e;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001338 uword i, o;
1339
1340 s = format (s, "\n");
1341
1342 e = mheap_elt_at_uoffset (v, 0);
1343 i = 0;
1344 while (1)
1345 {
1346 if ((i % 8) == 0)
1347 s = format (s, "%8d: ", i);
1348
1349 o = mheap_elt_uoffset (v, e);
1350
1351 if (e->is_free)
1352 s = format (s, "(%8d) ", o);
1353 else
1354 s = format (s, " %8d ", o);
1355
1356 if ((i % 8) == 7 || (i + 1) >= h->n_elts)
1357 s = format (s, "\n");
1358 }
1359 }
1360
1361 mheap_maybe_unlock (v);
1362
1363 return s;
1364}
1365
Dave Barachc3799992016-08-15 11:12:27 -04001366void
1367dmh (void *v)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001368{
Dave Barachc3799992016-08-15 11:12:27 -04001369 fformat (stderr, "%U", format_mheap, v, 1);
1370}
1371
1372static void
1373mheap_validate_breakpoint ()
1374{
1375 os_panic ();
1376}
1377
1378void
1379mheap_validate (void *v)
1380{
1381 mheap_t *h = mheap_header (v);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001382 uword i, s;
1383
1384 uword elt_count, elt_size;
1385 uword free_count_from_free_lists, free_size_from_free_lists;
1386 uword small_elt_free_count, small_elt_free_size;
1387
1388#define CHECK(x) if (! (x)) { mheap_validate_breakpoint (); os_panic (); }
1389
1390 if (vec_len (v) == 0)
1391 return;
1392
1393 mheap_maybe_lock (v);
1394
1395 /* Validate number of elements and size. */
1396 free_size_from_free_lists = free_count_from_free_lists = 0;
1397 for (i = 0; i < ARRAY_LEN (h->first_free_elt_uoffset_by_bin); i++)
1398 {
Dave Barachc3799992016-08-15 11:12:27 -04001399 mheap_elt_t *e, *n;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001400 uword is_first;
1401
Dave Barachb3d93da2016-08-03 14:34:38 -04001402 CHECK ((h->first_free_elt_uoffset_by_bin[i] != MHEAP_GROUNDED)
Dave Barachc3799992016-08-15 11:12:27 -04001403 ==
1404 ((h->non_empty_free_elt_heads[i /
1405 BITS (uword)] & ((uword) 1 <<
1406 (uword) (i %
1407 BITS
1408 (uword))))
1409 != 0));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001410
Dave Barachb3d93da2016-08-03 14:34:38 -04001411 if (h->first_free_elt_uoffset_by_bin[i] == MHEAP_GROUNDED)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001412 continue;
1413
1414 e = mheap_elt_at_uoffset (v, h->first_free_elt_uoffset_by_bin[i]);
1415 is_first = 1;
1416 while (1)
1417 {
1418 uword s;
1419
1420 n = mheap_next_elt (e);
1421
1422 /* Object must be marked free. */
1423 CHECK (e->is_free);
1424
1425 /* Next object's previous free bit must also be set. */
1426 CHECK (n->prev_is_free);
1427
1428 if (is_first)
Dave Barachb3d93da2016-08-03 14:34:38 -04001429 CHECK (e->free_elt.prev_uoffset == MHEAP_GROUNDED);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001430 is_first = 0;
1431
1432 s = mheap_elt_data_bytes (e);
1433 CHECK (user_data_size_to_bin_index (s) == i);
1434
1435 free_count_from_free_lists += 1;
1436 free_size_from_free_lists += s;
1437
Dave Barachb3d93da2016-08-03 14:34:38 -04001438 if (e->free_elt.next_uoffset == MHEAP_GROUNDED)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001439 break;
1440
1441 n = mheap_elt_at_uoffset (v, e->free_elt.next_uoffset);
1442
1443 /* Check free element linkages. */
1444 CHECK (n->free_elt.prev_uoffset == mheap_elt_uoffset (v, e));
1445
1446 e = n;
1447 }
1448 }
1449
1450 /* Go through small object cache. */
1451 small_elt_free_count = small_elt_free_size = 0;
1452 for (i = 0; i < ARRAY_LEN (h->small_object_cache.bins.as_u8); i++)
1453 {
1454 if (h->small_object_cache.bins.as_u8[i] != 0)
1455 {
Dave Barachc3799992016-08-15 11:12:27 -04001456 mheap_elt_t *e;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001457 uword b = h->small_object_cache.bins.as_u8[i] - 1;
1458 uword o = h->small_object_cache.offsets[i];
1459 uword s;
1460
1461 e = mheap_elt_at_uoffset (v, o);
1462
1463 /* Object must be allocated. */
Dave Barachc3799992016-08-15 11:12:27 -04001464 CHECK (!e->is_free);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001465
1466 s = mheap_elt_data_bytes (e);
1467 CHECK (user_data_size_to_bin_index (s) == b);
1468
1469 small_elt_free_count += 1;
1470 small_elt_free_size += s;
1471 }
1472 }
1473
1474 {
Dave Barachc3799992016-08-15 11:12:27 -04001475 mheap_elt_t *e, *n;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001476 uword elt_free_size, elt_free_count;
1477
1478 elt_count = elt_size = elt_free_size = elt_free_count = 0;
Dave Barachc3799992016-08-15 11:12:27 -04001479 for (e = v; e->n_user_data != MHEAP_N_USER_DATA_INVALID; e = n)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001480 {
1481 if (e->prev_n_user_data != MHEAP_N_USER_DATA_INVALID)
Dave Barachc3799992016-08-15 11:12:27 -04001482 CHECK (e->prev_n_user_data * sizeof (e->user_data[0]) >=
1483 MHEAP_MIN_USER_DATA_BYTES);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001484
Dave Barachc3799992016-08-15 11:12:27 -04001485 CHECK (e->n_user_data * sizeof (e->user_data[0]) >=
1486 MHEAP_MIN_USER_DATA_BYTES);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001487
1488 n = mheap_next_elt (e);
1489
1490 CHECK (e->is_free == n->prev_is_free);
1491
1492 elt_count++;
1493 s = mheap_elt_data_bytes (e);
1494 elt_size += s;
1495
1496 if (e->is_free)
1497 {
1498 elt_free_count++;
1499 elt_free_size += s;
1500 }
1501
1502 /* Consecutive free objects should have been combined. */
Dave Barachc3799992016-08-15 11:12:27 -04001503 CHECK (!(e->prev_is_free && n->prev_is_free));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001504 }
1505
1506 CHECK (free_count_from_free_lists == elt_free_count);
1507 CHECK (free_size_from_free_lists == elt_free_size);
1508 CHECK (elt_count == h->n_elts + elt_free_count + small_elt_free_count);
Dave Barachc3799992016-08-15 11:12:27 -04001509 CHECK (elt_size + (elt_count + 1) * MHEAP_ELT_OVERHEAD_BYTES ==
1510 vec_len (v));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001511 }
1512
1513 {
Dave Barachc3799992016-08-15 11:12:27 -04001514 mheap_elt_t *e, *n;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001515
Dave Barachc3799992016-08-15 11:12:27 -04001516 for (e = v; e->n_user_data == MHEAP_N_USER_DATA_INVALID; e = n)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001517 {
1518 n = mheap_next_elt (e);
1519 CHECK (e->n_user_data == n->prev_n_user_data);
1520 }
1521 }
1522
1523#undef CHECK
1524
1525 mheap_maybe_unlock (v);
1526
1527 h->validate_serial += 1;
1528}
1529
Dave Barachc3799992016-08-15 11:12:27 -04001530static void
1531mheap_get_trace (void *v, uword offset, uword size)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001532{
Dave Barachc3799992016-08-15 11:12:27 -04001533 mheap_t *h;
1534 mheap_trace_main_t *tm;
1535 mheap_trace_t *t;
1536 uword i, n_callers, trace_index, *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001537 mheap_trace_t trace;
1538
1539 /* Spurious Coverity warnings be gone. */
Dave Barachb7b92992018-10-17 10:38:51 -04001540 clib_memset (&trace, 0, sizeof (trace));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001541
1542 n_callers = clib_backtrace (trace.callers, ARRAY_LEN (trace.callers),
1543 /* Skip mheap_get_aligned's frame */ 1);
1544 if (n_callers == 0)
Dave Barachc3799992016-08-15 11:12:27 -04001545 return;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001546
1547 for (i = n_callers; i < ARRAY_LEN (trace.callers); i++)
1548 trace.callers[i] = 0;
1549
1550 h = mheap_header (v);
1551 tm = &h->trace_main;
1552
Dave Barachc3799992016-08-15 11:12:27 -04001553 if (!tm->trace_by_callers)
1554 tm->trace_by_callers =
Ole Troan73710c72018-06-04 22:27:49 +02001555 hash_create_shmem (0, sizeof (trace.callers), sizeof (uword));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001556
1557 p = hash_get_mem (tm->trace_by_callers, &trace.callers);
1558 if (p)
1559 {
1560 trace_index = p[0];
1561 t = tm->traces + trace_index;
1562 }
1563 else
1564 {
1565 i = vec_len (tm->trace_free_list);
1566 if (i > 0)
1567 {
1568 trace_index = tm->trace_free_list[i - 1];
1569 _vec_len (tm->trace_free_list) = i - 1;
1570 }
1571 else
1572 {
Dave Barachc3799992016-08-15 11:12:27 -04001573 mheap_trace_t *old_start = tm->traces;
1574 mheap_trace_t *old_end = vec_end (tm->traces);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001575
1576 vec_add2 (tm->traces, t, 1);
1577
Dave Barachc3799992016-08-15 11:12:27 -04001578 if (tm->traces != old_start)
1579 {
1580 hash_pair_t *p;
1581 mheap_trace_t *q;
1582 /* *INDENT-OFF* */
Damjan Marion607de1a2016-08-16 22:53:54 +02001583 hash_foreach_pair (p, tm->trace_by_callers,
Dave Barachc3799992016-08-15 11:12:27 -04001584 ({
1585 q = uword_to_pointer (p->key, mheap_trace_t *);
1586 ASSERT (q >= old_start && q < old_end);
Ed Warnickecb9cada2015-12-08 15:45:58 -07001587 p->key = pointer_to_uword (tm->traces + (q - old_start));
1588 }));
Dave Barachc3799992016-08-15 11:12:27 -04001589 /* *INDENT-ON* */
1590 }
Ed Warnickecb9cada2015-12-08 15:45:58 -07001591 trace_index = t - tm->traces;
1592 }
1593
1594 t = tm->traces + trace_index;
1595 t[0] = trace;
1596 t->n_allocations = 0;
1597 t->n_bytes = 0;
1598 hash_set_mem (tm->trace_by_callers, t->callers, trace_index);
1599 }
1600
1601 t->n_allocations += 1;
1602 t->n_bytes += size;
Dave Barachc3799992016-08-15 11:12:27 -04001603 t->offset = offset; /* keep a sample to autopsy */
Ed Warnickecb9cada2015-12-08 15:45:58 -07001604 hash_set (tm->trace_index_by_offset, offset, t - tm->traces);
1605}
1606
Dave Barachc3799992016-08-15 11:12:27 -04001607static void
1608mheap_put_trace (void *v, uword offset, uword size)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001609{
Dave Barachc3799992016-08-15 11:12:27 -04001610 mheap_t *h;
1611 mheap_trace_main_t *tm;
1612 mheap_trace_t *t;
1613 uword trace_index, *p;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001614
1615 h = mheap_header (v);
1616 tm = &h->trace_main;
1617 p = hash_get (tm->trace_index_by_offset, offset);
Dave Barachc3799992016-08-15 11:12:27 -04001618 if (!p)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001619 return;
1620
1621 trace_index = p[0];
1622 hash_unset (tm->trace_index_by_offset, offset);
1623 ASSERT (trace_index < vec_len (tm->traces));
1624
1625 t = tm->traces + trace_index;
1626 ASSERT (t->n_allocations > 0);
1627 ASSERT (t->n_bytes >= size);
1628 t->n_allocations -= 1;
1629 t->n_bytes -= size;
1630 if (t->n_allocations == 0)
1631 {
1632 hash_unset_mem (tm->trace_by_callers, t->callers);
1633 vec_add1 (tm->trace_free_list, trace_index);
Dave Barachb7b92992018-10-17 10:38:51 -04001634 clib_memset (t, 0, sizeof (t[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -07001635 }
1636}
1637
Dave Barachc3799992016-08-15 11:12:27 -04001638static int
1639mheap_trace_sort (const void *_t1, const void *_t2)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001640{
Dave Barachc3799992016-08-15 11:12:27 -04001641 const mheap_trace_t *t1 = _t1;
1642 const mheap_trace_t *t2 = _t2;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001643 word cmp;
1644
1645 cmp = (word) t2->n_bytes - (word) t1->n_bytes;
Dave Barachc3799992016-08-15 11:12:27 -04001646 if (!cmp)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001647 cmp = (word) t2->n_allocations - (word) t1->n_allocations;
1648 return cmp;
1649}
1650
1651always_inline void
1652mheap_trace_main_free (mheap_trace_main_t * tm)
1653{
1654 vec_free (tm->traces);
1655 vec_free (tm->trace_free_list);
1656 hash_free (tm->trace_by_callers);
1657 hash_free (tm->trace_index_by_offset);
1658}
1659
Dave Barachc3799992016-08-15 11:12:27 -04001660void
1661mheap_trace (void *v, int enable)
Ed Warnickecb9cada2015-12-08 15:45:58 -07001662{
Dave Barachc3799992016-08-15 11:12:27 -04001663 mheap_t *h;
Ed Warnickecb9cada2015-12-08 15:45:58 -07001664
1665 h = mheap_header (v);
1666
1667 if (enable)
1668 {
1669 h->flags |= MHEAP_FLAG_TRACE;
1670 }
1671 else
1672 {
1673 mheap_trace_main_free (&h->trace_main);
1674 h->flags &= ~MHEAP_FLAG_TRACE;
1675 }
1676}
Dave Barachc3799992016-08-15 11:12:27 -04001677
1678/*
1679 * fd.io coding-style-patch-verification: ON
1680 *
1681 * Local Variables:
1682 * eval: (c-set-style "gnu")
1683 * End:
1684 */