/* * Copyright Institute of Communication and Computer Systems (ICCS) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /** * @file src/memalign.c * @author Ioannis Koutras (joko@microlab.ntua.gr) * @date November 2012 * * @brief Implementation of memory-aligned allocation calls for * freelist-organized raw blocks. */ #include "dmmlib/memalign.h" #include #include #include #include "dmmlib/config.h" #include "dmmlib/dmmlib.h" #include "memcpy.h" #include "default_rb.h" #include "dmmlib_trace.h" #include "locks.h" #include "statistics.h" #ifdef BITMAP_RB_ONLY #error Current memory-aligned allocation implementation supports only \ freelist-organized raw blocks. #endif /* BITMAP_RB_ONLY */ /** The obsolete function memalign() allocates size bytes and returns a pointer * to the allocated memory. The memory address will be a multiple of alignment, * which must be a power of two. */ void *memalign(size_t alignment, size_t size) { void *memptr; DEFAULT_RB_T *encapsulated_rb; raw_block_header_t *raw_block; size_t extra_size; /* Verify that alignment is a power of two */ assert((alignment && !(alignment & (alignment - 1))) != 0); memptr = NULL; /* extra_size denotes the worst-case scenario. Because memcpy() is used, at * least HEADER_SIZE is needed for no memory overlap. */ extra_size = sizeof(raw_block_header_t) + alignment - 1; if(2 * (size + extra_size) < SYS_ALLOC_SIZE - sizeof(raw_block_header_t) - sizeof(freelist_rb_t)) { /* Search the available freelist-organized raw blocks */ SLIST_FOREACH(raw_block, &systemallocator.rb_head, pointers) { #ifdef TRYLOCK_ON_MALLOC if(TRYLOCK_RAW_BLOCK(raw_block) == 0) { #else /* TRYLOCK_ON_MALLOC */ LOCK_RAW_BLOCK(raw_block); #endif /* TRYLOCK_ON_MALLOC */ encapsulated_rb = (freelist_rb_t *) ((uintptr_t) raw_block + sizeof(raw_block_header_t)); memptr = freelist_memalign(encapsulated_rb, alignment, size); UNLOCK_RAW_BLOCK(raw_block); if(memptr != NULL) { break; } #ifdef TRYLOCK_ON_MALLOC } #endif /* TRYLOCK_ON_MALLOC */ } if(memptr == NULL) { /* no block was found, try to create a new one */ raw_block = create_raw_block((size_t) SYS_ALLOC_SIZE, DEFAULT_RB_TYPE); if(raw_block != NULL) { LOCK_GLOBAL(); LOCK_RAW_BLOCK(raw_block); SLIST_INSERT_HEAD(&systemallocator.rb_head, raw_block, pointers); UNLOCK_GLOBAL(); encapsulated_rb = (DEFAULT_RB_T *) ((uintptr_t) raw_block + sizeof(raw_block_header_t)); memptr = freelist_memalign(encapsulated_rb, alignment, size); UNLOCK_RAW_BLOCK(raw_block); } } } else { /* A big block has to be created */ memptr = (void *)create_raw_block(size + extra_size + sizeof(raw_block_header_t), BIGBLOCK); if(memptr != NULL) { memptr = (void *)((uintptr_t) memptr + sizeof(raw_block_header_t)); /* Check if alignment is needed */ if(((uintptr_t) memptr) % alignment != 0) { size_t padding = (- (size_t) memptr) & (alignment - 1); while(padding < sizeof(raw_block_header_t)) { padding += alignment; } /* Sometimes a deadlock is observed unless the old mutex is * destroyed. */ DESTROY_RAW_BLOCK_LOCK(((raw_block_header_t *) memptr)); /* Copy the raw block's header to the new location */ memcpy((void *)((uintptr_t) memptr - sizeof(raw_block_header_t) + padding), (void *)((uintptr_t) memptr - sizeof(raw_block_header_t)), sizeof(raw_block_header_t) ); /* Update *memptr */ memptr = (void *)((uintptr_t) memptr + padding); /* Update big block's size and requested size */ raw_block_header_t *aligned_header = (raw_block_header_t *)((uintptr_t) memptr - sizeof(raw_block_header_t)); INIT_RAW_BLOCK_LOCK(aligned_header); LOCK_RAW_BLOCK(aligned_header); aligned_header->size -= padding; #ifdef REQUEST_SIZE_INFO aligned_header->requested_size = size; #endif /* REQUEST_SIZE_INFO */ UNLOCK_RAW_BLOCK(aligned_header); } #ifdef WITH_DEBUG LOCK_GLOBAL(); LOCK_RAW_BLOCK(((raw_block_header_t *) ((uintptr_t) memptr - sizeof(raw_block_header_t)))); SLIST_INSERT_HEAD(&systemallocator.bb_head, (raw_block_header_t *) ((uintptr_t) memptr - sizeof(raw_block_header_t)), pointers); UNLOCK_RAW_BLOCK(((raw_block_header_t *) ((uintptr_t) memptr - sizeof(raw_block_header_t)))); UNLOCK_GLOBAL(); #endif /* WITH_DEBUG */ } } if(memptr != NULL) { /* Assert that the returned address is a multiple of alignment */ assert((uintptr_t) memptr % alignment == 0); #ifdef REQUEST_SIZE_INFO UPDATE_GLOBAL_STATS(MEMALIGN, size); #else /* REQUEST_SIZE_INFO */ UPDATE_GLOBAL_STATS(MEMALIGN); #endif /* REQUEST_SIZE_INFO */ } MEM_TRACE("dmmlib - ma %p %zu %zu\n", *memptr, alignment, size); return memptr; } /** The function posix_memalign() allocates size bytes and places the address of * the allocated memory in *memptr. The address of the allocated memory will be * a multiple of alignment, which must be a power of two and a multiple of * sizeof(void *). If size is 0, then posix_memalign() returns either NULL, or a * unique pointer value that can later be successfully passed to free(). */ int posix_memalign(void **memptr, size_t alignment, size_t size) { void *ptr = NULL; /* Input check */ if(size == 0 || (alignment & 1) != 0 || alignment % sizeof(void *) != 0) { return -1; } ptr = memalign(alignment, size); if(ptr != NULL) { *memptr = ptr; return 0; } return -1; }