mirror of https://gitee.com/openkylin/openmpi.git
385 lines
16 KiB
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);
|
|
}
|