Type: fix Change-Id: If59a66aae658dd35dbcb4987ab00c306b3c6e2e2 Signed-off-by: Damjan Marion <damarion@cisco.com>
833 lines
18 KiB
C
833 lines
18 KiB
C
/*
|
|
* Copyright (c) 2015 Cisco and/or its affiliates.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*
|
|
Copyright (c) 2001, 2002, 2003 Eliot Dresselhaus
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
"Software"), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <vppinfra/cache.h> /* for CLIB_CACHE_LINE_BYTES */
|
|
#include <vppinfra/mem.h>
|
|
#include <vppinfra/hash.h>
|
|
#include <vppinfra/vec.h>
|
|
#include <vppinfra/heap.h>
|
|
#include <vppinfra/error.h>
|
|
|
|
always_inline heap_elt_t *
|
|
elt_at (heap_header_t * h, uword i)
|
|
{
|
|
ASSERT (i < vec_len (h->elts));
|
|
return h->elts + i;
|
|
}
|
|
|
|
always_inline heap_elt_t *
|
|
last (heap_header_t * h)
|
|
{
|
|
return elt_at (h, h->tail);
|
|
}
|
|
|
|
always_inline heap_elt_t *
|
|
first (heap_header_t * h)
|
|
{
|
|
return elt_at (h, h->head);
|
|
}
|
|
|
|
/* Objects sizes are binned into N_BINS bins.
|
|
Objects with size <= SMALL_BINS have their own bins.
|
|
Larger objects are grouped together in power or 2 sized
|
|
bins.
|
|
|
|
Sizes are in units of elt_bytes bytes. */
|
|
|
|
/* Convert size to bin. */
|
|
always_inline uword
|
|
size_to_bin (uword size)
|
|
{
|
|
uword bin;
|
|
|
|
ASSERT (size > 0);
|
|
|
|
if (size <= HEAP_SMALL_BINS)
|
|
{
|
|
bin = size - 1;
|
|
if (size == 0)
|
|
bin = 0;
|
|
}
|
|
else
|
|
{
|
|
bin = HEAP_SMALL_BINS + max_log2 (size) - (HEAP_LOG2_SMALL_BINS + 1);
|
|
if (bin >= HEAP_N_BINS)
|
|
bin = HEAP_N_BINS - 1;
|
|
}
|
|
|
|
return bin;
|
|
}
|
|
|
|
/* Convert bin to size. */
|
|
always_inline __attribute__ ((unused))
|
|
uword bin_to_size (uword bin)
|
|
{
|
|
uword size;
|
|
|
|
if (bin <= HEAP_SMALL_BINS - 1)
|
|
size = bin + 1;
|
|
else
|
|
size = (uword) 1 << ((bin - HEAP_SMALL_BINS) + HEAP_LOG2_SMALL_BINS + 1);
|
|
|
|
return size;
|
|
}
|
|
|
|
static void
|
|
elt_delete (heap_header_t * h, heap_elt_t * e)
|
|
{
|
|
heap_elt_t *l = vec_end (h->elts) - 1;
|
|
|
|
ASSERT (e >= h->elts && e <= l);
|
|
|
|
/* Update doubly linked pointers. */
|
|
{
|
|
heap_elt_t *p = heap_prev (e);
|
|
heap_elt_t *n = heap_next (e);
|
|
|
|
if (p == e)
|
|
{
|
|
n->prev = 0;
|
|
h->head = n - h->elts;
|
|
}
|
|
else if (n == e)
|
|
{
|
|
p->next = 0;
|
|
h->tail = p - h->elts;
|
|
}
|
|
else
|
|
{
|
|
p->next = n - p;
|
|
n->prev = p - n;
|
|
}
|
|
}
|
|
|
|
/* Add to index free list or delete from end. */
|
|
if (e < l)
|
|
vec_add1 (h->free_elts, e - h->elts);
|
|
else
|
|
_vec_len (h->elts)--;
|
|
}
|
|
|
|
/*
|
|
Before: P ... E
|
|
After : P ... NEW ... E
|
|
*/
|
|
always_inline void
|
|
elt_insert_before (heap_header_t * h, heap_elt_t * e, heap_elt_t * new)
|
|
{
|
|
heap_elt_t *p = heap_prev (e);
|
|
|
|
if (p == e)
|
|
{
|
|
new->prev = 0;
|
|
new->next = e - new;
|
|
p->prev = new - p;
|
|
h->head = new - h->elts;
|
|
}
|
|
else
|
|
{
|
|
new->prev = p - new;
|
|
new->next = e - new;
|
|
e->prev = new - e;
|
|
p->next = new - p;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Before: E ... N
|
|
After : E ... NEW ... N
|
|
*/
|
|
always_inline void
|
|
elt_insert_after (heap_header_t * h, heap_elt_t * e, heap_elt_t * new)
|
|
{
|
|
heap_elt_t *n = heap_next (e);
|
|
|
|
if (n == e)
|
|
{
|
|
new->next = 0;
|
|
new->prev = e - new;
|
|
e->next = new - e;
|
|
h->tail = new - h->elts;
|
|
}
|
|
else
|
|
{
|
|
new->prev = e - new;
|
|
new->next = n - new;
|
|
e->next = new - e;
|
|
n->prev = new - n;
|
|
}
|
|
}
|
|
|
|
always_inline heap_elt_t *
|
|
elt_new (heap_header_t * h)
|
|
{
|
|
heap_elt_t *e;
|
|
uword l;
|
|
if ((l = vec_len (h->free_elts)) > 0)
|
|
{
|
|
e = elt_at (h, h->free_elts[l - 1]);
|
|
_vec_len (h->free_elts) -= 1;
|
|
}
|
|
else
|
|
vec_add2 (h->elts, e, 1);
|
|
return e;
|
|
}
|
|
|
|
/* Return pointer to object at given offset.
|
|
Used to write free list index of free objects. */
|
|
always_inline u32 *
|
|
elt_data (void *v, heap_elt_t * e)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
return v + heap_offset (e) * h->elt_bytes;
|
|
}
|
|
|
|
always_inline void
|
|
set_free_elt (void *v, heap_elt_t * e, uword fi)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
|
|
e->offset |= HEAP_ELT_FREE_BIT;
|
|
if (h->elt_bytes >= sizeof (u32))
|
|
{
|
|
*elt_data (v, e) = fi;
|
|
}
|
|
else
|
|
{
|
|
/* For elt_bytes < 4 we must store free index in separate
|
|
vector. */
|
|
uword elt_index = e - h->elts;
|
|
vec_validate (h->small_free_elt_free_index, elt_index);
|
|
h->small_free_elt_free_index[elt_index] = fi;
|
|
}
|
|
}
|
|
|
|
always_inline uword
|
|
get_free_elt (void *v, heap_elt_t * e, uword * bin_result)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
uword fb, fi;
|
|
|
|
ASSERT (heap_is_free (e));
|
|
fb = size_to_bin (heap_elt_size (v, e));
|
|
|
|
if (h->elt_bytes >= sizeof (u32))
|
|
{
|
|
fi = *elt_data (v, e);
|
|
}
|
|
else
|
|
{
|
|
uword elt_index = e - h->elts;
|
|
fi = vec_elt (h->small_free_elt_free_index, elt_index);
|
|
}
|
|
|
|
*bin_result = fb;
|
|
return fi;
|
|
}
|
|
|
|
always_inline void
|
|
remove_free_block (void *v, uword b, uword i)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
uword l;
|
|
|
|
ASSERT (b < vec_len (h->free_lists));
|
|
ASSERT (i < vec_len (h->free_lists[b]));
|
|
|
|
l = vec_len (h->free_lists[b]);
|
|
|
|
if (i < l - 1)
|
|
{
|
|
uword t = h->free_lists[b][l - 1];
|
|
h->free_lists[b][i] = t;
|
|
set_free_elt (v, elt_at (h, t), i);
|
|
}
|
|
_vec_len (h->free_lists[b]) = l - 1;
|
|
}
|
|
|
|
static heap_elt_t *
|
|
search_free_list (void *v, uword size)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
heap_elt_t *f, *u;
|
|
uword b, fb, f_size, f_index;
|
|
word s, l;
|
|
|
|
if (!v)
|
|
return 0;
|
|
|
|
/* Search free lists for bins >= given size. */
|
|
for (b = size_to_bin (size); b < vec_len (h->free_lists); b++)
|
|
if ((l = vec_len (h->free_lists[b])) > 0)
|
|
{
|
|
/* Find an object that is large enough.
|
|
Search list in reverse so that more recently freed objects will be
|
|
allocated again sooner. */
|
|
u8 found = 0;
|
|
do
|
|
{
|
|
l--;
|
|
f_index = h->free_lists[b][l];
|
|
f = elt_at (h, f_index);
|
|
f_size = heap_elt_size (v, f);
|
|
if ((s = f_size - size) >= 0)
|
|
{
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
while (l > 0);
|
|
|
|
/* If we fail to find a large enough object, try the next larger size. */
|
|
if (found == 0)
|
|
continue;
|
|
|
|
ASSERT (heap_is_free (f));
|
|
|
|
/* Link in used object (u) after free object (f). */
|
|
if (s == 0)
|
|
{
|
|
u = f;
|
|
fb = HEAP_N_BINS;
|
|
}
|
|
else
|
|
{
|
|
u = elt_new (h);
|
|
f = elt_at (h, f_index);
|
|
elt_insert_after (h, f, u);
|
|
fb = size_to_bin (s);
|
|
}
|
|
|
|
u->offset = heap_offset (f) + s;
|
|
|
|
if (fb != b)
|
|
{
|
|
if (fb < HEAP_N_BINS)
|
|
{
|
|
uword i;
|
|
vec_validate (h->free_lists, fb);
|
|
i = vec_len (h->free_lists[fb]);
|
|
vec_add1 (h->free_lists[fb], f - h->elts);
|
|
set_free_elt (v, f, i);
|
|
}
|
|
|
|
remove_free_block (v, b, l);
|
|
}
|
|
|
|
return u;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void combine_free_blocks (void *v, heap_elt_t * e0, heap_elt_t * e1);
|
|
|
|
static inline void
|
|
dealloc_elt (void *v, heap_elt_t * e)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
uword b, l;
|
|
heap_elt_t *n, *p;
|
|
|
|
b = size_to_bin (heap_elt_size (v, e));
|
|
vec_validate (h->free_lists, b);
|
|
l = vec_len (h->free_lists[b]);
|
|
vec_add1 (h->free_lists[b], e - h->elts);
|
|
set_free_elt (v, e, l);
|
|
|
|
/* See if we can combine the block we just freed with neighboring free blocks. */
|
|
p = heap_prev (e);
|
|
if (!heap_is_free (p))
|
|
p = e;
|
|
|
|
n = heap_next (e);
|
|
if (!heap_is_free (n))
|
|
n = e;
|
|
|
|
if (p != n)
|
|
combine_free_blocks (v, p, n);
|
|
}
|
|
|
|
__clib_export void *
|
|
_heap_alloc (void *v,
|
|
uword size,
|
|
uword align,
|
|
uword elt_bytes, uword * offset_return, uword * handle_return)
|
|
{
|
|
uword offset = 0, align_size;
|
|
heap_header_t *h;
|
|
heap_elt_t *e;
|
|
|
|
if (size == 0)
|
|
goto error;
|
|
|
|
/* Round up alignment to power of 2. */
|
|
if (align <= 1)
|
|
{
|
|
align = 0;
|
|
align_size = size;
|
|
}
|
|
else
|
|
{
|
|
align = max_pow2 (align);
|
|
align_size = size + align - 1;
|
|
}
|
|
|
|
e = search_free_list (v, align_size);
|
|
|
|
/* If nothing found on free list, allocate object from end of vector. */
|
|
if (!e)
|
|
{
|
|
uword max_len;
|
|
|
|
offset = vec_len (v);
|
|
max_len = heap_get_max_len (v);
|
|
|
|
if (max_len && offset + align_size > max_len)
|
|
goto error;
|
|
|
|
h = heap_header (v);
|
|
if (!v || !(h->flags & HEAP_IS_STATIC))
|
|
v = _vec_resize (v,
|
|
align_size,
|
|
(offset + align_size) * elt_bytes,
|
|
sizeof (h[0]), HEAP_DATA_ALIGN);
|
|
else
|
|
_vec_len (v) += align_size;
|
|
|
|
if (offset == 0)
|
|
{
|
|
h = heap_header (v);
|
|
h->elt_bytes = elt_bytes;
|
|
}
|
|
}
|
|
|
|
h = heap_header (v);
|
|
|
|
/* Add new element to doubly linked chain of elements. */
|
|
if (!e)
|
|
{
|
|
e = elt_new (h);
|
|
e->offset = offset;
|
|
elt_insert_after (h, last (h), e);
|
|
}
|
|
|
|
if (align > 0)
|
|
{
|
|
uword e_index;
|
|
uword new_offset, old_offset;
|
|
|
|
old_offset = e->offset;
|
|
new_offset = (old_offset + align - 1) & ~(align - 1);
|
|
e->offset = new_offset;
|
|
e_index = e - h->elts;
|
|
|
|
/* Free fragments before and after aligned object. */
|
|
if (new_offset > old_offset)
|
|
{
|
|
heap_elt_t *before_e = elt_new (h);
|
|
before_e->offset = old_offset;
|
|
elt_insert_before (h, h->elts + e_index, before_e);
|
|
dealloc_elt (v, before_e);
|
|
}
|
|
|
|
if (new_offset + size < old_offset + align_size)
|
|
{
|
|
heap_elt_t *after_e = elt_new (h);
|
|
after_e->offset = new_offset + size;
|
|
elt_insert_after (h, h->elts + e_index, after_e);
|
|
dealloc_elt (v, after_e);
|
|
}
|
|
|
|
e = h->elts + e_index;
|
|
}
|
|
|
|
h->used_count++;
|
|
|
|
/* Keep track of used elements when debugging.
|
|
This allows deallocation to check that passed objects are valid. */
|
|
if (CLIB_DEBUG > 0)
|
|
{
|
|
uword handle = e - h->elts;
|
|
ASSERT (!clib_bitmap_get (h->used_elt_bitmap, handle));
|
|
h->used_elt_bitmap = clib_bitmap_ori (h->used_elt_bitmap, handle);
|
|
}
|
|
|
|
*offset_return = e->offset;
|
|
*handle_return = e - h->elts;
|
|
return v;
|
|
|
|
error:
|
|
*offset_return = *handle_return = ~0;
|
|
return v;
|
|
}
|
|
|
|
__clib_export void
|
|
heap_dealloc (void *v, uword handle)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
heap_elt_t *e;
|
|
|
|
ASSERT (handle < vec_len (h->elts));
|
|
|
|
/* For debugging we keep track of indices for valid objects.
|
|
We make sure user is not trying to free object with an invalid index. */
|
|
if (CLIB_DEBUG > 0)
|
|
{
|
|
ASSERT (clib_bitmap_get (h->used_elt_bitmap, handle));
|
|
h->used_elt_bitmap = clib_bitmap_andnoti (h->used_elt_bitmap, handle);
|
|
}
|
|
|
|
h->used_count--;
|
|
|
|
e = h->elts + handle;
|
|
ASSERT (!heap_is_free (e));
|
|
|
|
dealloc_elt (v, e);
|
|
}
|
|
|
|
/* While freeing objects at INDEX we noticed free blocks i0 <= index and
|
|
i1 >= index. We combine these two or three blocks into one big free block. */
|
|
static void
|
|
combine_free_blocks (void *v, heap_elt_t * e0, heap_elt_t * e1)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
uword total_size, i, b, tb, ti, i_last, g_offset;
|
|
heap_elt_t *e;
|
|
|
|
struct
|
|
{
|
|
u32 index;
|
|
u32 bin;
|
|
u32 bin_index;
|
|
} f[3], g;
|
|
|
|
/* Compute total size of free objects i0 through i1. */
|
|
total_size = 0;
|
|
for (i = 0, e = e0; 1; e = heap_next (e), i++)
|
|
{
|
|
ASSERT (i < ARRAY_LEN (f));
|
|
|
|
ti = get_free_elt (v, e, &tb);
|
|
|
|
ASSERT (tb < vec_len (h->free_lists));
|
|
ASSERT (ti < vec_len (h->free_lists[tb]));
|
|
|
|
f[i].index = h->free_lists[tb][ti];
|
|
f[i].bin = tb;
|
|
f[i].bin_index = ti;
|
|
|
|
total_size += heap_elt_size (v, elt_at (h, f[i].index));
|
|
|
|
if (e == e1)
|
|
{
|
|
i_last = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Compute combined bin. See if all objects can be
|
|
combined into existing bin. */
|
|
b = size_to_bin (total_size);
|
|
g.index = g.bin_index = 0;
|
|
for (i = 0; i <= i_last; i++)
|
|
if (b == f[i].bin)
|
|
{
|
|
g = f[i];
|
|
break;
|
|
}
|
|
|
|
/* Make sure we found a bin. */
|
|
if (i > i_last)
|
|
{
|
|
g.index = elt_new (h) - h->elts;
|
|
vec_validate (h->free_lists, b);
|
|
g.bin_index = vec_len (h->free_lists[b]);
|
|
vec_add1 (h->free_lists[b], g.index);
|
|
elt_insert_before (h, elt_at (h, f[0].index), elt_at (h, g.index));
|
|
}
|
|
|
|
g_offset = elt_at (h, f[0].index)->offset;
|
|
|
|
/* Delete unused bins. */
|
|
for (i = 0; i <= i_last; i++)
|
|
if (g.index != f[i].index)
|
|
{
|
|
ti = get_free_elt (v, elt_at (h, f[i].index), &tb);
|
|
remove_free_block (v, tb, ti);
|
|
elt_delete (h, elt_at (h, f[i].index));
|
|
}
|
|
|
|
/* Initialize new element. */
|
|
elt_at (h, g.index)->offset = g_offset;
|
|
set_free_elt (v, elt_at (h, g.index), g.bin_index);
|
|
}
|
|
|
|
__clib_export uword
|
|
heap_len (void *v, word handle)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
|
|
if (CLIB_DEBUG > 0)
|
|
ASSERT (clib_bitmap_get (h->used_elt_bitmap, handle));
|
|
return heap_elt_size (v, elt_at (h, handle));
|
|
}
|
|
|
|
__clib_export void *
|
|
_heap_free (void *v)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
uword b;
|
|
|
|
if (!v)
|
|
return v;
|
|
|
|
clib_bitmap_free (h->used_elt_bitmap);
|
|
for (b = 0; b < vec_len (h->free_lists); b++)
|
|
vec_free (h->free_lists[b]);
|
|
vec_free (h->free_lists);
|
|
vec_free (h->elts);
|
|
vec_free (h->free_elts);
|
|
vec_free (h->small_free_elt_free_index);
|
|
if (!(h->flags & HEAP_IS_STATIC))
|
|
vec_free_h (v, sizeof (h[0]));
|
|
return v;
|
|
}
|
|
|
|
uword
|
|
heap_bytes (void *v)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
uword bytes, b;
|
|
|
|
if (!v)
|
|
return 0;
|
|
|
|
bytes = sizeof (h[0]);
|
|
bytes += vec_len (v) * sizeof (h->elt_bytes);
|
|
for (b = 0; b < vec_len (h->free_lists); b++)
|
|
bytes += vec_capacity (h->free_lists[b], 0);
|
|
bytes += vec_bytes (h->free_lists);
|
|
bytes += vec_capacity (h->elts, 0);
|
|
bytes += vec_capacity (h->free_elts, 0);
|
|
bytes += vec_bytes (h->used_elt_bitmap);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
static u8 *
|
|
debug_elt (u8 * s, void *v, word i, word n)
|
|
{
|
|
heap_elt_t *e, *e0, *e1;
|
|
heap_header_t *h = heap_header (v);
|
|
word j;
|
|
|
|
if (vec_len (h->elts) == 0)
|
|
return s;
|
|
|
|
if (i < 0)
|
|
e0 = first (h);
|
|
else
|
|
{
|
|
e0 = h->elts + i;
|
|
for (j = 0; j < n / 2; j++)
|
|
e0 = heap_prev (e0);
|
|
}
|
|
|
|
if (n < 0)
|
|
e1 = h->elts + h->tail;
|
|
else
|
|
{
|
|
e1 = h->elts + i;
|
|
for (j = 0; j < n / 2; j++)
|
|
e1 = heap_next (e1);
|
|
}
|
|
|
|
i = -n / 2;
|
|
for (e = e0; 1; e = heap_next (e))
|
|
{
|
|
if (heap_is_free (e))
|
|
s = format (s, "index %4d, free\n", e - h->elts);
|
|
else if (h->format_elt)
|
|
s = format (s, "%U", h->format_elt, v, elt_data (v, e));
|
|
else
|
|
s = format (s, "index %4d, used\n", e - h->elts);
|
|
i++;
|
|
if (e == e1)
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
__clib_export u8 *
|
|
format_heap (u8 *s, va_list *va)
|
|
{
|
|
void *v = va_arg (*va, void *);
|
|
uword verbose = va_arg (*va, uword);
|
|
heap_header_t *h = heap_header (v);
|
|
heap_header_t zero;
|
|
|
|
clib_memset (&zero, 0, sizeof (zero));
|
|
|
|
if (!v)
|
|
h = &zero;
|
|
|
|
{
|
|
f64 elt_bytes = vec_len (v) * h->elt_bytes;
|
|
f64 overhead_bytes = heap_bytes (v);
|
|
|
|
s = format (s, "heap %p, %6d objects, size %.1fk + overhead %.1fk\n",
|
|
v, h->used_count, elt_bytes / 1024,
|
|
(overhead_bytes - elt_bytes) / 1024);
|
|
}
|
|
|
|
if (v && verbose)
|
|
s = debug_elt (s, v, -1, -1);
|
|
|
|
return s;
|
|
}
|
|
|
|
__clib_export void
|
|
heap_validate (void *v)
|
|
{
|
|
heap_header_t *h = heap_header (v);
|
|
uword i, o, s;
|
|
u8 *free_map;
|
|
heap_elt_t *e, *n;
|
|
|
|
uword used_count, total_size;
|
|
uword free_count, free_size;
|
|
|
|
ASSERT (h->used_count == clib_bitmap_count_set_bits (h->used_elt_bitmap));
|
|
|
|
ASSERT (first (h)->prev == 0);
|
|
ASSERT (last (h)->next == 0);
|
|
|
|
/* Validate number of elements and size. */
|
|
free_size = free_count = 0;
|
|
for (i = 0; i < vec_len (h->free_lists); i++)
|
|
{
|
|
free_count += vec_len (h->free_lists[i]);
|
|
for (o = 0; o < vec_len (h->free_lists[i]); o++)
|
|
{
|
|
e = h->elts + h->free_lists[i][o];
|
|
s = heap_elt_size (v, e);
|
|
ASSERT (size_to_bin (s) == i);
|
|
ASSERT (heap_is_free (e));
|
|
free_size += s;
|
|
}
|
|
}
|
|
|
|
{
|
|
uword elt_free_size, elt_free_count;
|
|
|
|
used_count = total_size = elt_free_size = elt_free_count = 0;
|
|
for (e = first (h); 1; e = n)
|
|
{
|
|
int is_free = heap_is_free (e);
|
|
used_count++;
|
|
s = heap_elt_size (v, e);
|
|
total_size += s;
|
|
ASSERT (is_free ==
|
|
!clib_bitmap_get (h->used_elt_bitmap, e - h->elts));
|
|
if (is_free)
|
|
{
|
|
elt_free_count++;
|
|
elt_free_size += s;
|
|
}
|
|
n = heap_next (e);
|
|
if (e == n)
|
|
{
|
|
ASSERT (last (h) == n);
|
|
break;
|
|
}
|
|
|
|
/* We should never have two free adjacent elements. */
|
|
ASSERT (!(heap_is_free (e) && heap_is_free (n)));
|
|
}
|
|
|
|
ASSERT (free_count == elt_free_count);
|
|
ASSERT (free_size == elt_free_size);
|
|
ASSERT (used_count == h->used_count + free_count);
|
|
ASSERT (total_size == vec_len (v));
|
|
}
|
|
|
|
free_map = vec_new (u8, used_count);
|
|
|
|
e = first (h);
|
|
for (i = o = 0; 1; i++)
|
|
{
|
|
ASSERT (heap_offset (e) == o);
|
|
s = heap_elt_size (v, e);
|
|
|
|
if (heap_is_free (e))
|
|
{
|
|
uword fb, fi;
|
|
|
|
fi = get_free_elt (v, e, &fb);
|
|
|
|
ASSERT (fb < vec_len (h->free_lists));
|
|
ASSERT (fi < vec_len (h->free_lists[fb]));
|
|
ASSERT (h->free_lists[fb][fi] == e - h->elts);
|
|
|
|
ASSERT (!free_map[i]);
|
|
free_map[i] = 1;
|
|
}
|
|
|
|
n = heap_next (e);
|
|
|
|
if (e == n)
|
|
break;
|
|
|
|
ASSERT (heap_prev (n) == e);
|
|
|
|
o += s;
|
|
e = n;
|
|
}
|
|
|
|
vec_free (free_map);
|
|
}
|
|
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|