openmpi/opal/mca/allocator/bucket/allocator_bucket_alloc.c

385 lines
16 KiB
C

/*
* Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
* University Research and Technology
* Corporation. All rights reserved.
* Copyright (c) 2004-2005 The University of Tennessee and The University
* of Tennessee Research Foundation. All rights
* reserved.
* Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
* University of Stuttgart. All rights reserved.
* Copyright (c) 2004-2005 The Regents of the University of California.
* All rights reserved.
* Copyright (c) 2007 IBM Corp., All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
#include "opal_config.h"
#include "opal/constants.h"
#include "opal/mca/allocator/bucket/allocator_bucket_alloc.h"
/**
* The define controls the size in bytes of the 1st bucket and hence every one
* afterwards.
*/
#define MCA_ALLOCATOR_BUCKET_1_SIZE 8
/**
* This is the number of left bit shifts from 1 needed to get to the number of
* bytes in the initial memory buckets
*/
#define MCA_ALLOCATOR_BUCKET_1_BITSHIFTS 3
/*
* Initializes the mca_allocator_bucket_options_t data structure for the passed
* parameters.
*/
mca_allocator_bucket_t * mca_allocator_bucket_init(
mca_allocator_base_module_t * mem,
int num_buckets,
mca_allocator_base_component_segment_alloc_fn_t get_mem_funct,
mca_allocator_base_component_segment_free_fn_t free_mem_funct)
{
mca_allocator_bucket_t * mem_options = (mca_allocator_bucket_t *) mem;
int i;
size_t size;
/* if a bad value is used for the number of buckets, default to 30 */
if(num_buckets <= 0) {
num_buckets = 30;
}
/* initialize the array of buckets */
size = sizeof(mca_allocator_bucket_bucket_t) * num_buckets;
mem_options->buckets = (mca_allocator_bucket_bucket_t*) malloc(size);
if(NULL == mem_options->buckets) {
return(NULL);
}
for(i = 0; i < num_buckets; i++) {
mem_options->buckets[i].free_chunk = NULL;
mem_options->buckets[i].segment_head = NULL;
OBJ_CONSTRUCT(&(mem_options->buckets[i].lock), opal_mutex_t);
}
mem_options->num_buckets = num_buckets;
mem_options->get_mem_fn = get_mem_funct;
mem_options->free_mem_fn = free_mem_funct;
return(mem_options);
}
/*
* Accepts a request for memory in a specific region defined by the
* mca_allocator_bucket_options_t struct and returns a pointer to memory in that
* region or NULL if there was an error
*
*/
void * mca_allocator_bucket_alloc(mca_allocator_base_module_t * mem,
size_t size)
{
mca_allocator_bucket_t * mem_options = (mca_allocator_bucket_t *) mem;
/* initialize for the later bit shifts */
int bucket_num = 0;
size_t bucket_size = MCA_ALLOCATOR_BUCKET_1_SIZE;
size_t allocated_size;
mca_allocator_bucket_chunk_header_t * chunk;
mca_allocator_bucket_chunk_header_t * first_chunk;
mca_allocator_bucket_segment_head_t * segment_header;
/* add the size of the header into the amount we need to request */
size += sizeof(mca_allocator_bucket_chunk_header_t);
/* figure out which bucket it will come from. */
while(size > bucket_size) {
bucket_num++;
bucket_size <<= 1;
}
/* now that we know what bucket it will come from, we must get the lock */
OPAL_THREAD_LOCK(&(mem_options->buckets[bucket_num].lock));
/* see if there is already a free chunk */
if(NULL != mem_options->buckets[bucket_num].free_chunk) {
chunk = mem_options->buckets[bucket_num].free_chunk;
mem_options->buckets[bucket_num].free_chunk = chunk->u.next_free;
chunk->u.bucket = bucket_num;
/* go past the header */
chunk += 1;
/*release the lock */
OPAL_THREAD_UNLOCK(&(mem_options->buckets[bucket_num].lock));
return((void *) chunk);
}
/* figure out the size of bucket we need */
allocated_size = bucket_size;
/* we have to add in the size of the segment header into the
* amount we need to request */
allocated_size += sizeof(mca_allocator_bucket_segment_head_t);
/* attempt to get the memory */
segment_header = (mca_allocator_bucket_segment_head_t *)
mem_options->get_mem_fn(mem_options->super.alc_context, &allocated_size);
if(NULL == segment_header) {
/* release the lock */
OPAL_THREAD_UNLOCK(&(mem_options->buckets[bucket_num].lock));
return(NULL);
}
/* if were allocated more memory then we actually need, then we will try to
* break it up into multiple chunks in the current bucket */
allocated_size -= (sizeof(mca_allocator_bucket_segment_head_t) + bucket_size);
chunk = first_chunk = segment_header->first_chunk =
(mca_allocator_bucket_chunk_header_t *) (segment_header + 1);
/* add the segment into the segment list */
segment_header->next_segment = mem_options->buckets[bucket_num].segment_head;
mem_options->buckets[bucket_num].segment_head = segment_header;
if(allocated_size >= bucket_size) {
mem_options->buckets[bucket_num].free_chunk =
(mca_allocator_bucket_chunk_header_t *) ((char *) chunk + bucket_size);
chunk->next_in_segment = (mca_allocator_bucket_chunk_header_t *)
((char *)chunk + bucket_size);
while(allocated_size >= bucket_size) {
chunk = (mca_allocator_bucket_chunk_header_t *) ((char *) chunk + bucket_size);
chunk->u.next_free = (mca_allocator_bucket_chunk_header_t *)
((char *) chunk + bucket_size);
chunk->next_in_segment = chunk->u.next_free;
allocated_size -= bucket_size;
}
chunk->next_in_segment = first_chunk;
chunk->u.next_free = NULL;
} else {
first_chunk->next_in_segment = first_chunk;
}
first_chunk->u.bucket = bucket_num;
OPAL_THREAD_UNLOCK(&(mem_options->buckets[bucket_num].lock));
/* return the memory moved past the header */
return((void *) (first_chunk + 1));
}
/*
* allocates an aligned region of memory
*/
void * mca_allocator_bucket_alloc_align(mca_allocator_base_module_t * mem,
size_t size, size_t alignment)
{
mca_allocator_bucket_t * mem_options = (mca_allocator_bucket_t *) mem;
int bucket_num = 1;
void * ptr;
size_t aligned_max_size, bucket_size;
size_t alignment_off, allocated_size;
mca_allocator_bucket_chunk_header_t * chunk;
mca_allocator_bucket_chunk_header_t * first_chunk;
mca_allocator_bucket_segment_head_t * segment_header;
char * aligned_memory;
/* since we do not have a way to get pre aligned memory, we need to request
* a chunk then return an aligned spot in it. In the worst case we need
* the requested size plus the alignment and the header size */
aligned_max_size = size + alignment + sizeof(mca_allocator_bucket_chunk_header_t)
+ sizeof(mca_allocator_bucket_segment_head_t);
bucket_size = size + sizeof(mca_allocator_bucket_chunk_header_t);
allocated_size = aligned_max_size;
/* get some memory */
ptr = mem_options->get_mem_fn(mem_options->super.alc_context, &allocated_size);
if(NULL == ptr) {
return(NULL);
}
/* the first part of the memory is the segment header */
segment_header = (mca_allocator_bucket_segment_head_t *) ptr;
/* we temporarily define the first chunk to be right after the segment_header */
first_chunk = (mca_allocator_bucket_chunk_header_t *) (segment_header + 1);
/* we want to align the memory right after the header, so we go past the header */
aligned_memory = (char *) (first_chunk + 1);
/* figure out how much the alignment is off by */
alignment_off = ((size_t) aligned_memory) % alignment;
aligned_memory += (alignment - alignment_off);
/* we now have an aligned piece of memory. Now we have to put the chunk
* header right before the aligned memory */
first_chunk = (mca_allocator_bucket_chunk_header_t *) aligned_memory - 1;
while(bucket_size > MCA_ALLOCATOR_BUCKET_1_SIZE) {
bucket_size >>= 1;
bucket_num++;
}
bucket_size = 1;
bucket_size <<= MCA_ALLOCATOR_BUCKET_1_BITSHIFTS + bucket_num;
/* if were allocated more memory then we actually need, then we will try to
* break it up into multiple chunks in the current bucket */
allocated_size -= aligned_max_size;
chunk = segment_header->first_chunk = first_chunk;
/* we now need to get a lock on the bucket */
OPAL_THREAD_LOCK(&(mem_options->buckets[bucket_num].lock));
/* add the segment into the segment list */
segment_header->next_segment = mem_options->buckets[bucket_num].segment_head;
mem_options->buckets[bucket_num].segment_head = segment_header;
if(allocated_size >= bucket_size) {
mem_options->buckets[bucket_num].free_chunk =
(mca_allocator_bucket_chunk_header_t *) ((char *) chunk + bucket_size);
chunk->next_in_segment = (mca_allocator_bucket_chunk_header_t *)
((char *)chunk + bucket_size);
while(allocated_size >= bucket_size) {
chunk = (mca_allocator_bucket_chunk_header_t *) ((char *) chunk + bucket_size);
chunk->u.next_free = (mca_allocator_bucket_chunk_header_t *)
((char *) chunk + bucket_size);
chunk->next_in_segment = chunk->u.next_free;
allocated_size -= bucket_size;
}
chunk->next_in_segment = first_chunk;
chunk->u.next_free = NULL;
} else {
first_chunk->next_in_segment = first_chunk;
}
first_chunk->u.bucket = bucket_num;
OPAL_THREAD_UNLOCK(&(mem_options->buckets[bucket_num].lock));
/* return the aligned memory */
return((void *) (aligned_memory));
}
/*
* function to reallocate the segment of memory
*/
void * mca_allocator_bucket_realloc(mca_allocator_base_module_t * mem,
void * ptr, size_t size)
{
mca_allocator_bucket_t * mem_options = (mca_allocator_bucket_t *) mem;
/* initialize for later bit shifts */
size_t bucket_size = 1;
int bucket_num;
void * ret_ptr;
/* get the header of the chunk */
mca_allocator_bucket_chunk_header_t * chunk = (mca_allocator_bucket_chunk_header_t *) ptr - 1;
bucket_num = chunk->u.bucket;
bucket_size <<= (bucket_num + MCA_ALLOCATOR_BUCKET_1_BITSHIFTS);
/* since the header area is not available to the user, we need to
* subtract off the header size */
bucket_size -= sizeof(mca_allocator_bucket_chunk_header_t);
/* if the requested size is less than or equal to what they ask for,
* just give them back what they passed in */
if(size <= bucket_size) {
return(ptr);
}
/* we need a new space in memory, so let's get it */
ret_ptr = mca_allocator_bucket_alloc((mca_allocator_base_module_t *) mem_options, size);
if(NULL == ret_ptr) {
/* we were unable to get a larger area of memory */
return(NULL);
}
/* copy what they have in memory to the new spot */
memcpy(ret_ptr, ptr, bucket_size);
/* free the old area in memory */
mca_allocator_bucket_free((mca_allocator_base_module_t *) mem_options, ptr);
return(ret_ptr);
}
/*
* Frees the passed region of memory
*
*/
void mca_allocator_bucket_free(mca_allocator_base_module_t * mem, void * ptr)
{
mca_allocator_bucket_t * mem_options = (mca_allocator_bucket_t *) mem;
mca_allocator_bucket_chunk_header_t * chunk = (mca_allocator_bucket_chunk_header_t *) ptr - 1;
int bucket_num = chunk->u.bucket;
OPAL_THREAD_LOCK(&(mem_options->buckets[bucket_num].lock));
chunk->u.next_free = mem_options->buckets[bucket_num].free_chunk;
mem_options->buckets[bucket_num].free_chunk = chunk;
OPAL_THREAD_UNLOCK(&(mem_options->buckets[bucket_num].lock));
}
/*
* Frees all the memory from all the buckets back to the system. Note that
* this function only frees memory that was previously freed with
* mca_allocator_bucket_free().
*
*/
int mca_allocator_bucket_cleanup(mca_allocator_base_module_t * mem)
{
mca_allocator_bucket_t * mem_options = (mca_allocator_bucket_t *) mem;
int i;
mca_allocator_bucket_chunk_header_t * next_chunk;
mca_allocator_bucket_chunk_header_t * chunk;
mca_allocator_bucket_chunk_header_t * first_chunk;
mca_allocator_bucket_segment_head_t ** segment_header;
mca_allocator_bucket_segment_head_t * segment;
bool empty = true;
for(i = 0; i < mem_options->num_buckets; i++) {
OPAL_THREAD_LOCK(&(mem_options->buckets[i].lock));
segment_header = &(mem_options->buckets[i].segment_head);
if( NULL == (*segment_header) ) {
OPAL_THREAD_UNLOCK(&(mem_options->buckets[i].lock));
continue;
}
/* first we suppose the execution is correct and all chunks
* have been correctly released. Therefore, if we make sure
* all segments only contain free items then we can release
* everything in one go.
*/
empty = true;
segment = mem_options->buckets[i].segment_head;
while( (true == empty) && (NULL != segment) ) {
first_chunk = segment->first_chunk;
chunk = first_chunk;
/* determine if the segment is free */
do {
if(chunk->u.bucket == i) {
empty = false;
break;
}
chunk = chunk->next_in_segment;
} while(chunk != first_chunk);
/* go to next segment */
segment = segment->next_segment;
}
if( true == empty ) { /* all segments ready for release */
mca_allocator_bucket_segment_head_t* next_segment;
segment = mem_options->buckets[i].segment_head;
while( NULL != segment ) {
next_segment = segment->next_segment;
/* free the memory */
if(mem_options->free_mem_fn)
mem_options->free_mem_fn(mem->alc_context, segment);
segment = next_segment;
}
mem_options->buckets[i].free_chunk = NULL;
mem_options->buckets[i].segment_head = NULL;
} else {
/* traverse the list of segment headers until we hit NULL */
while(NULL != *segment_header) {
first_chunk = (*segment_header)->first_chunk;
chunk = first_chunk;
empty = true;
/* determine if the segment is free */
do {
if(chunk->u.bucket == i) {
empty = false;
}
chunk = chunk->next_in_segment;
} while(empty && (chunk != first_chunk));
if(empty) {
chunk = first_chunk;
/* remove the chunks from the free list */
do {
if(mem_options->buckets[i].free_chunk == chunk) {
mem_options->buckets[i].free_chunk = chunk->u.next_free;
} else {
next_chunk = mem_options->buckets[i].free_chunk;
while(next_chunk->u.next_free != chunk) {
next_chunk = next_chunk->u.next_free;
}
next_chunk->u.next_free = chunk->u.next_free;
}
} while((chunk = chunk->next_in_segment) != first_chunk);
/* set the segment list to point to the next segment */
segment = *segment_header;
*segment_header = segment->next_segment;
/* free the memory */
if(mem_options->free_mem_fn)
mem_options->free_mem_fn(mem->alc_context, segment);
} else {
/* go to next segment */
segment_header = &((*segment_header)->next_segment);
}
}
}
/* relese the lock on the bucket */
OPAL_THREAD_UNLOCK(&(mem_options->buckets[i].lock));
}
return(OPAL_SUCCESS);
}