Beispiel #1
0
static void test_check_free_block(void)
{
  Heap_Block *block = NULL;
  Heap_Block *next_block = NULL;
  Heap_Block *first_free_block = NULL;
  Heap_Block *secound_free_block = NULL;
  void *p1 = NULL;

  puts( "test the _Heap_Walk_check_free_block() function" );

  puts( "\tset a previous size for the next block which is not equal to the size of the actual block" );
  test_heap_init_default();
  block = _Heap_Free_list_first( &TestHeap );
  next_block = _Heap_Block_at( block, _Heap_Block_size( block ) );
  next_block->prev_size = _Heap_Block_size( block ) - 1;
  test_call_heap_walk( false );

  puts( "\tclear the previous_used flag of the first free block after an used block" );
  test_heap_init_default();
  p1 = test_allocate_block();
  block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size );
  first_free_block = _Heap_Free_list_first( &TestHeap );
  first_free_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED;
  first_free_block->prev_size = _Heap_Block_size( block );
  _Heap_Free_list_insert_after( first_free_block, block );
  test_call_heap_walk( false );

  puts( "\ttake a free block out of the free list" );
  test_heap_init_custom();
  test_create_heap_with_gaps();
  first_free_block = _Heap_Free_list_first( &TestHeap );
  secound_free_block = first_free_block->next;
  _Heap_Free_list_remove( secound_free_block );
  test_call_heap_walk( false );
}
Beispiel #2
0
static Heap_Resize_status _Heap_Resize_block_checked(
  Heap_Control *heap,
  Heap_Block *block,
  uintptr_t alloc_begin,
  uintptr_t new_alloc_size,
  uintptr_t *old_size,
  uintptr_t *new_size
)
{
  Heap_Statistics *const stats = &heap->stats;

  uintptr_t const block_begin = (uintptr_t) block;
  uintptr_t block_size = _Heap_Block_size( block );
  uintptr_t block_end = block_begin + block_size;

  uintptr_t alloc_size = block_end - alloc_begin + HEAP_ALLOC_BONUS;

  Heap_Block *next_block = _Heap_Block_at( block, block_size );
  uintptr_t next_block_size = _Heap_Block_size( next_block );
  bool next_block_is_free = _Heap_Is_free( next_block );

  _HAssert( _Heap_Is_block_in_heap( heap, next_block ) );
  _HAssert( _Heap_Is_prev_used( next_block ) );

  *old_size = alloc_size;

  if ( next_block_is_free ) {
    block_size += next_block_size;
    alloc_size += next_block_size;
  }

  if ( new_alloc_size > alloc_size ) {
    return HEAP_RESIZE_UNSATISFIED;
  }

  if ( next_block_is_free ) {
    _Heap_Block_set_size( block, block_size );

    _Heap_Free_list_remove( next_block );

    next_block = _Heap_Block_at( block, block_size );
    next_block->size_and_flag |= HEAP_PREV_BLOCK_USED;

    /* Statistics */
    --stats->free_blocks;
    stats->free_size -= next_block_size;
  }

  block = _Heap_Block_allocate( heap, block, alloc_begin, new_alloc_size );

  block_size = _Heap_Block_size( block );
  next_block = _Heap_Block_at( block, block_size );
  *new_size = (uintptr_t) next_block - alloc_begin + HEAP_ALLOC_BONUS;

  /* Statistics */
  ++stats->resizes;

  return HEAP_RESIZE_SUCCESSFUL;
}
static Heap_Block *_Heap_Block_allocate_from_end(
  Heap_Control *heap,
  Heap_Block *block,
  Heap_Block *free_list_anchor,
  uintptr_t alloc_begin,
  uintptr_t alloc_size
)
{
  Heap_Statistics *const stats = &heap->stats;

  uintptr_t block_begin = (uintptr_t) block;
  uintptr_t block_size = _Heap_Block_size( block );
  uintptr_t block_end = block_begin + block_size;

  Heap_Block *const new_block =
    _Heap_Block_of_alloc_area( alloc_begin, heap->page_size );
  uintptr_t const new_block_begin = (uintptr_t) new_block;
  uintptr_t const new_block_size = block_end - new_block_begin;

  block_end = new_block_begin;
  block_size = block_end - block_begin;

  _HAssert( block_size >= heap->min_block_size );
  _HAssert( new_block_size >= heap->min_block_size );

  /* Statistics */
  stats->free_size += block_size;

  if ( _Heap_Is_prev_used( block ) ) {
    _Heap_Free_list_insert_after( free_list_anchor, block );

    free_list_anchor = block;

    /* Statistics */
    ++stats->free_blocks;
  } else {
    Heap_Block *const prev_block = _Heap_Prev_block( block );
    uintptr_t const prev_block_size = _Heap_Block_size( prev_block );

    block = prev_block;
    block_begin = (uintptr_t) block;
    block_size += prev_block_size;
  }

  block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED;

  new_block->prev_size = block_size;
  new_block->size_and_flag = new_block_size;

  _Heap_Block_split( heap, new_block, free_list_anchor, alloc_size );

  return new_block;
}
static void
check_result(
  Heap_Control* the_heap,
  Heap_Block* the_block,
  _H_uptr_t user_addr,
  _H_uptr_t aligned_user_addr,
  uint32_t size)
{
  _H_uptr_t const user_area = _H_p2u(_Heap_User_area(the_block));
  _H_uptr_t const block_end = _H_p2u(the_block)
    + _Heap_Block_size(the_block) + HEAP_BLOCK_HEADER_OFFSET;
  _H_uptr_t const user_end = aligned_user_addr + size;
  _H_uptr_t const heap_start = _H_p2u(the_heap->start) + HEAP_OVERHEAD;
  _H_uptr_t const heap_end = _H_p2u(the_heap->final)
    + HEAP_BLOCK_HEADER_OFFSET;
  uint32_t const page_size = the_heap->page_size;

  _HAssert(user_addr == user_area);
  _HAssert(aligned_user_addr - user_area < page_size);
  _HAssert(aligned_user_addr >= user_area);
  _HAssert(aligned_user_addr < block_end);
  _HAssert(user_end > user_area);
  _HAssert(user_end <= block_end);
  _HAssert(aligned_user_addr >= heap_start);
  _HAssert(aligned_user_addr < heap_end);
  _HAssert(user_end > heap_start);
  _HAssert(user_end <= heap_end);
}
Beispiel #5
0
static uintptr_t _Heap_Check_block(
  const Heap_Control *heap,
  const Heap_Block *block,
  uintptr_t alloc_size,
  uintptr_t alignment,
  uintptr_t boundary
)
{
  uintptr_t const page_size = heap->page_size;
  uintptr_t const min_block_size = heap->min_block_size;

  uintptr_t const block_begin = (uintptr_t) block;
  uintptr_t const block_size = _Heap_Block_size( block );
  uintptr_t const block_end = block_begin + block_size;

  uintptr_t const alloc_begin_floor = _Heap_Alloc_area_of_block( block );
  uintptr_t const alloc_begin_ceiling = block_end - min_block_size
    + HEAP_BLOCK_HEADER_SIZE + page_size - 1;

  uintptr_t alloc_end = block_end + HEAP_ALLOC_BONUS;
  uintptr_t alloc_begin = alloc_end - alloc_size;

  alloc_begin = _Heap_Align_down( alloc_begin, alignment );

  /* Ensure that the we have a valid new block at the end */
  if ( alloc_begin > alloc_begin_ceiling ) {
    alloc_begin = _Heap_Align_down( alloc_begin_ceiling, alignment );
  }

  alloc_end = alloc_begin + alloc_size;

  /* Ensure boundary constaint */
  if ( boundary != 0 ) {
    uintptr_t const boundary_floor = alloc_begin_floor + alloc_size;
    uintptr_t boundary_line = _Heap_Align_down( alloc_end, boundary );

    while ( alloc_begin < boundary_line && boundary_line < alloc_end ) {
      if ( boundary_line < boundary_floor ) {
        return 0;
      }
      alloc_begin = boundary_line - alloc_size;
      alloc_begin = _Heap_Align_down( alloc_begin, alignment );
      alloc_end = alloc_begin + alloc_size;
      boundary_line = _Heap_Align_down( alloc_end, boundary );
    }
  }

  /* Ensure that the we have a valid new block at the beginning */
  if ( alloc_begin >= alloc_begin_floor ) {
    uintptr_t const alloc_block_begin =
      (uintptr_t) _Heap_Block_of_alloc_area( alloc_begin, page_size );
    uintptr_t const free_size = alloc_block_begin - block_begin;

    if ( free_size >= min_block_size || free_size == 0 ) {
      return alloc_begin;
    }
  }

  return 0;
}
Beispiel #6
0
  static void _Heap_Protection_delay_block_free(
    Heap_Control *heap,
    Heap_Block *block
  )
  {
    uintptr_t *const pattern_begin = (uintptr_t *)
      _Heap_Alloc_area_of_block( block );
    uintptr_t *const pattern_end = (uintptr_t *)
      ((uintptr_t) block + _Heap_Block_size( block ) + HEAP_ALLOC_BONUS);
    uintptr_t const delayed_free_block_count =
      heap->Protection.delayed_free_block_count;
    uintptr_t *current = NULL;

    block->Protection_begin.next_delayed_free_block = block;
    block->Protection_begin.task = _Thread_Get_executing();

    if ( delayed_free_block_count > 0 ) {
      Heap_Block *const last = heap->Protection.last_delayed_free_block;

      last->Protection_begin.next_delayed_free_block = block;
    } else {
      heap->Protection.first_delayed_free_block = block;
    }
    heap->Protection.last_delayed_free_block = block;
    heap->Protection.delayed_free_block_count = delayed_free_block_count + 1;

    for ( current = pattern_begin; current != pattern_end; ++current ) {
      *current = HEAP_FREE_PATTERN;
    }
  }
