aos/lib/aos/slot_alloc/slot_alloc.c
Daniel Schwyn a7a01f8ea1 Bug fixes
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-04-04 15:16:17 +02:00

241 lines
7.5 KiB
C

/**
* \file
* \brief Slot allocator wrapper
*
* Warning: slot_alloc_init calls vregion_map which calls vspace_add_vregion.
* vspace_add_vregion uses malloc to increase it's slab.
* Since malloc depends upon slot_alloc_init being called successfully,
* vspace_add_vregion should have enough initial slab space to not use malloc.
*/
/*
* Copyright (c) 2010, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
*/
#include <aos/aos.h>
#include <aos/core_state.h>
#include <aos/caddr.h>
#include "internal.h"
/**
* \brief Returns the default slot allocator for the caller
*/
struct slot_allocator *get_default_slot_allocator(void)
{
struct slot_alloc_state *state = get_slot_alloc_state();
return (struct slot_allocator*)(&state->defca);
}
/**
* \brief Default slot allocator
*
* \param ret Pointer to the cap to return the allocated slot in
*
* Allocates one slot from the default allocator
*/
errval_t slot_alloc(struct capref *ret)
{
struct slot_allocator *ca = get_default_slot_allocator();
return ca->alloc(ca, ret);
}
/**
* \brief slot allocator for the root
*
* \param ret Pointer to the cap to return the allocated slot in
*
* Allocates one slot from the root slot allocator
*/
errval_t slot_alloc_root(struct capref *ret)
{
errval_t err;
struct slot_alloc_state *state = get_slot_alloc_state();
size_t rootcn_free = single_slot_alloc_freecount(&state->rootca);
// If there's two root cnode slots left, we need to trigger refill as
// the multi slot allocator might need a root cnode slot as well if it's
// just about to run out of slots and root_slot_allocator_refill calls
// into slot_alloc().
//debug_printf("Slot refilling %d \n", state->rootca.refilling);
if ((rootcn_free == 2) && !state->rootca.refilling) {
state->rootca.refilling = true;
err = root_slot_allocator_refill(NULL, NULL);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_ROOTSA_RESIZE);
}
state->rootca.refilling = false;
}
// If we got below two here somehow it's game over
assert(rootcn_free > 2);
struct slot_allocator *ca = (struct slot_allocator*)(&state->rootca);
return ca->alloc(ca, ret);
}
typedef errval_t (*cn_ram_alloc_func_t)(void *st, size_t reqbytes, struct capref *ret);
static errval_t rootcn_alloc(void *st, size_t reqbytes, struct capref *ret)
{
return ram_alloc(ret, reqbytes);
}
errval_t root_slot_allocator_refill(cn_ram_alloc_func_t myalloc, void *allocst)
{
errval_t err;
struct slot_alloc_state *state = get_slot_alloc_state();
struct single_slot_allocator *sca = &state->rootca;
cslot_t nslots = sca->a.nslots;
assert(nslots >= L2_CNODE_SLOTS);
// Double size of root cnode
struct capref root_ram, newroot_cap;
if (myalloc == NULL) {
// Fall back to plain ram_alloc if caller has not provided allocator
// function.
myalloc = rootcn_alloc;
}
err = myalloc(allocst, nslots * 2 * OBJSIZE_CTE, &root_ram);
if (err_is_fail(err)) {
return err_push(err, MM_ERR_SLOT_MM_ALLOC);
}
err = slot_alloc(&newroot_cap);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SLOT_ALLOC);
}
err = cnode_create_from_mem(newroot_cap, root_ram, ObjType_L1CNode,
NULL, nslots * 2);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_CNODE_CREATE_FROM_MEM);
}
// Delete RAM cap of new CNode
err = cap_delete(root_ram);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_CAP_DELETE);
}
// Resize rootcn
err = root_cnode_resize(newroot_cap, root_ram);
if (err_is_fail(err)) {
DEBUG_ERR(err, "resizing root cnode");
return err;
}
// Delete old Root CNode and free slot
err = cap_destroy(root_ram);
if (err_is_fail(err)) {
DEBUG_ERR(err, "deleting old root cnode");
return err_push(err, LIB_ERR_CAP_DESTROY);
}
// update root slot allocator size and our metadata
return single_slot_alloc_resize(sca, nslots * 2);
}
/**
* \brief Default slot free
*
* \param ret The cap to free
*
* Frees the passed in slot.
*
* \bug During dispatcher initialization and special domains like
* init and mem_serv free slots which
* are not allocated by the default allocator.
* This function detects such cases and ignores the errors.
* It maybe ignoring errors that must be caught.
*/
errval_t slot_free(struct capref ret)
{
struct slot_alloc_state *state = get_slot_alloc_state();
if (cnodecmp(ret.cnode, cnode_base)) { // Detect frees in basecn
return SYS_ERR_OK;
}
if (cnodecmp(ret.cnode, cnode_root)) {
struct slot_allocator *ca = (struct slot_allocator*)(&state->rootca);
return ca->free(ca, ret);
}
struct slot_allocator *ca = (struct slot_allocator*)(&state->defca);
errval_t err = ca->free(ca, ret);
// XXX: Detect frees in special case of init and mem_serv
if (err_no(err) == LIB_ERR_SLOT_ALLOC_WRONG_CNODE) {
return SYS_ERR_OK;
}
return err;
}
errval_t slot_alloc_init(void)
{
errval_t err;
struct slot_alloc_state *state = get_slot_alloc_state();
/* Default allocator */
// While initializing, other domains will call into it. Be careful
struct capref cap;
struct cnoderef cnode;
struct multi_slot_allocator *def = &state->defca;
// Generic
thread_mutex_init(&def->a.mutex);
def->a.alloc = two_level_alloc;
def->a.free = two_level_free;
def->a.space = SLOT_ALLOC_CNODE_SLOTS;
def->a.nslots = SLOT_ALLOC_CNODE_SLOTS;
def->head = &state->head;
def->head->next = NULL;
def->reserve = &state->reserve;
def->reserve->next = NULL;
// Head
cap.cnode = cnode_root;
cap.slot = ROOTCN_SLOT_SLOT_ALLOC1;
cnode = build_cnoderef(cap, CNODE_TYPE_OTHER);
err = single_slot_alloc_init_raw(&def->head->a, cap, cnode,
SLOT_ALLOC_CNODE_SLOTS, state->head_buf,
sizeof(state->head_buf));
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC_INIT_RAW);
}
// Reserve
cap.cnode = cnode_root;
cap.slot = ROOTCN_SLOT_SLOT_ALLOC2;
cnode = build_cnoderef(cap, CNODE_TYPE_OTHER);
err = single_slot_alloc_init_raw(&def->reserve->a, cap, cnode,
SLOT_ALLOC_CNODE_SLOTS, state->reserve_buf,
sizeof(state->reserve_buf));
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC_INIT_RAW);
}
// Slab
size_t allocation_unit = sizeof(struct slot_allocator_list) +
SINGLE_SLOT_ALLOC_BUFLEN(SLOT_ALLOC_CNODE_SLOTS);
slab_init(&def->slab, allocation_unit, NULL);
/* Root allocator */
err = single_slot_alloc_init_raw(&state->rootca, cap_root, cnode_root,
L2_CNODE_SLOTS, state->root_buf,
sizeof(state->root_buf));
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC_INIT_RAW);
}
state->rootca.a.space = L2_CNODE_SLOTS - ROOTCN_FREE_SLOTS;
state->rootca.head->space = L2_CNODE_SLOTS - ROOTCN_FREE_SLOTS;
state->rootca.head->slot = ROOTCN_FREE_SLOTS;
state->rootca.refilling = false;
return SYS_ERR_OK;
}