| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| #include <allocman/allocman.h> |
| #include <allocman/util.h> |
| #include <stdlib.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <sel4/sel4.h> |
| #include <vka/capops.h> |
| #include <sel4utils/util.h> |
| |
| static int _refill_watermark(allocman_t *alloc); |
| |
| static inline int _can_alloc(struct allocman_properties properties, size_t alloc_depth, size_t free_depth) |
| { |
| int in_alloc = alloc_depth > 0; |
| int in_free = free_depth > 0; |
| return (properties.alloc_can_alloc || !in_alloc) && (properties.free_can_alloc || !in_free); |
| } |
| |
| static inline int _can_free(struct allocman_properties properties, size_t alloc_depth, size_t free_depth) |
| { |
| int in_alloc = alloc_depth > 0; |
| int in_free = free_depth > 0; |
| return (properties.alloc_can_free || !in_alloc) && (properties.free_can_free || !in_free); |
| } |
| |
| /* Signals an operation is being started, and returns whether |
| this is the root operation, or a dependent call */ |
| static int _start_operation(allocman_t *alloc) |
| { |
| int ret = !alloc->in_operation; |
| alloc->in_operation = 1; |
| return ret; |
| } |
| |
| static inline void _end_operation(allocman_t *alloc, int root) |
| { |
| alloc->in_operation = !root; |
| /* Anytime we end an operation we need to make sure we have watermark |
| resources */ |
| if (root) { |
| _refill_watermark(alloc); |
| } |
| } |
| |
| static void allocman_mspace_queue_for_free(allocman_t *alloc, void *ptr, size_t bytes) { |
| if (alloc->num_freed_mspace_chunks == alloc->desired_freed_mspace_chunks) { |
| assert(!"Out of space to store free'd objects. Leaking memory"); |
| return; |
| } |
| alloc->freed_mspace_chunks[alloc->num_freed_mspace_chunks] = |
| (struct allocman_freed_mspace_chunk) {ptr, bytes}; |
| alloc->num_freed_mspace_chunks++; |
| } |
| |
| static void allocman_cspace_queue_for_free(allocman_t *alloc, const cspacepath_t *path) { |
| if (alloc->num_freed_slots == alloc->desired_freed_slots) { |
| assert(!"Out of space to store free'd objects. Leaking memory"); |
| return; |
| } |
| alloc->freed_slots[alloc->num_freed_slots] = *path; |
| alloc->num_freed_slots++; |
| } |
| |
| static void allocman_utspace_queue_for_free(allocman_t *alloc, seL4_Word cookie, size_t size_bits) { |
| if (alloc->num_freed_utspace_chunks == alloc->desired_freed_utspace_chunks) { |
| assert(!"Out of space to store free'd objects. Leaking memory"); |
| return; |
| } |
| alloc->freed_utspace_chunks[alloc->num_freed_utspace_chunks] = |
| (struct allocman_freed_utspace_chunk) {size_bits, cookie}; |
| alloc->num_freed_utspace_chunks++; |
| } |
| |
| /* this nasty macro prevents code duplication for the free functions. Unfortunately I can think of no other |
| * way of allowing the number of arguments to the 'free' function in the body to be parameterized */ |
| #define ALLOCMAN_FREE(alloc,space,...) do { \ |
| int root; \ |
| assert(alloc->have_##space); \ |
| if (!_can_free(alloc->space.properties, alloc->space##_alloc_depth, alloc->space##_free_depth)) { \ |
| allocman_##space##_queue_for_free(alloc, __VA_ARGS__); \ |
| return; \ |
| } \ |
| root = _start_operation(alloc); \ |
| alloc->space##_free_depth++; \ |
| alloc->space.free(alloc, alloc->space.space, __VA_ARGS__); \ |
| alloc->space##_free_depth--; \ |
| _end_operation(alloc, root); \ |
| } while(0) |
| |
| void allocman_cspace_free(allocman_t *alloc, const cspacepath_t *slot) |
| { |
| ALLOCMAN_FREE(alloc, cspace, slot); |
| } |
| |
| void allocman_mspace_free(allocman_t *alloc, void *ptr, size_t bytes) |
| { |
| ALLOCMAN_FREE(alloc, mspace, ptr, bytes); |
| } |
| |
| void allocman_utspace_free(allocman_t *alloc, seL4_Word cookie, size_t size_bits) |
| { |
| ALLOCMAN_FREE(alloc, utspace, cookie, size_bits); |
| } |
| |
| static void *_try_watermark_mspace(allocman_t *alloc, size_t size, int *_error) |
| { |
| size_t i; |
| for (i = 0; i < alloc->num_mspace_chunks; i++) { |
| if (alloc->mspace_chunk[i].size == size) { |
| if (alloc->mspace_chunk_count[i] > 0) { |
| void *ret = alloc->mspace_chunks[i][--alloc->mspace_chunk_count[i]]; |
| SET_ERROR(_error, 0); |
| alloc->used_watermark = 1; |
| return ret; |
| } |
| } |
| } |
| SET_ERROR(_error, 1); |
| return NULL; |
| } |
| |
| static int _try_watermark_cspace(allocman_t *alloc, cspacepath_t *slot) |
| { |
| if (alloc->num_cspace_slots == 0) { |
| return 1; |
| } |
| alloc->used_watermark = 1; |
| *slot = alloc->cspace_slots[--alloc->num_cspace_slots]; |
| return 0; |
| } |
| |
| static seL4_Word _try_watermark_utspace(allocman_t *alloc, size_t size_bits, seL4_Word type, const cspacepath_t *path, int *_error) |
| { |
| size_t i; |
| |
| for (i = 0; i < alloc->num_utspace_chunks; i++) { |
| if (alloc->utspace_chunk[i].size_bits == size_bits && alloc->utspace_chunk[i].type == type) { |
| if (alloc->utspace_chunk_count[i] > 0) { |
| struct allocman_utspace_allocation result = alloc->utspace_chunks[i][alloc->utspace_chunk_count[i] - 1]; |
| int error; |
| /* Need to perform a cap move */ |
| error = vka_cnode_move(path, &result.slot); |
| if (error != seL4_NoError) { |
| SET_ERROR(_error, 1); |
| return 0; |
| } |
| alloc->used_watermark = 1; |
| alloc->utspace_chunk_count[i]--; |
| allocman_cspace_free(alloc, &result.slot); |
| SET_ERROR(_error, 0); |
| return result.cookie; |
| } |
| } |
| } |
| SET_ERROR(_error, 1); |
| return 0; |
| } |
| |
| static void *_allocman_mspace_alloc(allocman_t *alloc, size_t size, int *_error, int use_watermark) |
| { |
| int root_op; |
| void *ret; |
| int error; |
| /* see if we have an allocator installed yet*/ |
| if (!alloc->have_mspace) { |
| SET_ERROR(_error, 1); |
| return 0; |
| } |
| /* Check that we are permitted to cspace_alloc here */ |
| if (!_can_alloc(alloc->mspace.properties, alloc->mspace_alloc_depth, alloc->mspace_free_depth)) { |
| if (use_watermark) { |
| ret = _try_watermark_mspace(alloc, size, _error); |
| if (!ret) { |
| ZF_LOGI("Failed to fullfill recursive allocation from watermark, size %zu\n", size); |
| } |
| return ret; |
| } else { |
| SET_ERROR(_error, 1); |
| return 0; |
| } |
| } |
| root_op = _start_operation(alloc); |
| /* Attempt the allocation */ |
| alloc->mspace_alloc_depth++; |
| ret = alloc->mspace.alloc(alloc, alloc->mspace.mspace, size, &error); |
| alloc->mspace_alloc_depth--; |
| if (!error) { |
| _end_operation(alloc, root_op); |
| SET_ERROR(_error, 0); |
| return ret; |
| } |
| /* We encountered some fail. We will try and allocate from the watermark pool. |
| Does not matter what the error or outcome is, just propogate back up*/ |
| if (use_watermark) { |
| ret = _try_watermark_mspace(alloc, size, _error); |
| if (!ret) { |
| ZF_LOGI("Regular mspace alloc failed, and watermark also failed. for size %zu\n", size); |
| } |
| _end_operation(alloc, root_op); |
| return ret; |
| } else { |
| _end_operation(alloc, root_op); |
| SET_ERROR(_error, 1); |
| return NULL; |
| } |
| } |
| |
| static int _allocman_cspace_alloc(allocman_t *alloc, cspacepath_t *slot, int use_watermark) |
| { |
| int root_op; |
| int error; |
| /* see if we have an allocator installed yet*/ |
| if (!alloc->have_cspace) { |
| return 1; |
| } |
| /* Check that we are permitted to cspace_alloc here */ |
| if (!_can_alloc(alloc->cspace.properties, alloc->cspace_alloc_depth, alloc->cspace_free_depth)) { |
| if (use_watermark) { |
| int ret = _try_watermark_cspace(alloc, slot); |
| if (ret) { |
| ZF_LOGI("Failed to allocate cslot from watermark\n"); |
| } |
| return ret; |
| } else { |
| return 1; |
| } |
| } |
| root_op = _start_operation(alloc); |
| /* Attempt the allocation */ |
| alloc->cspace_alloc_depth++; |
| error = alloc->cspace.alloc(alloc, alloc->cspace.cspace, slot); |
| alloc->cspace_alloc_depth--; |
| if (!error) { |
| _end_operation(alloc, root_op); |
| return 0; |
| } |
| /* We encountered some fail. We will try and allocate from the watermark pool. |
| Does not matter what the error or outcome is, just propogate back up*/ |
| if (use_watermark) { |
| error = _try_watermark_cspace(alloc, slot); |
| if (error) { |
| ZF_LOGI("Regular cspace alloc failed, and failed from watermark\n"); |
| } |
| _end_operation(alloc, root_op); |
| return error; |
| } else { |
| _end_operation(alloc, root_op); |
| return 1; |
| } |
| } |
| |
| static seL4_Word _allocman_utspace_alloc(allocman_t *alloc, size_t size_bits, seL4_Word type, const cspacepath_t *path, uintptr_t paddr, bool canBeDev, int *_error, int use_watermark) |
| { |
| int root_op; |
| int error; |
| seL4_Word ret; |
| /* see if we have an allocator installed yet*/ |
| if (!alloc->have_utspace) { |
| SET_ERROR(_error,1); |
| return 0; |
| } |
| /* Check that we are permitted to utspace_alloc here */ |
| if (!_can_alloc(alloc->utspace.properties, alloc->utspace_alloc_depth, alloc->utspace_free_depth)) { |
| if (use_watermark && paddr == ALLOCMAN_NO_PADDR) { |
| ret = _try_watermark_utspace(alloc, size_bits, type, path, _error); |
| if (ret == 0) { |
| ZF_LOGI("Failed to allocate utspace from watermark. size %zu type %ld\n", size_bits, (long)type); |
| } |
| return ret; |
| } else { |
| SET_ERROR(_error, 1); |
| return 0; |
| } |
| } |
| root_op = _start_operation(alloc); |
| /* Attempt the allocation */ |
| alloc->utspace_alloc_depth++; |
| ret = alloc->utspace.alloc(alloc, alloc->utspace.utspace, size_bits, type, path, paddr, canBeDev, &error); |
| alloc->utspace_alloc_depth--; |
| if (!error) { |
| _end_operation(alloc, root_op); |
| SET_ERROR(_error, error); |
| return ret; |
| } |
| /* We encountered some fail. We will try and allocate from the watermark pool. |
| Does not matter what the error or outcome is, just propogate back up*/ |
| if (use_watermark && paddr == ALLOCMAN_NO_PADDR) { |
| ret = _try_watermark_utspace(alloc, size_bits, type, path, _error); |
| _end_operation(alloc, root_op); |
| if (ret == 0) { |
| ZF_LOGI("Regular utspace alloc failed and not watermark for size %zu type %ld\n", size_bits, (long)type); |
| } |
| return ret; |
| } else { |
| _end_operation(alloc, root_op); |
| SET_ERROR(_error, 1); |
| return 0; |
| } |
| } |
| |
| void *allocman_mspace_alloc(allocman_t *alloc, size_t size, int *_error) |
| { |
| return _allocman_mspace_alloc(alloc, size, _error, 1); |
| } |
| |
| int allocman_cspace_alloc(allocman_t *alloc, cspacepath_t *slot) |
| { |
| return _allocman_cspace_alloc(alloc, slot, 1); |
| } |
| |
| seL4_Word allocman_utspace_alloc_at(allocman_t *alloc, size_t size_bits, seL4_Word type, const cspacepath_t *path, uintptr_t paddr, bool canBeDev, int *_error) |
| { |
| return _allocman_utspace_alloc(alloc, size_bits, type, path, paddr, canBeDev, _error, 1); |
| } |
| |
| static int _refill_watermark(allocman_t *alloc) |
| { |
| int found_empty_pool; |
| int did_allocation; |
| size_t i; |
| if (alloc->refilling_watermark || !alloc->used_watermark) { |
| return 0; |
| } |
| alloc->refilling_watermark = 1; |
| |
| /* Run in a loop refilling our resources. We need a loop as refilling |
| one resource may require another watermark resource to be used. It is up |
| to the allocators to prove that this process results in a consistent |
| increase in the watermark pool, and hence will terminate. Need to be |
| very careful with re-entry in this loop, as our watermark resources |
| may change anytime we perform an allocation. We try and allocate evenly |
| across all the resources types since typically we are only refilling |
| a single object from each resource anyway, so the performance will be |
| the same, and if we aren't we are boot strapping and I'm not convinced |
| that all allocations orders are equivalent in this case */ |
| int limit = 0; |
| do { |
| found_empty_pool = 0; |
| did_allocation = 0; |
| while (alloc->num_freed_slots > 0) { |
| cspacepath_t slot = alloc->freed_slots[--alloc->num_freed_slots]; |
| allocman_cspace_free(alloc, &slot); |
| /* a free is like an allocation in that we have made some progress */ |
| did_allocation = 1; |
| } |
| while (alloc->num_freed_mspace_chunks > 0) { |
| struct allocman_freed_mspace_chunk chunk = alloc->freed_mspace_chunks[--alloc->num_freed_mspace_chunks]; |
| allocman_mspace_free(alloc, chunk.ptr, chunk.size); |
| did_allocation = 1; |
| } |
| while (alloc->num_freed_utspace_chunks > 0) { |
| struct allocman_freed_utspace_chunk chunk = alloc->freed_utspace_chunks[--alloc->num_freed_utspace_chunks]; |
| allocman_utspace_free(alloc, chunk.cookie, chunk.size_bits); |
| did_allocation = 1; |
| } |
| if (alloc->num_cspace_slots < alloc->desired_cspace_slots) { |
| int error; |
| found_empty_pool = 1; |
| cspacepath_t slot; |
| error = _allocman_cspace_alloc(alloc, &slot, 0); |
| if (!error) { |
| alloc->cspace_slots[alloc->num_cspace_slots++] = slot; |
| did_allocation = 1; |
| } |
| } |
| for (i = 0; i < alloc->num_utspace_chunks; i++) { |
| if (alloc->utspace_chunk_count[i] < alloc->utspace_chunk[i].count) { |
| cspacepath_t slot; |
| seL4_Word cookie; |
| int error; |
| /* First grab a slot */ |
| found_empty_pool = 1; |
| error = allocman_cspace_alloc(alloc, &slot); |
| if (!error) { |
| /* Now try to allocate */ |
| cookie = _allocman_utspace_alloc(alloc, alloc->utspace_chunk[i].size_bits, alloc->utspace_chunk[i].type, &slot, ALLOCMAN_NO_PADDR, false, &error, 0); |
| if (!error) { |
| alloc->utspace_chunks[i][alloc->utspace_chunk_count[i]].cookie = cookie; |
| alloc->utspace_chunks[i][alloc->utspace_chunk_count[i]].slot = slot; |
| alloc->utspace_chunk_count[i]++; |
| did_allocation = 1; |
| } else { |
| /* Give the slot back */ |
| allocman_cspace_free(alloc, &slot); |
| } |
| } |
| } |
| } |
| for (i = 0 ; i < alloc->num_mspace_chunks; i++) { |
| if (alloc->mspace_chunk_count[i] < alloc->mspace_chunk[i].count) { |
| void *result; |
| int error; |
| found_empty_pool = 1; |
| result = _allocman_mspace_alloc(alloc, alloc->mspace_chunk[i].size, &error, 0); |
| if (!error) { |
| alloc->mspace_chunks[i][alloc->mspace_chunk_count[i]++] = result; |
| did_allocation = 1; |
| } |
| } |
| } |
| limit++; |
| } while (found_empty_pool && did_allocation && limit < 4); |
| |
| alloc->refilling_watermark = 0; |
| if (!found_empty_pool) { |
| alloc->used_watermark = 0; |
| } |
| return found_empty_pool; |
| } |
| |
| int allocman_create(allocman_t *alloc, struct mspace_interface mspace) { |
| /* zero out the struct */ |
| memset(alloc, 0, sizeof(allocman_t)); |
| |
| alloc->mspace = mspace; |
| alloc->have_mspace = 1; |
| |
| return 0; |
| } |
| |
| int allocman_fill_reserves(allocman_t *alloc) { |
| int full; |
| int root = _start_operation(alloc); |
| /* force the reserves to be checked */ |
| alloc->used_watermark = 1; |
| /* attempt to fill */ |
| full = _refill_watermark(alloc); |
| _end_operation(alloc, root); |
| return full; |
| } |
| |
| #define ALLOCMAN_ATTACH(alloc, space, interface) do { \ |
| int root = _start_operation(alloc); \ |
| assert(root); \ |
| if (alloc->have_##space) { \ |
| /* an untyped allocator has already been attached, bail */ \ |
| LOG_ERROR("Alocate of type " #space " is already attached"); \ |
| return 1; \ |
| } \ |
| alloc->space = interface; \ |
| alloc->have_##space = 1; \ |
| _end_operation(alloc, root); \ |
| return 0; \ |
| }while(0) |
| |
| int allocman_attach_utspace(allocman_t *alloc, struct utspace_interface utspace) { |
| ALLOCMAN_ATTACH(alloc, utspace, utspace); |
| } |
| |
| int allocman_attach_cspace(allocman_t *alloc, struct cspace_interface cspace) { |
| ALLOCMAN_ATTACH(alloc, cspace, cspace); |
| } |
| |
| static int resize_array(allocman_t *alloc, size_t num, void **array, size_t *size, size_t *count, size_t item_size) { |
| int root = _start_operation(alloc); |
| void *new_array; |
| int error; |
| |
| assert(root); |
| |
| /* allocate new array */ |
| new_array = allocman_mspace_alloc(alloc, item_size * num, &error); |
| if (!!error) { |
| return error; |
| } |
| |
| /* if we have less than before. throw an error */ |
| while (num < (*count)) { |
| return -1; |
| } |
| |
| /* copy any existing slots and free the old array, but avoid using a null array */ |
| if ((*array)) { |
| memcpy(new_array, (*array), item_size * (*count)); |
| allocman_mspace_free(alloc, (*array), item_size * (*size)); |
| } |
| |
| /* switch the new array in */ |
| (*array) = new_array; |
| (*size) = num; |
| |
| alloc->used_watermark = 1; |
| _end_operation(alloc, root); |
| return error; |
| } |
| |
| static int resize_slots_array(allocman_t *alloc, size_t num, cspacepath_t **slots, size_t *size, size_t *count) { |
| return resize_array(alloc, num, (void**)slots, size, count, sizeof(cspacepath_t)); |
| } |
| |
| int allocman_configure_cspace_reserve(allocman_t *alloc, size_t num) { |
| return resize_slots_array(alloc, num, &alloc->cspace_slots, &alloc->desired_cspace_slots, &alloc->num_cspace_slots); |
| } |
| |
| int allocman_configure_max_freed_slots(allocman_t *alloc, size_t num) { |
| return resize_slots_array(alloc, num, &alloc->freed_slots, &alloc->desired_freed_slots, &alloc->num_freed_slots); |
| } |
| |
| int allocman_configure_max_freed_memory_chunks(allocman_t *alloc, size_t num) { |
| return resize_array(alloc, num, (void**)&alloc->freed_mspace_chunks, &alloc->desired_freed_mspace_chunks, &alloc->num_freed_mspace_chunks, sizeof(struct allocman_freed_mspace_chunk)); |
| } |
| |
| int allocman_configure_max_freed_untyped_chunks(allocman_t *alloc, size_t num) { |
| return resize_array(alloc, num, (void**)&alloc->freed_utspace_chunks, &alloc->desired_freed_utspace_chunks, &alloc->num_freed_utspace_chunks, sizeof(struct allocman_freed_utspace_chunk)); |
| } |
| |
| int allocman_configure_utspace_reserve(allocman_t *alloc, struct allocman_utspace_chunk chunk) { |
| int root = _start_operation(alloc); |
| size_t i; |
| struct allocman_utspace_chunk *new_chunk; |
| size_t *new_counts; |
| struct allocman_utspace_allocation **new_chunks; |
| struct allocman_utspace_allocation *new_alloc; |
| int error; |
| /* ensure this chunk hasn't already been added. would be nice to handle both decreasing and |
| * icnreasing reservations, but I cannot see the use case for that */ |
| for (i = 0; i < alloc->num_utspace_chunks; i++) { |
| if (alloc->utspace_chunk[i].size_bits == chunk.size_bits && alloc->utspace_chunk[i].type == chunk.type) { |
| return 1; |
| } |
| } |
| /* tack this chunk on */ |
| new_chunk = allocman_mspace_alloc(alloc, sizeof(struct allocman_utspace_chunk) * (alloc->num_utspace_chunks + 1), &error); |
| if (error) { |
| return error; |
| } |
| new_counts = allocman_mspace_alloc(alloc, sizeof(size_t) * (alloc->num_utspace_chunks + 1), &error); |
| if (error) { |
| allocman_mspace_free(alloc, new_chunk, sizeof(struct allocman_utspace_chunk) * (alloc->num_utspace_chunks + 1)); |
| return error; |
| } |
| new_chunks = allocman_mspace_alloc(alloc, sizeof(struct allocman_utspace_allocation *) * (alloc->num_utspace_chunks + 1), &error); |
| if (error) { |
| allocman_mspace_free(alloc, new_chunk, sizeof(struct allocman_utspace_chunk) * (alloc->num_utspace_chunks + 1)); |
| allocman_mspace_free(alloc, new_counts, sizeof(size_t) * (alloc->num_utspace_chunks + 1)); |
| return error; |
| } |
| new_alloc = allocman_mspace_alloc(alloc, sizeof(struct allocman_utspace_allocation) * chunk.count, &error); |
| if (error) { |
| allocman_mspace_free(alloc, new_chunk, sizeof(struct allocman_utspace_chunk) * (alloc->num_utspace_chunks + 1)); |
| allocman_mspace_free(alloc, new_counts, sizeof(size_t) * (alloc->num_utspace_chunks + 1)); |
| allocman_mspace_free(alloc, new_chunks, sizeof(struct allocman_utspace_allocation *) * (alloc->num_utspace_chunks + 1)); |
| return error; |
| } |
| if (alloc->num_utspace_chunks > 0) { |
| memcpy(new_chunk, alloc->utspace_chunk, sizeof(struct allocman_utspace_chunk) * alloc->num_utspace_chunks); |
| memcpy(new_counts, alloc->utspace_chunk_count, sizeof(size_t) * alloc->num_utspace_chunks); |
| memcpy(new_chunks, alloc->utspace_chunks, sizeof(struct allocman_utspace_allocation *) * alloc->num_utspace_chunks); |
| allocman_mspace_free(alloc, alloc->utspace_chunk, sizeof(struct allocman_utspace_chunk) * alloc->num_utspace_chunks); |
| allocman_mspace_free(alloc, alloc->utspace_chunk_count, sizeof(size_t) * alloc->num_utspace_chunks); |
| allocman_mspace_free(alloc, alloc->utspace_chunks, sizeof(struct allocman_utspace_allocation *) * alloc->num_utspace_chunks); |
| } |
| new_chunk[alloc->num_utspace_chunks] = chunk; |
| new_counts[alloc->num_utspace_chunks] = 0; |
| new_chunks[alloc->num_utspace_chunks] = new_alloc; |
| alloc->utspace_chunk = new_chunk; |
| alloc->utspace_chunk_count = new_counts; |
| alloc->utspace_chunks = new_chunks; |
| alloc->num_utspace_chunks++; |
| alloc->used_watermark = 1; |
| _end_operation(alloc, root); |
| return 0; |
| } |
| |
| int allocman_configure_mspace_reserve(allocman_t *alloc, struct allocman_mspace_chunk chunk) { |
| int root = _start_operation(alloc); |
| size_t i; |
| struct allocman_mspace_chunk *new_chunk; |
| size_t *new_counts; |
| void ***new_chunks; |
| void **new_alloc; |
| int error; |
| /* ensure this chunk hasn't already been added. would be nice to handle both decreasing and |
| * icnreasing reservations, but I cannot see the use case for that */ |
| for (i = 0; i < alloc->num_mspace_chunks; i++) { |
| if (alloc->mspace_chunk[i].size == chunk.size) { |
| return 1; |
| } |
| } |
| /* tack this chunk on */ |
| new_chunk = allocman_mspace_alloc(alloc, sizeof(struct allocman_mspace_chunk) * (alloc->num_mspace_chunks + 1), &error); |
| if (error) { |
| return error; |
| } |
| new_counts = allocman_mspace_alloc(alloc, sizeof(size_t) * (alloc->num_mspace_chunks + 1), &error); |
| if (error) { |
| allocman_mspace_free(alloc, new_chunk, sizeof(struct allocman_mspace_chunk) * (alloc->num_mspace_chunks + 1)); |
| return error; |
| } |
| new_chunks = allocman_mspace_alloc(alloc, sizeof(void **) * (alloc->num_mspace_chunks + 1), &error); |
| if (error) { |
| allocman_mspace_free(alloc, new_chunk, sizeof(struct allocman_mspace_chunk) * (alloc->num_mspace_chunks + 1)); |
| allocman_mspace_free(alloc, new_counts, sizeof(size_t) * (alloc->num_mspace_chunks + 1)); |
| return error; |
| } |
| new_alloc = allocman_mspace_alloc(alloc, sizeof(void *) * chunk.count, &error); |
| if (error) { |
| allocman_mspace_free(alloc, new_chunk, sizeof(struct allocman_mspace_chunk) * (alloc->num_mspace_chunks + 1)); |
| allocman_mspace_free(alloc, new_counts, sizeof(size_t) * (alloc->num_mspace_chunks + 1)); |
| allocman_mspace_free(alloc, new_chunks, sizeof(void **) * (alloc->num_mspace_chunks + 1)); |
| return error; |
| } |
| if (alloc->num_mspace_chunks > 0) { |
| memcpy(new_chunk, alloc->mspace_chunk, sizeof(struct allocman_mspace_chunk) * alloc->num_mspace_chunks); |
| memcpy(new_counts, alloc->mspace_chunk_count, sizeof(size_t) * alloc->num_mspace_chunks); |
| memcpy(new_chunks, alloc->mspace_chunks, sizeof(void **) * alloc->num_mspace_chunks); |
| allocman_mspace_free(alloc, alloc->mspace_chunk, sizeof(struct allocman_mspace_chunk) * alloc->num_mspace_chunks); |
| allocman_mspace_free(alloc, alloc->mspace_chunk_count, sizeof(size_t) * alloc->num_mspace_chunks); |
| allocman_mspace_free(alloc, alloc->mspace_chunks, sizeof(void **) * alloc->num_mspace_chunks); |
| } |
| new_chunk[alloc->num_mspace_chunks] = chunk; |
| new_counts[alloc->num_mspace_chunks] = 0; |
| new_chunks[alloc->num_mspace_chunks] = new_alloc; |
| alloc->mspace_chunk = new_chunk; |
| alloc->mspace_chunk_count = new_counts; |
| alloc->mspace_chunks = new_chunks; |
| alloc->num_mspace_chunks++; |
| alloc->used_watermark = 1; |
| _end_operation(alloc, root); |
| return 0; |
| } |
| |
| |
| int allocman_add_untypeds_from_timer_objects(allocman_t *alloc, timer_objects_t *to) { |
| int error = 0; |
| for (size_t i = 0; i < to->nobjs; i++) { |
| cspacepath_t path = allocman_cspace_make_path(alloc, to->objs[i].obj.cptr); |
| error = allocman_utspace_add_uts(alloc, 1, &path, &to->objs[i].obj.size_bits, |
| (uintptr_t *) &to->objs[i].region.base_addr, |
| ALLOCMAN_UT_DEV); |
| if (error) { |
| ZF_LOGE("Failed to add ut to allocman"); |
| return error; |
| } |
| } |
| return 0; |
| } |