/* * Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * malloc/__heap_expand_current_part.c * Attemps to extend the current part of the heap or create a new part. */ #include #include #include #include #include #include #ifdef __is_sortix_libk #include #endif bool __heap_expand_current_part(size_t requested_expansion) { // Determine the current page size. #ifdef __is_sortix_libk size_t page_size = libk_getpagesize(); #else size_t page_size = getpagesize(); #endif // Determine how much memory and how many pages are required. The worst // case is that we'll need to create a new part, so allocate enough // memory for that case. size_t needed_expansion = sizeof(struct heap_part) + requested_expansion + sizeof(struct heap_part_post); size_t needed_expansion_pages = needed_expansion / page_size + (needed_expansion % page_size ? 1 : 0); // Allocate at least a few pages to prevent inefficient allocations. size_t minimum_part_pages = 0; if ( __heap_state.current_part ) minimum_part_pages = __heap_state.current_part->part_size / page_size; if ( 256 < minimum_part_pages ) minimum_part_pages = 256; if ( minimum_part_pages < 4 ) minimum_part_pages = 4; if ( needed_expansion_pages < minimum_part_pages ) needed_expansion_pages = minimum_part_pages; // Calculate exactly how many bytes are added to the heap. size_t num_bytes = needed_expansion_pages * page_size; #ifdef __is_sortix_libk void* mapping = libk_heap_expand(&num_bytes); if ( !mapping ) return false; bool ideal_allocation = true; #else // Decide where we'd like to allocation memory. Ideally, we'd like to extend // an existing part, but if there is none, then simply somewhere near the // start of the address space (so it can grow upwards) will do. void* suggestion = __heap_state.current_part ? heap_part_end(__heap_state.current_part) : (void*) (0x1 * page_size); // Attempt to allocate memory for the new part. void* mapping = mmap(suggestion, num_bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // We are out of memory if we can't make a suitable allocation. if ( mapping == MAP_FAILED ) return false; bool ideal_allocation = mapping == suggestion; #endif // Extend the current part if we managed to do our ideal allocation. if ( __heap_state.current_part && ideal_allocation ) { struct heap_part* part = __heap_state.current_part; struct heap_part_post* old_post = heap_part_to_post(part); struct heap_part* part_prev = old_post->part_prev; uint8_t* new_chunk_begin = (uint8_t*) mapping - sizeof(struct heap_part_post); size_t new_chunk_size = num_bytes; // Remove the last chunk in the part from the heap if it is unused, we // will then simply include its bytes in our new chunk below. struct heap_chunk* last_chunk; if ( (last_chunk = heap_part_last_chunk(part)) && !heap_chunk_is_used(last_chunk) ) { heap_remove_chunk(last_chunk); new_chunk_begin = (uint8_t*) last_chunk; new_chunk_size += last_chunk->chunk_size; } // Extend the part by moving the end part structure further ahead. part->part_size += num_bytes; struct heap_part_post* post = heap_part_to_post(part); post->part_magic = HEAP_PART_MAGIC; post->part_prev = part_prev; post->part_size = part->part_size; post->unused[0] = 0; post->unused[1] = 0; post->unused[2] = 0; // Format a new heap chunk that contains the new space, possibly unified // with whatever unused space previously existed. assert(requested_expansion <= new_chunk_size); heap_insert_chunk(heap_chunk_format(new_chunk_begin, new_chunk_size)); return true; } // Format a new part in our new allocation. struct heap_part* part = (struct heap_part*) mapping; part->unused[0] = 0; part->unused[1] = 0; part->unused[2] = 0; part->part_size = num_bytes; part->part_magic = HEAP_PART_MAGIC; struct heap_part_post* post = heap_part_to_post(part); post->part_magic = HEAP_PART_MAGIC; post->part_size = part->part_size; post->unused[0] = 0; post->unused[1] = 0; post->unused[2] = 0; // Insert our part into the part linked list. if ( (part->part_next = __heap_state.current_part) ) heap_part_to_post(__heap_state.current_part)->part_prev = part; post->part_prev = NULL; // Insert a new chunk into the heap containing the unused space in the part. uint8_t* new_chunk_begin = (uint8_t*) mapping + sizeof(struct heap_part); size_t new_chunk_size = num_bytes - (sizeof(struct heap_part) + sizeof(struct heap_part_post)); assert(requested_expansion <= new_chunk_size); heap_insert_chunk(heap_chunk_format(new_chunk_begin, new_chunk_size)); __heap_state.current_part = part; return true; }