206 lines
5.8 KiB
C
206 lines
5.8 KiB
C
/**
|
|
* \file
|
|
* \brief Slot management for the memory allocator.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2007, 2008, 2016, 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, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
|
|
#include <aos/aos.h>
|
|
#include <mm/mm.h>
|
|
#include <mm/slot_alloc.h>
|
|
#include <stdio.h>
|
|
|
|
static errval_t rootcn_alloc(void *st, size_t reqsize, struct capref *ret)
|
|
{
|
|
return mm_alloc(st, reqsize, ret);
|
|
}
|
|
|
|
/// Allocate a new cnode if needed
|
|
errval_t slot_prealloc_refill(void *this)
|
|
{
|
|
struct slot_prealloc *sa = this;
|
|
uint8_t refill = !sa->current;
|
|
static bool is_refilling = false;
|
|
errval_t err = SYS_ERR_OK;
|
|
|
|
if (is_refilling) {
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
if (sa->meta[refill].free == L2_CNODE_SLOTS) {
|
|
return SYS_ERR_OK; // Nop
|
|
}
|
|
|
|
is_refilling = true;
|
|
|
|
// Allocate a ram cap
|
|
struct capref ram_cap;
|
|
err = mm_alloc(sa->mm, OBJSIZE_L2CNODE, &ram_cap);
|
|
if (err_is_fail(err)) {
|
|
is_refilling = false;
|
|
err = err_push(err, MM_ERR_SLOT_MM_ALLOC);
|
|
goto out;
|
|
}
|
|
|
|
// Retype to and build the next cnode
|
|
struct capref cnode_cap;
|
|
err = slot_alloc_root(&cnode_cap);
|
|
if (err_no(err) == LIB_ERR_SLOT_ALLOC_NO_SPACE) {
|
|
// resize root slot allocator (and rootcn)
|
|
err = root_slot_allocator_refill(rootcn_alloc, sa->mm);
|
|
if (err_is_fail(err)) {
|
|
err = err_push(err, LIB_ERR_ROOTSA_RESIZE);
|
|
goto out;
|
|
}
|
|
// retry slot_alloc_root
|
|
err = slot_alloc_root(&cnode_cap);
|
|
}
|
|
if (err_is_fail(err)) {
|
|
err = err_push(err, LIB_ERR_SLOT_ALLOC);
|
|
goto out;
|
|
}
|
|
|
|
err = cnode_create_from_mem(cnode_cap, ram_cap, ObjType_L2CNode,
|
|
&sa->meta[refill].cap.cnode, L2_CNODE_SLOTS);
|
|
if (err_is_fail(err)) {
|
|
err = err_push(err, LIB_ERR_CNODE_CREATE);
|
|
goto out;
|
|
}
|
|
|
|
// Set the metadata
|
|
sa->meta[refill].cap.slot = 0;
|
|
sa->meta[refill].free = L2_CNODE_SLOTS;
|
|
|
|
out:
|
|
is_refilling = false;
|
|
return err;
|
|
}
|
|
|
|
errval_t slot_alloc_prealloc(void *inst, uint64_t nslots, struct capref *ret)
|
|
{
|
|
struct slot_prealloc *this = inst;
|
|
assert(nslots < L2_CNODE_SLOTS);
|
|
|
|
/* Check if enough space */
|
|
if (this->meta[this->current].free < nslots) {
|
|
/*
|
|
debug_printf("slot_prealloc: switching cnodes %d->%d\n",
|
|
this->current, !this->current);
|
|
*/
|
|
// Allocate from next cnode
|
|
this->current = !this->current;
|
|
}
|
|
|
|
if (this->meta[this->current].free < nslots) {
|
|
return MM_ERR_SLOT_NOSLOTS;
|
|
}
|
|
|
|
/* Return next slot and update */
|
|
*ret = this->meta[this->current].cap;
|
|
this->meta[this->current].cap.slot += nslots;
|
|
this->meta[this->current].free -= nslots;
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Initialise preallocating slot allocator instance
|
|
*
|
|
* \param this Pointer to area for instance data
|
|
* \param initial_cnode First cap in an empty cnode to start allocating from
|
|
* \param initial_space Number of slots free in initial cnode
|
|
* \param ram_mm Memory allocator to use for RAM caps when creating new CNodes
|
|
*/
|
|
errval_t slot_prealloc_init(struct slot_prealloc *this,
|
|
struct capref initial_cnode,
|
|
uint64_t initial_space,
|
|
struct mm *ram_mm)
|
|
{
|
|
this->mm = ram_mm;
|
|
|
|
assert(initial_space == L2_CNODE_SLOTS);
|
|
if (initial_space != L2_CNODE_SLOTS) {
|
|
debug_printf("Initial CNode for 2 level preallocating slot allocator needs to be 16kB");
|
|
return LIB_ERR_SLOT_ALLOC_INIT;
|
|
}
|
|
|
|
this->current = 0;
|
|
this->meta[0].cap = initial_cnode;
|
|
this->meta[0].free = initial_space;
|
|
this->meta[1].free = 0;
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
errval_t slot_alloc_basecn_init(struct slot_alloc_basecn *this)
|
|
{
|
|
// Use ROOTCN_SLOT_SLOT_ALLOC0 as CNode fore basecn allocator
|
|
this->cap.cnode.croot = CPTR_ROOTCN;
|
|
this->cap.cnode.cnode = ROOTCN_SLOT_ADDR(ROOTCN_SLOT_SLOT_ALLOC0);
|
|
this->cap.cnode.level = CNODE_TYPE_OTHER;
|
|
this->cap.slot = 0;
|
|
this->free = L2_CNODE_SLOTS;
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
errval_t slot_alloc_basecn(void *inst, uint64_t nslots, struct capref *ret)
|
|
{
|
|
struct slot_alloc_basecn *this = inst;
|
|
errval_t err;
|
|
|
|
if (nslots > this->free) {
|
|
/* XXX: Special case for init, need to get memory from basecn */
|
|
struct capref ram;
|
|
size_t bits = 1 << (L2_CNODE_BITS + OBJBITS_CTE);
|
|
err = ram_alloc(&ram, bits);
|
|
if (err_is_fail(err)) {
|
|
DEBUG_ERR(err, "ram_alloc in slot_alloc_basecn cannot allocate L2 "
|
|
"CNode-sized ram cap");
|
|
return err_push(err, LIB_ERR_RAM_ALLOC);
|
|
}
|
|
|
|
/* to conform with 2 level cspace: put new cnode into rootcn */
|
|
struct capref cnode;
|
|
err = slot_alloc_root(&cnode);
|
|
if (err_is_fail(err)) {
|
|
DEBUG_ERR(err, "allocating root cnode slot");
|
|
return err_push(err, LIB_ERR_SLOT_ALLOC);
|
|
}
|
|
|
|
err = cnode_create_from_mem(cnode, ram, ObjType_L2CNode,
|
|
&this->cap.cnode, L2_CNODE_SLOTS);
|
|
if (err_is_fail(err)) {
|
|
return err_push(err, LIB_ERR_CNODE_CREATE);
|
|
}
|
|
|
|
this->cap.slot = 0;
|
|
this->free = L2_CNODE_SLOTS;
|
|
}
|
|
|
|
assert(nslots <= this->free);
|
|
*ret = this->cap;
|
|
this->cap.slot += nslots;
|
|
this->free -= nslots;
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/// Requires an instance of range_slot_allocator
|
|
errval_t slot_alloc_dynamic(void *alloc, uint64_t nslots, struct capref *ret)
|
|
{
|
|
return range_slot_alloc(alloc, nslots, ret);
|
|
}
|
|
|
|
errval_t slot_refill_dynamic(void *alloc)
|
|
{
|
|
return range_slot_alloc_refill(alloc, L2_CNODE_SLOTS);
|
|
}
|