void _Heap_Get_free_information(
  Heap_Control        *the_heap,
  Heap_Information    *info
)
{
  Heap_Block *the_block;
  Heap_Block *const tail = _Heap_Free_list_tail(the_heap);

  info->number = 0;
  info->largest = 0;
  info->total = 0;

  for(the_block = _Heap_Free_list_first(the_heap);
      the_block != tail;
      the_block = the_block->next)
  {
    uint32_t const the_size = _Heap_Block_size(the_block);

    /* As we always coalesce free blocks, prev block must have been used. */
    _HAssert(_Heap_Is_prev_used(the_block));

    info->number++;
    info->total += the_size;
    if ( info->largest < the_size )
        info->largest = the_size;
  }
}
Beispiel #8
0
bool _Heap_Size_of_alloc_area(
  Heap_Control *heap,
  void *alloc_begin_ptr,
  uintptr_t *alloc_size
)
{
  uintptr_t const page_size = heap->page_size;
  uintptr_t const alloc_begin = (uintptr_t) alloc_begin_ptr;
  Heap_Block *block = _Heap_Block_of_alloc_area( alloc_begin, page_size );
  Heap_Block *next_block = NULL;
  uintptr_t block_size = 0;

  if ( !_Heap_Is_block_in_heap( heap, block ) ) {
    return false;
  }

  block_size = _Heap_Block_size( block );
  next_block = _Heap_Block_at( block, block_size );

  if (
    !_Heap_Is_block_in_heap( heap, next_block )
      || !_Heap_Is_prev_used( next_block )
  ) {
    return false;
  }

  *alloc_size = (uintptr_t) next_block + HEAP_BLOCK_SIZE_OFFSET - alloc_begin;

  return true;
}
Beispiel #9
0
static void test_heap_do_block_allocate( int variant, void *p2 )
{
  Heap_Block *const block =
    _Heap_Block_of_alloc_area( (uintptr_t) p2, test_page_size());
  uintptr_t const alloc_box_begin = _Heap_Alloc_area_of_block( block );
  uintptr_t const alloc_box_size = _Heap_Block_size( block );
  uintptr_t const alloc_box_end = alloc_box_begin + alloc_box_size;
  uintptr_t alloc_begin = 0;
  uintptr_t alloc_size = 0;

  puts( "\tallocate block at the beginning");
  alloc_begin = alloc_box_begin;
  alloc_size = 0;
  test_block_alloc( variant, 0, alloc_begin, alloc_size );

  puts( "\tallocate block full space");
  alloc_begin = alloc_box_begin;
  alloc_size = alloc_box_size + HEAP_ALLOC_BONUS
    - HEAP_BLOCK_HEADER_SIZE;
  test_block_alloc( variant, 1, alloc_begin, alloc_size );

  puts( "\tallocate block in the middle");
  alloc_begin = alloc_box_begin + TEST_DEFAULT_PAGE_SIZE;
  alloc_size = 0;
  test_block_alloc( variant, 2, alloc_begin, alloc_size );

  puts( "\tallocate block at the end");
  alloc_begin = alloc_box_end - TEST_DEFAULT_PAGE_SIZE;
  alloc_size = TEST_DEFAULT_PAGE_SIZE + HEAP_ALLOC_BONUS
    - HEAP_BLOCK_HEADER_SIZE;
  test_block_alloc( variant, 3, alloc_begin, alloc_size );
}
Heap_Block *_Heap_Block_allocate(
  Heap_Control *heap,
  Heap_Block *block,
  uintptr_t alloc_begin,
  uintptr_t alloc_size
)
{
  Heap_Statistics *const stats = &heap->stats;

  uintptr_t const alloc_area_begin = _Heap_Alloc_area_of_block( block );
  uintptr_t const alloc_area_offset = alloc_begin - alloc_area_begin;

  Heap_Block *free_list_anchor = NULL;

  _HAssert( alloc_area_begin <= alloc_begin );

  if ( _Heap_Is_free( block ) ) {
    free_list_anchor = block->prev;

    _Heap_Free_list_remove( block );

    /* Statistics */
    --stats->free_blocks;
    ++stats->used_blocks;
    stats->free_size -= _Heap_Block_size( block );
  } else {
    free_list_anchor = _Heap_Free_list_head( heap );
  }

  if ( alloc_area_offset < heap->page_size ) {
    alloc_size += alloc_area_offset;

    block = _Heap_Block_allocate_from_begin(
      heap,
      block,
      free_list_anchor,
      alloc_size
    );
  } else {
    block = _Heap_Block_allocate_from_end(
      heap,
      block,
      free_list_anchor,
      alloc_begin,
      alloc_size
    );
  }

  /* Statistics */
  if ( stats->min_free_size > stats->free_size ) {
    stats->min_free_size = stats->free_size;
  }

  return block;
}
Beispiel #11
0
Heap_Block *_Heap_Greedy_allocate(
  Heap_Control *heap,
  const uintptr_t *block_sizes,
  size_t block_count
)
{
  Heap_Block *const free_list_tail = _Heap_Free_list_tail( heap );
  Heap_Block *allocated_blocks = NULL;
  Heap_Block *blocks = NULL;
  Heap_Block *current;
  size_t i;

  _Heap_Protection_free_all_delayed_blocks( heap );

  for (i = 0; i < block_count; ++i) {
    void *next = _Heap_Allocate( heap, block_sizes [i] );

    if ( next != NULL ) {
      Heap_Block *next_block = _Heap_Block_of_alloc_area(
        (uintptr_t) next,
        heap->page_size
      );

      next_block->next = allocated_blocks;
      allocated_blocks = next_block;
    }
  }

  while ( (current = _Heap_Free_list_first( heap )) != free_list_tail ) {
    _Heap_Block_allocate(
      heap,
      current,
      _Heap_Alloc_area_of_block( current ),
      _Heap_Block_size( current ) - HEAP_BLOCK_HEADER_SIZE
    );

    current->next = blocks;
    blocks = current;
  }

  while ( allocated_blocks != NULL ) {
    current = allocated_blocks;
    allocated_blocks = allocated_blocks->next;
    _Heap_Free( heap, (void *) _Heap_Alloc_area_of_block( current ) );
  }

  return blocks;
}
Beispiel #12
0
  static void _Heap_Check_allocation(
    const Heap_Control *heap,
    const Heap_Block *block,
    uintptr_t alloc_begin,
    uintptr_t alloc_size,
    uintptr_t alignment,
    uintptr_t boundary
  )
  {
    uintptr_t const min_block_size = heap->min_block_size;
    uintptr_t const page_size = heap->page_size;

    uintptr_t const block_begin = (uintptr_t) block;
    uintptr_t const block_size = _Heap_Block_size( block );
    uintptr_t const block_end = block_begin + block_size;

    uintptr_t const alloc_end = alloc_begin + alloc_size;

    uintptr_t const alloc_area_begin = _Heap_Alloc_area_of_block( block );
    uintptr_t const alloc_area_offset = alloc_begin - alloc_area_begin;

    _HAssert( block_size >= min_block_size );
    _HAssert( block_begin < block_end );
    _HAssert(
      _Heap_Is_aligned( block_begin + HEAP_BLOCK_HEADER_SIZE, page_size )
    );
    _HAssert(
      _Heap_Is_aligned( block_size, page_size )
    );

    _HAssert( alloc_end <= block_end + HEAP_ALLOC_BONUS );
    _HAssert( alloc_area_begin == block_begin + HEAP_BLOCK_HEADER_SIZE);
    _HAssert( alloc_area_offset < page_size );

    _HAssert( _Heap_Is_aligned( alloc_area_begin, page_size ) );
    if ( alignment == 0 ) {
      _HAssert( alloc_begin == alloc_area_begin );
    } else {
      _HAssert( _Heap_Is_aligned( alloc_begin, alignment ) );
    }

    if ( boundary != 0 ) {
      uintptr_t boundary_line = _Heap_Align_down( alloc_end, boundary );

      _HAssert( alloc_size <= boundary );
      _HAssert( boundary_line <= alloc_begin || alloc_end <= boundary_line );
    }
  }
