| // Copyright 2018 The Android Open Source Project |
| // |
| // 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. |
| #pragma once |
| |
| #include "android/utils/compiler.h" |
| #include <assert.h> |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| ANDROID_BEGIN_HEADER |
| |
| #ifdef ADDRESS_SPACE_NAMESPACE |
| namespace ADDRESS_SPACE_NAMESPACE { |
| #endif |
| |
| // This is ported from goldfish_address_space, allowing it to be used for |
| // general sub-allocations of any buffer range. |
| // It is also a pure header library, so there are no compiler tricks needed |
| // to use this in a particular implementation. please don't include this |
| // in a file that is included everywhere else, though. |
| |
| /* Represents a continuous range of addresses and a flag if this block is |
| * available |
| */ |
| struct address_block { |
| uint64_t offset; |
| union { |
| uint64_t size_available; /* VMSTATE_x does not support bit fields */ |
| struct { |
| uint64_t size : 63; |
| uint64_t available : 1; |
| }; |
| }; |
| }; |
| |
| /* A dynamic array of address blocks, with the following invariant: |
| * blocks[i].size > 0 |
| * blocks[i+1].offset = blocks[i].offset + blocks[i].size |
| */ |
| struct address_space_allocator { |
| struct address_block *blocks; |
| int size; |
| int capacity; |
| uint64_t total_bytes; |
| }; |
| |
| #define ANDROID_EMU_ADDRESS_SPACE_BAD_OFFSET (~(uint64_t)0) |
| #define ANDROID_EMU_ADDRESS_SPACE_DEFAULT_PAGE_SIZE 4096 |
| |
| /* The assert function to abort if something goes wrong. */ |
| static void address_space_assert(bool condition) { |
| #ifdef ANDROID_EMU_ADDRESS_SPACE_ASSERT_FUNC |
| ANDROID_EMU_ADDRESS_SPACE_ASSERT_FUNC(condition); |
| #else |
| assert(condition); |
| #endif |
| } |
| |
| static void* address_space_malloc0(size_t size) { |
| #ifdef ANDROID_EMU_ADDRESS_SPACE_MALLOC0_FUNC |
| return ANDROID_EMU_ADDRESS_SPACE_MALLOC0_FUNC(size); |
| #else |
| void* res = malloc(size); |
| memset(res, 0, size); |
| return res; |
| #endif |
| } |
| |
| static void* address_space_realloc(void* ptr, size_t size) { |
| #ifdef ANDROID_EMU_ADDRESS_SPACE_REALLOC_FUNC |
| return ANDROID_EMU_ADDRESS_SPACE_REALLOC_FUNC(ptr, size); |
| #else |
| void* res = realloc(ptr, size); |
| return res; |
| #endif |
| } |
| |
| static void address_space_free(void* ptr) { |
| #ifdef ANDROID_EMU_ADDRESS_SPACE_FREE_FUNC |
| return ANDROID_EMU_ADDRESS_SPACE_FREE_FUNC(ptr); |
| #else |
| free(ptr); |
| #endif |
| } |
| |
| /* Looks for the smallest (to reduce fragmentation) available block with size to |
| * fit the requested amount and returns its index or -1 if none is available. |
| */ |
| static int address_space_allocator_find_available_block( |
| struct address_block *block, |
| int n_blocks, |
| uint64_t size_at_least) |
| { |
| int index = -1; |
| uint64_t size_at_index = 0; |
| int i; |
| |
| address_space_assert(n_blocks >= 1); |
| |
| for (i = 0; i < n_blocks; ++i, ++block) { |
| uint64_t this_size = block->size; |
| address_space_assert(this_size > 0); |
| |
| if (this_size >= size_at_least && block->available && |
| (index < 0 || this_size < size_at_index)) { |
| index = i; |
| size_at_index = this_size; |
| } |
| } |
| |
| return index; |
| } |
| |
| static int |
| address_space_allocator_grow_capacity(int old_capacity) { |
| address_space_assert(old_capacity >= 1); |
| |
| return old_capacity + old_capacity; |
| } |
| |
| /* Inserts one more address block right after i'th (by borrowing i'th size) and |
| * adjusts sizes: |
| * pre: |
| * size > blocks[i].size |
| * |
| * post: |
| * * might reallocate allocator->blocks if there is no capacity to insert one |
| * * blocks[i].size -= size; |
| * * blocks[i+1].size = size; |
| */ |
| static struct address_block* |
| address_space_allocator_split_block( |
| struct address_space_allocator *allocator, |
| int i, |
| uint64_t size) |
| { |
| address_space_assert(allocator->capacity >= 1); |
| address_space_assert(allocator->size >= 1); |
| address_space_assert(allocator->size <= allocator->capacity); |
| address_space_assert(i >= 0); |
| address_space_assert(i < allocator->size); |
| address_space_assert(size < allocator->blocks[i].size); |
| |
| if (allocator->size == allocator->capacity) { |
| int new_capacity = address_space_allocator_grow_capacity(allocator->capacity); |
| allocator->blocks = |
| (struct address_block*) |
| address_space_realloc( |
| allocator->blocks, |
| sizeof(struct address_block) * new_capacity); |
| address_space_assert(allocator->blocks); |
| allocator->capacity = new_capacity; |
| } |
| |
| struct address_block *blocks = allocator->blocks; |
| |
| /* size = 5, i = 1 |
| * [ 0 | 1 | 2 | 3 | 4 ] => [ 0 | 1 | new | 2 | 3 | 4 ] |
| * i (i+1) (i+2) i (i+1) (i+2) |
| */ |
| memmove(&blocks[i + 2], &blocks[i + 1], |
| sizeof(struct address_block) * (allocator->size - i - 1)); |
| |
| struct address_block *to_borrow_from = &blocks[i]; |
| struct address_block *new_block = to_borrow_from + 1; |
| |
| uint64_t new_size = to_borrow_from->size - size; |
| |
| to_borrow_from->size = new_size; |
| |
| new_block->offset = to_borrow_from->offset + new_size; |
| new_block->size = size; |
| new_block->available = 1; |
| |
| ++allocator->size; |
| |
| return new_block; |
| } |
| |
| /* Marks i'th block as available. If adjacent ((i-1) and (i+1)) blocks are also |
| * available, it merges i'th block with them. |
| * pre: |
| * i < allocator->size |
| * post: |
| * i'th block is merged with adjacent ones if they are available, blocks that |
| * were merged from are removed. allocator->size is updated if blocks were |
| * removed. |
| */ |
| static void address_space_allocator_release_block( |
| struct address_space_allocator *allocator, |
| int i) |
| { |
| struct address_block *blocks = allocator->blocks; |
| int before = i - 1; |
| int after = i + 1; |
| int size = allocator->size; |
| |
| address_space_assert(i >= 0); |
| address_space_assert(i < size); |
| |
| blocks[i].available = 1; |
| |
| if (before >= 0 && blocks[before].available) { |
| if (after < size && blocks[after].available) { |
| // merge (before, i, after) into before |
| blocks[before].size += (blocks[i].size + blocks[after].size); |
| |
| size -= 2; |
| memmove(&blocks[i], &blocks[i + 2], |
| sizeof(struct address_block) * (size - i)); |
| allocator->size = size; |
| } else { |
| // merge (before, i) into before |
| blocks[before].size += blocks[i].size; |
| |
| --size; |
| memmove(&blocks[i], &blocks[i + 1], |
| sizeof(struct address_block) * (size - i)); |
| allocator->size = size; |
| } |
| } else if (after < size && blocks[after].available) { |
| // merge (i, after) into i |
| blocks[i].size += blocks[after].size; |
| |
| --size; |
| memmove(&blocks[after], &blocks[after + 1], |
| sizeof(struct address_block) * (size - after)); |
| allocator->size = size; |
| } |
| |
| } |
| |
| /* Takes a size to allocate an address block and returns an offset where this |
| * block is allocated. This block will not be available for other callers unless |
| * it is explicitly deallocated (see address_space_allocator_deallocate below). |
| */ |
| static uint64_t address_space_allocator_allocate( |
| struct address_space_allocator *allocator, |
| uint64_t size) |
| { |
| int i = address_space_allocator_find_available_block(allocator->blocks, |
| allocator->size, |
| size); |
| if (i < 0) { |
| return ANDROID_EMU_ADDRESS_SPACE_BAD_OFFSET; |
| } else { |
| address_space_assert(i < allocator->size); |
| |
| struct address_block *block = &allocator->blocks[i]; |
| address_space_assert(block->size >= size); |
| |
| if (block->size > size) { |
| block = address_space_allocator_split_block(allocator, i, size); |
| } |
| |
| address_space_assert(block->size == size); |
| block->available = 0; |
| |
| return block->offset; |
| } |
| } |
| |
| /* Takes an offset returned from address_space_allocator_allocate ealier |
| * (see above) and marks this block as available for further allocation. |
| */ |
| static uint32_t address_space_allocator_deallocate( |
| struct address_space_allocator *allocator, |
| uint64_t offset) |
| { |
| struct address_block *block = allocator->blocks; |
| int size = allocator->size; |
| int i; |
| |
| address_space_assert(size >= 1); |
| |
| for (i = 0; i < size; ++i, ++block) { |
| if (block->offset == offset) { |
| if (block->available) { |
| return EINVAL; |
| } else { |
| address_space_allocator_release_block(allocator, i); |
| return 0; |
| } |
| } |
| } |
| |
| return EINVAL; |
| } |
| |
| /* Creates a seed block. */ |
| static void address_space_allocator_init( |
| struct address_space_allocator *allocator, |
| uint64_t size, |
| int initial_capacity) |
| { |
| address_space_assert(initial_capacity >= 1); |
| |
| allocator->blocks = |
| (struct address_block*) |
| malloc(sizeof(struct address_block) * initial_capacity); |
| memset(allocator->blocks, 0, sizeof(struct address_block) * initial_capacity); |
| address_space_assert(allocator->blocks); |
| |
| struct address_block *block = allocator->blocks; |
| |
| block->offset = 0; |
| block->size = size; |
| block->available = 1; |
| |
| allocator->size = 1; |
| allocator->capacity = initial_capacity; |
| allocator->total_bytes = size; |
| } |
| |
| /* At this point there should be no used blocks and all available blocks must |
| * have been merged into one block. |
| */ |
| static void address_space_allocator_destroy( |
| struct address_space_allocator *allocator) |
| { |
| address_space_assert(allocator->size == 1); |
| address_space_assert(allocator->capacity >= allocator->size); |
| address_space_assert(allocator->blocks[0].available); |
| address_space_free(allocator->blocks); |
| } |
| |
| /* Destroy function if we don't care what was previoulsy allocated. |
| * have been merged into one block. |
| */ |
| static void address_space_allocator_destroy_nocleanup( |
| struct address_space_allocator *allocator) |
| { |
| address_space_free(allocator->blocks); |
| } |
| |
| /* Resets the state of the allocator to the initial state without |
| * performing any dynamic memory management. */ |
| static void address_space_allocator_reset( |
| struct address_space_allocator *allocator) |
| { |
| address_space_assert(allocator->size >= 1); |
| |
| allocator->size = 1; |
| |
| struct address_block* block = allocator->blocks; |
| block->offset = 0; |
| block->size = allocator->total_bytes; |
| block->available = 1; |
| } |
| |
| typedef void (*address_block_iter_func_t)(void* context, struct address_block*); |
| typedef void (*address_space_allocator_iter_func_t)(void* context, struct address_space_allocator*); |
| |
| static void address_space_allocator_run( |
| struct address_space_allocator *allocator, |
| void* context, |
| address_space_allocator_iter_func_t allocator_func, |
| address_block_iter_func_t block_func) |
| { |
| struct address_block *block = 0; |
| int size; |
| int i; |
| |
| allocator_func(context, allocator); |
| |
| block = allocator->blocks; |
| size = allocator->size; |
| |
| address_space_assert(size >= 1); |
| |
| for (i = 0; i < size; ++i, ++block) { |
| block_func(context, block); |
| } |
| } |
| |
| #ifdef ADDRESS_SPACE_NAMESPACE |
| } |
| #endif |
| |
| ANDROID_END_HEADER |