blob: 605679fdf9db3455aef8e702678b0dad668ba6b7 [file] [log] [blame]
/*
* 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;
}