Beispiel #13
0
  static void _Heap_Protection_check_free_block(
    Heap_Control *heap,
    Heap_Block *block
  )
  {
    uintptr_t *const pattern_begin = (uintptr_t *)
      _Heap_Alloc_area_of_block( block );
    uintptr_t *const pattern_end = (uintptr_t *)
      ((uintptr_t) block + _Heap_Block_size( block ) + HEAP_ALLOC_BONUS);
    uintptr_t *current = NULL;

    for ( current = pattern_begin; current != pattern_end; ++current ) {
      if ( *current != HEAP_FREE_PATTERN ) {
        _Heap_Protection_block_error( heap, block );
        break;
      }
    }
  }
Beispiel #14
0
void _Heap_Get_information(
  Heap_Control            *the_heap,
  Heap_Information_block  *the_info
)
{
  Heap_Block *the_block = the_heap->first_block;
  Heap_Block *const end = the_heap->last_block;

  _HAssert(the_block->prev_size == the_heap->page_size);
  _HAssert(_Heap_Is_prev_used(the_block));

  the_info->Free.number  = 0;
  the_info->Free.total   = 0;
  the_info->Free.largest = 0;
  the_info->Used.number  = 0;
  the_info->Used.total   = 0;
  the_info->Used.largest = 0;

  while ( the_block != end ) {
    uintptr_t const     the_size = _Heap_Block_size(the_block);
    Heap_Block *const  next_block = _Heap_Block_at(the_block, the_size);
    Heap_Information  *info;

    if ( _Heap_Is_prev_used(next_block) )
      info = &the_info->Used;
    else
      info = &the_info->Free;

    info->number++;
    info->total += the_size;
    if ( info->largest < the_size )
      info->largest = the_size;

    the_block = next_block;
  }

  /*
   *  Handle the last dummy block. Don't consider this block to be
   *  "used" as client never allocated it. Make 'Used.total' contain this
   *  blocks' overhead though.
   */
  the_info->Used.total += HEAP_BLOCK_HEADER_SIZE;
}
Beispiel #15
0
void _Heap_Iterate(
  Heap_Control *heap,
  Heap_Block_visitor visitor,
  void *visitor_arg
)
{
  Heap_Block *current = heap->first_block;
  Heap_Block *end = heap->last_block;
  bool stop = false;

  while ( !stop && current != end ) {
    uintptr_t size = _Heap_Block_size( current );
    Heap_Block *next = _Heap_Block_at( current, size );
    bool used = _Heap_Is_prev_used( next );

    stop = (*visitor)( current, size, used, visitor_arg );

    current = next;
  }
}
/*
 * Allocate block of size 'alloc_size' from 'the_block' belonging to
 * 'the_heap'. Split 'the_block' if possible, otherwise allocate it entirely.
 * When split, make the upper part used, and leave the lower part free.
 * Return the block allocated.
 *
 * NOTE: this is similar to _Heap_Block_allocate(), except it makes different
 * part of the split block used, and returns address of the block instead of its
 * size. We do need such variant for _Heap_Allocate_aligned() as we can't allow
 * user pointer to be too far from the beginning of the block, so that we can
 * recover start-of-block address from the user pointer without additional
 * information stored in the heap.
 */
static
Heap_Block *block_allocate(
  Heap_Control  *the_heap,
  Heap_Block    *the_block,
  uint32_t      alloc_size)
{
  Heap_Statistics *const stats = &the_heap->stats;
  uint32_t const block_size = _Heap_Block_size(the_block);
  uint32_t const the_rest = block_size - alloc_size;

  _HAssert(_Heap_Is_aligned(block_size, the_heap->page_size));
  _HAssert(_Heap_Is_aligned(alloc_size, the_heap->page_size));
  _HAssert(alloc_size <= block_size);
  _HAssert(_Heap_Is_prev_used(the_block));

  if(the_rest >= the_heap->min_block_size) {
    /* Split the block so that lower part is still free, and upper part
       becomes used. */
    the_block->size = the_rest | HEAP_PREV_USED;
    the_block = _Heap_Block_at(the_block, the_rest);
    the_block->prev_size = the_rest;
    the_block->size = alloc_size;
  }
  else {
    /* Don't split the block as remainder is either zero or too small to be
       used as a separate free block. Change 'alloc_size' to the size of the
       block and remove the block from the list of free blocks. */
    _Heap_Block_remove(the_block);
    alloc_size = block_size;
    stats->free_blocks -= 1;
  }
  /* Mark the block as used (in the next block). */
  _Heap_Block_at(the_block, alloc_size)->size |= HEAP_PREV_USED;
  /* Update statistics */
  stats->free_size -= alloc_size;
  if(stats->min_free_size > stats->free_size)
    stats->min_free_size = stats->free_size;
  stats->used_blocks += 1;
  return the_block;
}
Beispiel #17
0
static void test_heap_resize_block(void)
{
  void *p1, *p2, *p3;
  uintptr_t new_alloc_size = 0;
  Heap_Block *block = NULL;

  puts( "run tests for _Heap_Resize_Block()" );

  puts( "\tgive a block outside the heap to the function" );
  test_heap_init( TEST_DEFAULT_PAGE_SIZE );
  p1 = TestHeap.first_block - TEST_DEFAULT_PAGE_SIZE;
  new_alloc_size = 1;
  test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_FATAL_ERROR );

  puts( "\tincrease size");

  puts( "\t\tlet the next block be used alredy and try to get a size bigger than the actual block" );
  test_heap_init( TEST_DEFAULT_PAGE_SIZE );
  p1 = test_alloc_one_page();
  rtems_test_assert( p1 );

  p2 = test_alloc_one_page();
  rtems_test_assert( p2 );

  new_alloc_size = 3 * TEST_DEFAULT_PAGE_SIZE / 2;
  test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_UNSATISFIED );

  puts( "\t\tnext block not used and try to set the new allocation size between the page-alignments" );
  test_heap_init( TEST_DEFAULT_PAGE_SIZE );
  p1 = test_alloc_one_page();
  new_alloc_size = 3 * TEST_DEFAULT_PAGE_SIZE / 2;
  test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL );

  puts( "\t\tlet the block after the next be used and try to allocate more then one pagesize more" );
  test_heap_init( TEST_DEFAULT_PAGE_SIZE );
  p1 = test_alloc_one_page();
  rtems_test_assert( p1 );

  p2 = test_alloc_one_page();
  rtems_test_assert( p2 );

  p3 = test_alloc_one_page();
  rtems_test_assert( p3 );

  test_free( p2 );
  new_alloc_size = 5 * TEST_DEFAULT_PAGE_SIZE / 2;
  test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_UNSATISFIED );

  puts( "\ttry to resize to the same size" );
  test_heap_init( TEST_DEFAULT_PAGE_SIZE );
  p1 = test_alloc_one_page();
  block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size );
  new_alloc_size = _Heap_Block_size( block );
  test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL );

  puts( "\tdecrease size");

  puts( "\t\tdecrease a block with two pages to one page" );
  test_heap_init( TEST_DEFAULT_PAGE_SIZE );
  p1 = test_alloc_two_pages();
  new_alloc_size = 1;
  test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL );

  puts( "\t\tresize the block to the size 0" );
  test_heap_init( TEST_DEFAULT_PAGE_SIZE );
  p1 = test_alloc_one_page();
  new_alloc_size = 0;
  test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL );
}
Beispiel #18
0
static void test_block_alloc(
  int free_variant,
  int alloc_variant,
  uintptr_t alloc_begin,
  uintptr_t alloc_size
)
{
  void *p1 = NULL;
  void *p2 = NULL;
  void *p3 = NULL;

  uintptr_t size_fresh_heap = 0;
  uintptr_t pages_per_default_block = 0;
  uint32_t exp_free_pages = 0;
  uint32_t exp_free_blocks = 0;
  uint32_t exp_used_blocks = 0;

  test_heap_init( TEST_DEFAULT_PAGE_SIZE );

  size_fresh_heap = _Heap_Get_size( &TestHeap );
  exp_free_pages = size_fresh_heap / TestHeap.page_size;

  p1 = test_create_used_block();
  p2 = test_create_used_block();
  p3 = test_create_used_block();

  pages_per_default_block = _Heap_Block_size(
    _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size )
  ) / TestHeap.page_size;

  if (free_variant == 1) {
    test_free( p1 );
  } else if (free_variant == 2) {
    test_free( p3 );
  } else if (free_variant == 3) {
    test_free( p2 );
    test_free( p3 );
  }

  _Heap_Block_allocate(
    &TestHeap,
    _Heap_Block_of_alloc_area( (uintptr_t) p2, test_page_size()),
    alloc_begin,
    alloc_size
  );

  test_check_alloc_simple( (void *) alloc_begin, alloc_size, 0, 0 );

  /* check statistics */
  switch( free_variant ) {
    case 1:
      exp_free_pages = exp_free_pages - 2 * pages_per_default_block;
      exp_used_blocks = 2;

      switch( alloc_variant ) {
	case 1:
	  /* allocate block full space */
	  exp_free_blocks = 2;
	  break;
	case 2:
	  /* allocate block in the middle */
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
	  exp_free_blocks = 3;
	  break;
	case 3:
	  /* allocate block at the end */
	  exp_free_pages = exp_free_pages + pages_per_default_block - 2;
	  exp_free_blocks = 2;
	  break;
	default:
	  /* allocate block at the beginning */
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
	  exp_free_blocks = 3;
	  break;
      }
      break;
    case 2:
      exp_free_pages = exp_free_pages - 2 * pages_per_default_block;
      exp_used_blocks = 2;

      switch( alloc_variant ) {
	case 1:
	  /* allocate block full space */
	  exp_free_blocks = 1;
	  break;
	case 2:
	  /* allocate block in the middle */
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
	  exp_free_blocks = 2;
	  break;
	case 3:
	  /* allocate block at the end */
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
	  exp_free_blocks = 2;
	  break;
	default:
	  /* allocate block at the beginning */
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
	  exp_free_blocks = 1;
	  break;
      }
      break;
    case 3:
      exp_free_pages = exp_free_pages - pages_per_default_block;
      exp_used_blocks = 2;

      switch( alloc_variant ) {
	case 1:
	  /* allocate block full space */
	  exp_free_pages = exp_free_pages - pages_per_default_block;
	  exp_free_blocks = 1;
	  break;
	case 2:
	  /* allocate block in the middle */
	  exp_free_pages = exp_free_pages - 1;
	  exp_free_blocks = 2;
	  break;
	case 3:
	  /* allocate block at the end */
	  exp_free_pages = exp_free_pages - 2;
	  exp_free_blocks = 2;
	  break;
	default:
	  /* allocate block at the beginning */
	  exp_free_pages = exp_free_pages - 1;
	  exp_free_blocks = 1;
	  break;
      }
      break;
    default:
      exp_free_pages = exp_free_pages - 3 * pages_per_default_block;
      exp_used_blocks = 3;

      switch( alloc_variant ) {
	case 1:
	  /* allocate block full space */
	  exp_free_blocks = 1;
	  break;
	case 2:
	  /* allocate block in the middle */
	  exp_free_blocks = 3;
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
	  break;
	case 3:
	  /* allocate block at the end */
	  exp_free_blocks = 2;
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
	  break;
	default:
	  /* allocate block at the beginning */
	  exp_free_blocks = 2;
	  exp_free_pages = exp_free_pages + pages_per_default_block - 1;
      }
  }

  rtems_test_assert( TestHeap.stats.free_size == exp_free_pages * TestHeap.page_size );
  rtems_test_assert( TestHeap.stats.free_blocks == exp_free_blocks );
  rtems_test_assert( TestHeap.stats.used_blocks == exp_used_blocks );
}
Beispiel #19
0
void *_Heap_Allocate_aligned_with_boundary(
  Heap_Control *heap,
  uintptr_t alloc_size,
  uintptr_t alignment,
  uintptr_t boundary
)
{
  Heap_Statistics *const stats = &heap->stats;
  uintptr_t const block_size_floor = alloc_size + HEAP_BLOCK_HEADER_SIZE
    - HEAP_ALLOC_BONUS;
  uintptr_t const page_size = heap->page_size;
  Heap_Block *block = NULL;
  uintptr_t alloc_begin = 0;
  uint32_t search_count = 0;
  bool search_again = false;

  if ( block_size_floor < alloc_size ) {
    /* Integer overflow occured */
    return NULL;
  }

  if ( boundary != 0 ) {
    if ( boundary < alloc_size ) {
      return NULL;
    }

    if ( alignment == 0 ) {
      alignment = page_size;
    }
  }

  do {
    Heap_Block *const free_list_tail = _Heap_Free_list_tail( heap );

    block = _Heap_Free_list_first( heap );
    while ( block != free_list_tail ) {
      _HAssert( _Heap_Is_prev_used( block ) );

      _Heap_Protection_block_check( heap, block );

      /*
       * The HEAP_PREV_BLOCK_USED flag is always set in the block size_and_flag
       * field.  Thus the value is about one unit larger than the real block
       * size.  The greater than operator takes this into account.
       */
      if ( block->size_and_flag > block_size_floor ) {
        if ( alignment == 0 ) {
          alloc_begin = _Heap_Alloc_area_of_block( block );
        } else {
          alloc_begin = _Heap_Check_block(
            heap,
            block,
            alloc_size,
            alignment,
            boundary
          );
        }
      }

      /* Statistics */
      ++search_count;

      if ( alloc_begin != 0 ) {
        break;
      }

      block = block->next;
    }

    search_again = _Heap_Protection_free_delayed_blocks( heap, alloc_begin );
  } while ( search_again );

  if ( alloc_begin != 0 ) {
    block = _Heap_Block_allocate( heap, block, alloc_begin, alloc_size );

    _Heap_Check_allocation(
      heap,
      block,
      alloc_begin,
      alloc_size,
      alignment,
      boundary
    );

    /* Statistics */
    ++stats->allocs;
    stats->searches += search_count;
    stats->lifetime_allocated += _Heap_Block_size( block );
  } else {
    /* Statistics */
    ++stats->failed_allocs;
  }

  /* Statistics */
  if ( stats->max_search < search_count ) {
    stats->max_search = search_count;
  }

  return (void *) alloc_begin;
}
Beispiel #20
0
static void test_check_alloc(
  void *alloc_begin_ptr,
  void *expected_alloc_begin_ptr,
  uintptr_t alloc_size,
  uintptr_t alignment,
  uintptr_t boundary
)
{
  uintptr_t const min_block_size = TestHeap.min_block_size;
  uintptr_t const page_size = TestHeap.page_size;

  rtems_test_assert( alloc_begin_ptr == expected_alloc_begin_ptr );

  if( expected_alloc_begin_ptr != NULL ) {
    uintptr_t const alloc_begin = (uintptr_t ) alloc_begin_ptr;
    uintptr_t const alloc_end = alloc_begin + alloc_size;

    uintptr_t const alloc_area_begin = _Heap_Align_down( alloc_begin, page_size );
    uintptr_t const alloc_area_offset = alloc_begin - alloc_area_begin;
#if UNUSED
    uintptr_t const alloc_area_size = alloc_area_offset + alloc_size;
#endif
    Heap_Block *block = _Heap_Block_of_alloc_area( alloc_area_begin, page_size );
    uintptr_t const block_begin = (uintptr_t ) block;
    uintptr_t const block_size = _Heap_Block_size( block );
    uintptr_t const block_end = block_begin + block_size;

    rtems_test_assert( block_size >= min_block_size );
    rtems_test_assert( block_begin < block_end );
    rtems_test_assert(
      _Heap_Is_aligned( block_begin + HEAP_BLOCK_HEADER_SIZE, page_size )
    );
    rtems_test_assert(
      _Heap_Is_aligned( block_size, page_size )
    );

    rtems_test_assert( alloc_end <= block_end + HEAP_ALLOC_BONUS );
    rtems_test_assert( alloc_area_begin > block_begin );
    rtems_test_assert( alloc_area_offset < page_size );

    rtems_test_assert( _Heap_Is_aligned( alloc_area_begin, page_size ) );
    if ( alignment == 0 ) {
      rtems_test_assert( alloc_begin == alloc_area_begin );
    } else {
      rtems_test_assert( _Heap_Is_aligned( alloc_begin, alignment ) );
    }

    if ( boundary != 0 ) {
      uintptr_t boundary_line = _Heap_Align_down( alloc_end, boundary );

      rtems_test_assert( alloc_size <= boundary );
      rtems_test_assert(
        boundary_line <= alloc_begin
          || alloc_end <= boundary_line
      );
    }
  }

  rtems_test_assert(
    page_size < CPU_ALIGNMENT
      || _Heap_Walk( &TestHeap, 0, false )
  );
}
Beispiel #21
0
static void test_heap_allocate(void)
{
  void *p1 = NULL;
  void *p2 = NULL;
  void *p3 = NULL;
  uintptr_t alloc_size = 0;
  uintptr_t alignment = 0;
  uintptr_t boundary = 0;
  uintptr_t page_size = 0;
  uintptr_t first_page_begin = 0;
  uintptr_t previous_last_block_begin = 0;
  uintptr_t previous_last_page_begin = 0;

  uintptr_t last_block_begin = 0;
  uintptr_t last_alloc_begin = 0;

  test_heap_init( TEST_DEFAULT_PAGE_SIZE );

  last_block_begin = (uintptr_t) TestHeap.last_block;
  last_alloc_begin = _Heap_Alloc_area_of_block( TestHeap.last_block );

  puts( "run tests for _Heap_Allocate_aligned_with_boundary()");

  puts( "\tcheck if NULL will be returned if size causes integer overflow" );

  alloc_size = (uintptr_t ) -1;
  alignment = 0;
  boundary = 0;
  test_init_and_alloc( alloc_size, alignment, boundary, NULL );

  puts( "\ttry to allocate more space than the one which fits in the boundary" );

  alloc_size = 2;
  alignment = 0;
  boundary = alloc_size - 1;
  test_init_and_alloc( alloc_size, alignment, boundary, NULL );

  puts( "\tcheck if alignment will be set to page size if only a boundary is given" );

  alloc_size = 1;
  boundary = 1;

  alignment = 0;
  p1 = test_init_and_alloc_simple( alloc_size, alignment, boundary );

  alignment = test_page_size();
  test_init_and_alloc( alloc_size, alignment, boundary, p1 );

  puts( "\tcreate a block which is bigger then the first free space" );

  alignment = 0;
  boundary = 0;

  alloc_size = test_page_size();
  p1 = test_init_and_alloc_simple( alloc_size, alignment, boundary );
  p2 = test_alloc_simple( alloc_size, alignment, boundary );
  rtems_test_assert( p2 );

  test_free( p1 );

  alloc_size = 2 * alloc_size;
  p3 = test_alloc_simple( alloc_size, alignment, boundary );
  rtems_test_assert( p1 != p3 );

  puts( "\tset boundary before allocation begin" );

  alloc_size = 1;
  alignment = 0;
  boundary = last_alloc_begin - test_page_size();
  p1 = test_init_and_alloc_simple( alloc_size, alignment, boundary );
  rtems_test_assert( (uintptr_t ) p1 >= boundary );

  puts( "\tset boundary between allocation begin and end" );
  alloc_size = test_page_size();
  alignment = 0;
  boundary = last_alloc_begin - alloc_size / 2;
  p1 = test_init_and_alloc_simple( alloc_size, alignment, boundary );
  rtems_test_assert( (uintptr_t ) p1 + alloc_size <= boundary );

  puts( "\tset boundary after allocation end" );
  alloc_size = 1;
  alignment = 0;
  boundary = last_alloc_begin;
  p1 = test_init_and_alloc_simple( alloc_size, alignment, boundary );
  rtems_test_assert( (uintptr_t ) p1 + alloc_size < boundary );

  puts( "\tset boundary on allocation end" );
  alloc_size = TEST_DEFAULT_PAGE_SIZE - HEAP_BLOCK_HEADER_SIZE;
  alignment = 0;
  boundary = last_block_begin;
  p1 = (void *) (last_alloc_begin - TEST_DEFAULT_PAGE_SIZE);
  test_init_and_alloc( alloc_size, alignment, boundary, p1);

  puts( "\talign the allocation to different positions in the block header" );

  page_size = sizeof(uintptr_t);
  alloc_size = 1;
  boundary = 0;

  test_heap_init( page_size );

  /* Force the page size to a small enough value */
  TestHeap.page_size = page_size;

  alignment = first_page_begin - sizeof(uintptr_t);
  p1 = test_alloc( alloc_size, alignment, boundary, NULL );

  first_page_begin = ((uintptr_t) TestHeap.first_block ) + HEAP_BLOCK_HEADER_SIZE;
  alignment = first_page_begin + sizeof(uintptr_t);
  p1 = test_alloc( alloc_size, alignment, boundary, NULL );

  first_page_begin = ((uintptr_t) TestHeap.first_block )
	  + HEAP_BLOCK_HEADER_SIZE;
  alignment = first_page_begin;
  p1 = test_alloc_simple( alloc_size, alignment, boundary );

  puts( "\tallocate last block with different boundarys" );
  page_size = TEST_DEFAULT_PAGE_SIZE;
  test_heap_init( page_size );
  previous_last_block_begin = ((uintptr_t) TestHeap.last_block )
	  - TestHeap.min_block_size;
  previous_last_page_begin = previous_last_block_begin
	  + HEAP_BLOCK_HEADER_SIZE;
  alloc_size = TestHeap.page_size - HEAP_BLOCK_HEADER_SIZE;
  alignment = sizeof(uintptr_t);
  boundary = 0;
  p1 = test_alloc( alloc_size, alignment, boundary, (void *) (previous_last_page_begin + sizeof(uintptr_t)));

  test_heap_init( page_size );
  boundary = ((uintptr_t) TestHeap.last_block );
  p1 = test_alloc( alloc_size, alignment, boundary, (void *) previous_last_page_begin );

  puts( "\tbreak the boundaries and aligns more than one time" );

  page_size = CPU_ALIGNMENT * 20;
  alloc_size = page_size / 4;
  alignment = page_size / 5;
  boundary = page_size / 4;
  test_heap_init( page_size );
  p1 = (void *) (_Heap_Alloc_area_of_block( TestHeap.last_block ) - page_size );
  test_alloc( alloc_size, alignment, boundary, p1);

  puts( "\tdifferent combinations, so that there is no valid block at the end" );

  page_size = sizeof(uintptr_t);

  test_heap_init( 0 );

  /* Force the page size to a small enough value */
  TestHeap.page_size = page_size;

  alloc_size = 1;
  alignment = (uintptr_t) TestHeap.last_block;
  boundary = 0;
  p1 = test_alloc( alloc_size, alignment, boundary, NULL );

  boundary = (uintptr_t) TestHeap.last_block;
  p1 = test_alloc( alloc_size, alignment, boundary, NULL );

  alloc_size = 0;
  p1 = test_alloc( alloc_size, alignment, boundary, NULL );

  alloc_size = 1;
  alignment = sizeof(uintptr_t);
  boundary = 0;
  p1 = test_alloc_simple( alloc_size, alignment, boundary );

  puts( "\ttry to create a block, which is not possible because of the alignment and boundary" );

  alloc_size = 2;
  boundary = _Heap_Alloc_area_of_block( TestHeap.first_block )
	  + _Heap_Block_size( TestHeap.first_block ) / 2;
  alignment = boundary - 1;
  p1 = test_init_and_alloc( alloc_size, alignment, boundary, NULL );

  alloc_size = 2;
  alignment = _Heap_Alloc_area_of_block( TestHeap.first_block );
  boundary = alignment + 1;
  p1 = test_init_and_alloc( alloc_size, alignment, boundary, NULL );
}
Beispiel #22
0
bool _Heap_Free( Heap_Control *heap, void *alloc_begin_ptr )
{
  Heap_Statistics *const stats = &heap->stats;
  uintptr_t alloc_begin;
  Heap_Block *block;
  Heap_Block *next_block = NULL;
  uintptr_t block_size = 0;
  uintptr_t next_block_size = 0;
  bool next_is_free = false;

  /*
   * If NULL return true so a free on NULL is considered a valid release. This
   * is a special case that could be handled by the in heap check how-ever that
   * would result in false being returned which is wrong.
   */
  if ( alloc_begin_ptr == NULL ) {
    return true;
  }

  alloc_begin = (uintptr_t) alloc_begin_ptr;
  block = _Heap_Block_of_alloc_area( alloc_begin, heap->page_size );

  if ( !_Heap_Is_block_in_heap( heap, block ) ) {
    return false;
  }

  _Heap_Protection_block_check( heap, block );

  block_size = _Heap_Block_size( block );
  next_block = _Heap_Block_at( block, block_size );

  if ( !_Heap_Is_block_in_heap( heap, next_block ) ) {
    return false;
  }

  _Heap_Protection_block_check( heap, next_block );

  if ( !_Heap_Is_prev_used( next_block ) ) {
    _Heap_Protection_block_error( heap, block );
    return false;
  }

  if ( !_Heap_Protection_determine_block_free( heap, block ) ) {
    return true;
  }

  next_block_size = _Heap_Block_size( next_block );
  next_is_free = next_block != heap->last_block
    && !_Heap_Is_prev_used( _Heap_Block_at( next_block, next_block_size ));

  if ( !_Heap_Is_prev_used( block ) ) {
    uintptr_t const prev_size = block->prev_size;
    Heap_Block * const prev_block = _Heap_Block_at( block, -prev_size );

    if ( !_Heap_Is_block_in_heap( heap, prev_block ) ) {
      _HAssert( false );
      return( false );
    }

    /* As we always coalesce free blocks, the block that preceedes prev_block
       must have been used. */
    if ( !_Heap_Is_prev_used ( prev_block) ) {
      _HAssert( false );
      return( false );
    }

    if ( next_is_free ) {       /* coalesce both */
      uintptr_t const size = block_size + prev_size + next_block_size;
      _Heap_Free_list_remove( next_block );
      stats->free_blocks -= 1;
      prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED;
      next_block = _Heap_Block_at( prev_block, size );
      _HAssert(!_Heap_Is_prev_used( next_block));
      next_block->prev_size = size;
    } else {                      /* coalesce prev */
      uintptr_t const size = block_size + prev_size;
      prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED;
      next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED;
      next_block->prev_size = size;
    }
  } else if ( next_is_free ) {    /* coalesce next */
    uintptr_t const size = block_size + next_block_size;
    _Heap_Free_list_replace( next_block, block );
    block->size_and_flag = size | HEAP_PREV_BLOCK_USED;
    next_block  = _Heap_Block_at( block, size );
    next_block->prev_size = size;
  } else {                        /* no coalesce */
    /* Add 'block' to the head of the free blocks list as it tends to
       produce less fragmentation than adding to the tail. */
    _Heap_Free_list_insert_after( _Heap_Free_list_head( heap), block );
    block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED;
    next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED;
    next_block->prev_size = block_size;

    /* Statistics */
    ++stats->free_blocks;
    if ( stats->max_free_blocks < stats->free_blocks ) {
      stats->max_free_blocks = stats->free_blocks;
    }
  }

  /* Statistics */
  --stats->used_blocks;
  ++stats->frees;
  stats->free_size += block_size;

  return( true );
}
Beispiel #23
0
bool _Heap_Free( Heap_Control *heap, void *alloc_begin_ptr )
{
    Heap_Statistics *const stats = &heap->stats;
    uintptr_t alloc_begin = (uintptr_t) alloc_begin_ptr;
    Heap_Block *block =
        _Heap_Block_of_alloc_area( alloc_begin, heap->page_size );
    Heap_Block *next_block = NULL;
    uintptr_t block_size = 0;
    uintptr_t next_block_size = 0;
    bool next_is_free = false;

    if ( !_Heap_Is_block_in_heap( heap, block ) ) {
        return false;
    }

    block_size = _Heap_Block_size( block );
    next_block = _Heap_Block_at( block, block_size );

    if ( !_Heap_Is_block_in_heap( heap, next_block ) ) {
        _HAssert( false );
        return false;
    }

    if ( !_Heap_Is_prev_used( next_block ) ) {
        _HAssert( false );
        return false;
    }

    next_block_size = _Heap_Block_size( next_block );
    next_is_free = next_block != heap->last_block
                   && !_Heap_Is_prev_used( _Heap_Block_at( next_block, next_block_size ));

    if ( !_Heap_Is_prev_used( block ) ) {
        uintptr_t const prev_size = block->prev_size;
        Heap_Block * const prev_block = _Heap_Block_at( block, -prev_size );

        if ( !_Heap_Is_block_in_heap( heap, prev_block ) ) {
            _HAssert( false );
            return( false );
        }

        /* As we always coalesce free blocks, the block that preceedes prev_block
           must have been used. */
        if ( !_Heap_Is_prev_used ( prev_block) ) {
            _HAssert( false );
            return( false );
        }

        if ( next_is_free ) {       /* coalesce both */
            uintptr_t const size = block_size + prev_size + next_block_size;
            _Heap_Free_list_remove( next_block );
            stats->free_blocks -= 1;
            prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED;
            next_block = _Heap_Block_at( prev_block, size );
            _HAssert(!_Heap_Is_prev_used( next_block));
            next_block->prev_size = size;
        } else {                      /* coalesce prev */
            uintptr_t const size = block_size + prev_size;
            prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED;
            next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED;
            next_block->prev_size = size;
        }
    } else if ( next_is_free ) {    /* coalesce next */
        uintptr_t const size = block_size + next_block_size;
        _Heap_Free_list_replace( next_block, block );
        block->size_and_flag = size | HEAP_PREV_BLOCK_USED;
        next_block  = _Heap_Block_at( block, size );
        next_block->prev_size = size;
    } else {                        /* no coalesce */
        /* Add 'block' to the head of the free blocks list as it tends to
           produce less fragmentation than adding to the tail. */
        _Heap_Free_list_insert_after( _Heap_Free_list_head( heap), block );
        block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED;
        next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED;
        next_block->prev_size = block_size;

        /* Statistics */
        ++stats->free_blocks;
        if ( stats->max_free_blocks < stats->free_blocks ) {
            stats->max_free_blocks = stats->free_blocks;
        }
    }

    /* Statistics */
    --stats->used_blocks;
    ++stats->frees;
    stats->free_size += block_size;

    return( true );
}
boolean _Heap_Walk(
  Heap_Control  *the_heap,
  int            source,
  boolean        do_dump
)
{
  Heap_Block *the_block = the_heap->start;
  Heap_Block *const end = the_heap->final;
  Heap_Block *const tail = _Heap_Tail(the_heap);
  int error = 0;
  int passes = 0;

  do_dump = FALSE;
  /*
   * We don't want to allow walking the heap until we have
   * transferred control to the user task so we watch the
   * system state.
   */

/*
  if ( !_System_state_Is_up( _System_state_Get() ) )
    return TRUE;
*/

  if (source < 0)
    source = the_heap->stats.instance;

  if (do_dump == TRUE)
    printk("\nPASS: %d start %p final %p first %p last %p begin %p end %p\n",
      source, the_block, end,
      _Heap_First(the_heap), _Heap_Last(the_heap),
      the_heap->begin, the_heap->end);

  /*
   * Handle the 1st block
   */

  if (!_Heap_Is_prev_used(the_block)) {
    printk("PASS: %d !HEAP_PREV_USED flag of 1st block isn't set\n", source);
    error = 1;
  }

  if (the_block->prev_size != the_heap->page_size) {
    printk("PASS: %d !prev_size of 1st block isn't page_size\n", source);
    error = 1;
  }

  while ( the_block != end ) {
    uint32_t const the_size = _Heap_Block_size(the_block);
    Heap_Block *const next_block = _Heap_Block_at(the_block, the_size);
    boolean prev_used = _Heap_Is_prev_used(the_block);

    if (do_dump) {
      printk("PASS: %d block %p size %d(%c)",
        source, the_block, the_size, (prev_used ? 'U' : 'F'));
      if (prev_used)
        printk(" prev_size %d", the_block->prev_size);
      else
        printk(" (prev_size) %d", the_block->prev_size);
    }

    if (!_Heap_Is_block_in(the_heap, next_block)) {
      if (do_dump) printk("\n");
      printk("PASS: %d !block %p is out of heap\n", source, next_block);
      error = 1;
      break;
    }

    if (!_Heap_Is_prev_used(next_block)) {
      if (do_dump)
        printk( " prev %p next %p", the_block->prev, the_block->next);
      if (_Heap_Block_size(the_block) != next_block->prev_size) {
        if (do_dump) printk("\n");
        printk("PASS: %d !front and back sizes don't match", source);
        error = 1;
      }
      if (!prev_used) {
        if (do_dump || error) printk("\n");
        printk("PASS: %d !two consecutive blocks are free", source);
        error = 1;
      }

      { /* Check if 'the_block' is in the free block list */
        Heap_Block* block = _Heap_First(the_heap);
        while(block != the_block && block != tail)
          block = block->next;
        if(block != the_block) {
          if (do_dump || error) printk("\n");
          printk("PASS: %d !the_block not in the free list", source);
          error = 1;
        }
      }

    }
    if (do_dump || error) printk("\n");

    if (the_size < the_heap->min_block_size) {
      printk("PASS: %d !block size is too small\n", source);
      error = 1;
      break;
    }
    if (!_Heap_Is_aligned( the_size, the_heap->page_size)) {
      printk("PASS: %d !block size is misaligned\n", source);
      error = 1;
    }

    if (++passes > (do_dump ? 10 : 0) && error)
      break;

    the_block = next_block;
  }

  if (the_block != end) {
    printk("PASS: %d !last block address isn't equal to 'final' %p %p\n",
      source, the_block, end);
    error = 1;
  }

  if (_Heap_Block_size(the_block) != the_heap->page_size) {
    printk("PASS: %d !last block's size isn't page_size (%d != %d)\n", source,
           _Heap_Block_size(the_block), the_heap->page_size);
    error = 1;
  }

  if(do_dump && error)
    _Internal_error_Occurred( INTERNAL_ERROR_CORE, TRUE, 0xffff0000 );

  return error;

}
Beispiel #25
0
uintptr_t _Heap_Extend(
  Heap_Control *heap,
  void *extend_area_begin_ptr,
  uintptr_t extend_area_size,
  uintptr_t unused __attribute__((unused))
)
{
  Heap_Statistics *const stats = &heap->stats;
  Heap_Block *const first_block = heap->first_block;
  Heap_Block *start_block = first_block;
  Heap_Block *merge_below_block = NULL;
  Heap_Block *merge_above_block = NULL;
  Heap_Block *link_below_block = NULL;
  Heap_Block *link_above_block = NULL;
  Heap_Block *extend_first_block = NULL;
  Heap_Block *extend_last_block = NULL;
  uintptr_t const page_size = heap->page_size;
  uintptr_t const min_block_size = heap->min_block_size;
  uintptr_t const extend_area_begin = (uintptr_t) extend_area_begin_ptr;
  uintptr_t const extend_area_end = extend_area_begin + extend_area_size;
  uintptr_t const free_size = stats->free_size;
  uintptr_t extend_first_block_size = 0;
  uintptr_t extended_size = 0;
  bool extend_area_ok = false;

  if ( extend_area_end < extend_area_begin ) {
    return 0;
  }

  extend_area_ok = _Heap_Get_first_and_last_block(
    extend_area_begin,
    extend_area_size,
    page_size,
    min_block_size,
    &extend_first_block,
    &extend_last_block
  );
  if (!extend_area_ok ) {
    /* For simplicity we reject extend areas that are too small */
    return 0;
  }

  do {
    uintptr_t const sub_area_begin = (start_block != first_block) ?
      (uintptr_t) start_block : heap->area_begin;
    uintptr_t const sub_area_end = start_block->prev_size;
    Heap_Block *const end_block =
      _Heap_Block_of_alloc_area( sub_area_end, page_size );

    if (
      sub_area_end > extend_area_begin && extend_area_end > sub_area_begin
    ) {
      return 0;
    }

    if ( extend_area_end == sub_area_begin ) {
      merge_below_block = start_block;
    } else if ( extend_area_end < sub_area_end ) {
      link_below_block = start_block;
    }

    if ( sub_area_end == extend_area_begin ) {
      start_block->prev_size = extend_area_end;

      merge_above_block = end_block;
    } else if ( sub_area_end < extend_area_begin ) {
      link_above_block = end_block;
    }

    start_block = _Heap_Block_at( end_block, _Heap_Block_size( end_block ) );
  } while ( start_block != first_block );

  if ( extend_area_begin < heap->area_begin ) {
    heap->area_begin = extend_area_begin;
  } else if ( heap->area_end < extend_area_end ) {
    heap->area_end = extend_area_end;
  }

  extend_first_block_size =
    (uintptr_t) extend_last_block - (uintptr_t) extend_first_block;

  extend_first_block->prev_size = extend_area_end;
  extend_first_block->size_and_flag =
    extend_first_block_size | HEAP_PREV_BLOCK_USED;
  _Heap_Protection_block_initialize( heap, extend_first_block );

  extend_last_block->prev_size = extend_first_block_size;
  extend_last_block->size_and_flag = 0;
  _Heap_Protection_block_initialize( heap, extend_last_block );

  if ( (uintptr_t) extend_first_block < (uintptr_t) heap->first_block ) {
    heap->first_block = extend_first_block;
  } else if ( (uintptr_t) extend_last_block > (uintptr_t) heap->last_block ) {
    heap->last_block = extend_last_block;
  }

  if ( merge_below_block != NULL ) {
    _Heap_Merge_below( heap, extend_area_begin, merge_below_block );
  } else if ( link_below_block != NULL ) {
    _Heap_Link_below(
      link_below_block,
      extend_last_block
    );
  }

  if ( merge_above_block != NULL ) {
    _Heap_Merge_above( heap, merge_above_block, extend_area_end );
  } else if ( link_above_block != NULL ) {
    _Heap_Link_above(
      link_above_block,
      extend_first_block,
      extend_last_block
    );
  }

  if ( merge_below_block == NULL && merge_above_block == NULL ) {
    _Heap_Free_block( heap, extend_first_block );
  }

  _Heap_Set_last_block_size( heap );

  extended_size = stats->free_size - free_size;

  /* Statistics */
  stats->size += extended_size;

  return extended_size;
}
Beispiel #26
0
static bool _Heap_Walk_check_control(
  int source,
  Heap_Walk_printer printer,
  Heap_Control *heap
)
{
  uintptr_t const page_size = heap->page_size;
  uintptr_t const min_block_size = heap->min_block_size;
  Heap_Block *const first_free_block = _Heap_Free_list_first( heap );
  Heap_Block *const last_free_block = _Heap_Free_list_last( heap );
  Heap_Block *const first_block = heap->first_block;
  Heap_Block *const last_block = heap->last_block;

  (*printer)(
    source,
    false,
    "page size %u, min block size %u\n"
      "\tarea begin 0x%08x, area end 0x%08x\n"
      "\tfirst block 0x%08x, last block 0x%08x\n"
      "\tfirst free 0x%08x, last free 0x%08x\n",
    page_size, min_block_size,
    heap->area_begin, heap->area_end,
    first_block, last_block,
    first_free_block, last_free_block
  );

  if ( page_size == 0 ) {
    (*printer)( source, true, "page size is zero\n" );

    return false;
  }

  if ( !_Addresses_Is_aligned( (void *) page_size ) ) {
    (*printer)(
      source,
      true,
      "page size %u not CPU aligned\n",
      page_size
    );

    return false;
  }

  if ( !_Heap_Is_aligned( min_block_size, page_size ) ) {
    (*printer)(
      source,
      true,
      "min block size %u not page aligned\n",
      min_block_size
    );

    return false;
  }

  if (
    !_Heap_Is_aligned( _Heap_Alloc_area_of_block( first_block ), page_size )
  ) {
    (*printer)(
      source,
      true,
      "first block 0x%08x: alloc area not page aligned\n",
      first_block
    );

    return false;
  }

  if ( !_Heap_Is_prev_used( first_block ) ) {
    (*printer)(
      source,
      true,
      "first block: HEAP_PREV_BLOCK_USED is cleared\n"
    );

    return false;
  }

  if ( _Heap_Is_free( last_block ) ) {
    (*printer)(
      source,
      true,
      "last block: is free\n"
    );

    return false;
  }

  if (
    _Heap_Block_at( last_block, _Heap_Block_size( last_block ) ) != first_block
  ) {
    (*printer)(
      source,
      true,
      "last block: next block is not the first block\n"
    );

    return false;
  }

  return _Heap_Walk_check_free_list( source, printer, heap );
}
Beispiel #27
0
static bool _Heap_Walk_check_free_block(
  int source,
  Heap_Walk_printer printer,
  Heap_Control *heap,
  Heap_Block *block
)
{
  Heap_Block *const free_list_tail = _Heap_Free_list_tail( heap );
  Heap_Block *const free_list_head = _Heap_Free_list_head( heap );
  Heap_Block *const first_free_block = _Heap_Free_list_first( heap );
  Heap_Block *const last_free_block = _Heap_Free_list_last( heap );
  bool const prev_used = _Heap_Is_prev_used( block );
  uintptr_t const block_size = _Heap_Block_size( block );
  Heap_Block *const next_block = _Heap_Block_at( block, block_size );

  (*printer)(
    source,
    false,
    "block 0x%08x: size %u, prev 0x%08x%s, next 0x%08x%s\n",
    block,
    block_size,
    block->prev,
    block->prev == first_free_block ?
      " (= first free)"
        : (block->prev == free_list_head ? " (= head)" : ""),
    block->next,
    block->next == last_free_block ?
      " (= last free)"
        : (block->next == free_list_tail ? " (= tail)" : "")
  );

  if ( block_size != next_block->prev_size ) {
    (*printer)(
      source,
      true,
      "block 0x%08x: size %u != size %u (in next block 0x%08x)\n",
      block,
      block_size,
      next_block->prev_size,
      next_block
    );

    return false;
  }

  if ( !prev_used ) {
    (*printer)(
      source,
      true,
      "block 0x%08x: two consecutive blocks are free\n",
      block
    );

    return false;
  }

  if ( !_Heap_Walk_is_in_free_list( heap, block ) ) {
    (*printer)(
      source,
      true,
      "block 0x%08x: free block not in free list\n",
      block
    );

    return false;
  }

  return true;
}
Beispiel #28
0
static void test_main_loop(void)
{
  void *p1 = NULL;
  void *p2 = NULL;
  Heap_Block *block = NULL;
  Heap_Block *next_block = NULL;

  puts( "Test the main loop" );

  puts( "\tset the blocksize so, that the next block is outside the heap" );
  test_heap_init_custom();
  /* use all blocks */
  p1 = test_fill_heap();
  block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size );
  block->size_and_flag = ( 2 * _Heap_Block_size( block ) ) | HEAP_PREV_BLOCK_USED;
  test_call_heap_walk( false );

  puts( "\twalk a heap with blocks with different states of the previous-used flag" );
  test_heap_init_custom();
  test_create_heap_with_gaps();
  test_allocate_block(); /* fill one gap */
  test_call_heap_walk( true );

  puts( "\tcreate a block with a not page aligned size" );
  test_heap_init_custom();
  p1 = test_allocate_block();
  p2 = test_allocate_block();
  _Heap_Free( &TestHeap, p1 );
  block = _Heap_Block_of_alloc_area( (uintptr_t) p2, TestHeap.page_size );
  block->size_and_flag = (3 * TestHeap.page_size / 2) & ~HEAP_PREV_BLOCK_USED;
  test_call_heap_walk( false );

  puts( "\tcreate a block with a size smaller than the min_block_size" );
  test_heap_init_default();
  p1 = test_allocate_block();
  p2 = test_allocate_block();
  _Heap_Free( &TestHeap, p1 );
  block = _Heap_Block_of_alloc_area( (uintptr_t) p2, TestHeap.page_size );
  block->size_and_flag = 0;
  test_call_heap_walk( false );

  puts( "\tmake a block with a size, so that the block reaches into the next block" );
  test_heap_init_default();
  p1 = test_allocate_block();
  p2 = test_allocate_block();
  block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size  );
  block->size_and_flag = ( 3 * _Heap_Block_size( block ) / 2 ) | HEAP_PREV_BLOCK_USED;
  test_call_heap_walk( false );

  puts( "\tcreate a block with invalid successor" );
  test_heap_init_default();
  test_allocate_block();
  p1 = test_allocate_block();
  block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size  );
  block->size_and_flag = (0 - TestHeap.page_size) | HEAP_PREV_BLOCK_USED;
  test_call_heap_walk( false );

  puts( "\tmake a block with a size, so that it includes the next block" );
  test_heap_init_default();
  p1 = test_allocate_block();
  p2 = test_allocate_block();
  block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size  );
  next_block = _Heap_Block_at( block, _Heap_Block_size( block ) );
  block->size_and_flag = ( _Heap_Block_size( block ) + _Heap_Block_size( next_block ) ) | HEAP_PREV_BLOCK_USED;
  test_call_heap_walk( true );
}
Beispiel #29
0
bool _Heap_Walk(
  Heap_Control *heap,
  int source,
  bool dump
)
{
  uintptr_t const page_size = heap->page_size;
  uintptr_t const min_block_size = heap->min_block_size;
  Heap_Block *const first_block = heap->first_block;
  Heap_Block *const last_block = heap->last_block;
  Heap_Block *block = first_block;
  Heap_Walk_printer printer = dump ?
    _Heap_Walk_print : _Heap_Walk_print_nothing;

  if ( !_System_state_Is_up( _System_state_Get() ) ) {
    return true;
  }

  if ( !_Heap_Walk_check_control( source, printer, heap ) ) {
    return false;
  }

  do {
    uintptr_t const block_begin = (uintptr_t) block;
    uintptr_t const block_size = _Heap_Block_size( block );
    bool const prev_used = _Heap_Is_prev_used( block );
    Heap_Block *const next_block = _Heap_Block_at( block, block_size );
    uintptr_t const next_block_begin = (uintptr_t) next_block;
    bool const is_not_last_block = block != last_block;

    if ( !_Heap_Is_block_in_heap( heap, next_block ) ) {
      (*printer)(
        source,
        true,
        "block 0x%08x: next block 0x%08x not in heap\n",
        block,
        next_block
      );

      return false;
    }

    if ( !_Heap_Is_aligned( block_size, page_size ) && is_not_last_block ) {
      (*printer)(
        source,
        true,
        "block 0x%08x: block size %u not page aligned\n",
        block,
        block_size
      );

      return false;
    }

    if ( block_size < min_block_size && is_not_last_block ) {
      (*printer)(
        source,
        true,
        "block 0x%08x: size %u < min block size %u\n",
        block,
        block_size,
        min_block_size
      );

      return false;
    }

    if ( next_block_begin <= block_begin && is_not_last_block ) {
      (*printer)(
        source,
        true,
        "block 0x%08x: next block 0x%08x is not a successor\n",
        block,
        next_block
      );

      return false;
    }

    if ( !_Heap_Is_prev_used( next_block ) ) {
      if ( !_Heap_Walk_check_free_block( source, printer, heap, block ) ) {
        return false;
      }
    } else if (prev_used) {
      (*printer)(
        source,
        false,
        "block 0x%08x: size %u\n",
        block,
        block_size
      );
    } else {
      (*printer)(
        source,
        false,
        "block 0x%08x: size %u, prev_size %u\n",
        block,
        block_size,
        block->prev_size
      );
    }

    block = next_block;
  } while ( block != first_block );

  return true;
}
void *_Heap_Allocate_aligned(
  Heap_Control *the_heap,
  size_t        size,
  uint32_t      alignment
)
{
  uint32_t search_count;
  Heap_Block *the_block;

  void *user_ptr = NULL;
  uint32_t  const page_size = the_heap->page_size;
  Heap_Statistics *const stats = &the_heap->stats;
  Heap_Block *const tail = _Heap_Tail(the_heap);

  uint32_t const end_to_user_offs = size - HEAP_BLOCK_HEADER_OFFSET;

  uint32_t const the_size =
    _Heap_Calc_block_size(size, page_size, the_heap->min_block_size);

  if(the_size == 0)
    return NULL;

  if(alignment == 0)
    alignment = CPU_ALIGNMENT;

  /* Find large enough free block that satisfies the alignment requirements. */

  for(the_block = _Heap_First(the_heap), search_count = 0;
      the_block != tail;
      the_block = the_block->next, ++search_count)
  {
    uint32_t const block_size = _Heap_Block_size(the_block);

    /* As we always coalesce free blocks, prev block must have been used. */
    _HAssert(_Heap_Is_prev_used(the_block));

    if(block_size >= the_size) { /* the_block is large enough. */

      _H_uptr_t user_addr;
      _H_uptr_t aligned_user_addr;
      _H_uptr_t const user_area = _H_p2u(_Heap_User_area(the_block));

      /* Calculate 'aligned_user_addr' that will become the user pointer we
         return. It should be at least 'end_to_user_offs' bytes less than the
         the 'block_end' and should be aligned on 'alignment' boundary.
         Calculations are from the 'block_end' as we are going to split free
         block so that the upper part of the block becomes used block. */
      _H_uptr_t const block_end = _H_p2u(the_block) + block_size;
      aligned_user_addr = block_end - end_to_user_offs;
      _Heap_Align_down_uptr(&aligned_user_addr, alignment);

      /* 'user_addr' is the 'aligned_user_addr' further aligned down to the
         'page_size' boundary. We need it as blocks' user areas should begin
         only at 'page_size' aligned addresses */
      user_addr = aligned_user_addr;
      _Heap_Align_down_uptr(&user_addr, page_size);

      /* Make sure 'user_addr' calculated didn't run out of 'the_block'. */
      if(user_addr >= user_area) {

        /* The block seems to be acceptable. Check if the remainder of
           'the_block' is less than 'min_block_size' so that 'the_block' won't
           actually be split at the address we assume. */
        if(user_addr - user_area < the_heap->min_block_size) {

          /* The block won't be split, so 'user_addr' will be equal to the
             'user_area'. */
          user_addr = user_area;

          /* We can't allow the distance between 'user_addr' and
           'aligned_user_addr' to be outside of [0,page_size) range. If we do,
           we will need to store this distance somewhere to be able to
           resurrect the block address from the user pointer. (Having the
           distance within [0,page_size) range allows resurrection by
           aligning user pointer down to the nearest 'page_size' boundary.) */
          if(aligned_user_addr - user_addr >= page_size) {

            /* The user pointer will be too far from 'user_addr'. See if we
               can make 'aligned_user_addr' to be close enough to the
               'user_addr'. */
            aligned_user_addr = user_addr;
            _Heap_Align_up_uptr(&aligned_user_addr, alignment);
            if(aligned_user_addr - user_addr >= page_size) {
              /* No, we can't use the block */
              aligned_user_addr = 0;
            }
          }
        }

        if(aligned_user_addr) {

          /* The block is indeed acceptable: calculate the size of the block
             to be allocated and perform allocation. */
          uint32_t const alloc_size =
            block_end - user_addr + HEAP_BLOCK_USER_OFFSET;

          _HAssert(_Heap_Is_aligned_ptr((void*)aligned_user_addr, alignment));

          the_block = block_allocate(the_heap, the_block, alloc_size);

          stats->searches += search_count + 1;
          stats->allocs += 1;

          check_result(the_heap, the_block, user_addr,
            aligned_user_addr, size);

          user_ptr = (void*)aligned_user_addr;
          break;
        }
      }
    }
  }

  if(stats->max_search < search_count)
    stats->max_search = search_count;

  return user_ptr;
}