aos/lib/aos/slot_alloc/twolevel_slot_alloc.c

275 lines
8.7 KiB
C

/**
* \file
* \brief Slot allocator for two level CSpace
*/
/*
* Copyright (c) 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 <aos/core_state.h>
#include "internal.h"
#include <stdlib.h>
/**
* \brief slot allocator
*
* \param ca Instance of the allocator
* \param ret Pointer to return the allocated slot
*/
errval_t two_level_alloc(struct slot_allocator *ca, struct capref *ret)
{
errval_t err = SYS_ERR_OK;
struct multi_slot_allocator *mca = (struct multi_slot_allocator*)ca;
thread_mutex_lock(&ca->mutex);
assert(ca->space != 0);
ca->space--;
/* Try allocating from the list of single slot allocators */
struct slot_allocator_list *walk = mca->head;
//struct slot_allocator_list *prev = NULL;
while(walk != NULL) {
err = walk->a.a.alloc(&walk->a.a, ret);
if (err_no(err) != LIB_ERR_SLOT_ALLOC_NO_SPACE) {
break;
}
//prev = walk;
walk = walk->next;
}
if (err_is_fail(err)) {
thread_mutex_unlock(&ca->mutex);
return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC);
}
/* If no more slots left, grow */
if (ca->space == 0) {
ca->space = ca->nslots;
/* Pull in the reserve */
mca->reserve->next = mca->head;
mca->head = mca->reserve;
/* Setup a new reserve */
// Cnode: in Root CN
struct capref cap;
struct cnoderef cnode;
thread_mutex_unlock(&ca->mutex);
// Do not call slot_alloc_root() here as we want control over refill.
struct slot_alloc_state *state = get_slot_alloc_state();
struct slot_allocator *rca = (struct slot_allocator *)(&state->rootca);
// Need to refill when one slot left, otherwise it's too late
size_t rootcn_free = single_slot_alloc_freecount(&state->rootca);
if (rootcn_free <= 2) {
// resize root slot allocator (and rootcn)
err = root_slot_allocator_refill(NULL, NULL);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_ROOTSA_RESIZE);
}
}
err = rca->alloc(rca, &cap);
if (err_is_fail(err)) {
DEBUG_ERR(err, "allocating root cnode slot failed");
return err_push(err, LIB_ERR_SLOT_ALLOC);
}
err = cnode_create_raw(cap, &cnode, ObjType_L2CNode, ca->nslots, NULL);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_CNODE_CREATE);
}
thread_mutex_lock(&ca->mutex);
// Buffers
void *buf = slab_alloc(&mca->slab);
if (!buf) { /* Grow slab */
// Allocate slot out of the list
mca->a.space--;
thread_mutex_unlock(&ca->mutex);
// get slot for frame for refilling slab allocator
struct capref frame;
err = mca->a.alloc(&mca->a, &frame);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SLOT_ALLOC);
}
// use slab refill function that never causes a pagefault
err = slab_refill_no_pagefault(&mca->slab, frame, SLAB_STATIC_SIZE(1, mca->slab.blocksize));
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SLAB_REFILL);
}
thread_mutex_lock(&ca->mutex);
// Try allocating again
buf = slab_alloc(&mca->slab);
if (!buf) {
thread_mutex_unlock(&ca->mutex);
return err_push(err, LIB_ERR_SLAB_ALLOC_FAIL);
}
}
mca->reserve = buf;
buf = (char *)buf + sizeof(struct slot_allocator_list);
size_t bufsize = mca->slab.blocksize - sizeof(struct slot_allocator_list);
// Allocator
err = single_slot_alloc_init_raw(&mca->reserve->a, cap, cnode,
mca->a.nslots, buf, bufsize);
if (err_is_fail(err)) {
thread_mutex_unlock(&ca->mutex);
return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC_INIT_RAW);
}
}
thread_mutex_unlock(&ca->mutex);
return SYS_ERR_OK;
}
/**
* \brief Free an allocated slot
*
* \param ca Instance of the allocator
* \param cap The slot to free
*
* Walks the list of single slot allocators trying to free the slot.
*/
errval_t two_level_free(struct slot_allocator *ca, struct capref cap)
{
errval_t err;
thread_mutex_lock(&ca->mutex);
struct multi_slot_allocator *mca = (struct multi_slot_allocator*)ca;
struct slot_allocator_list *walk = mca->head;
while(walk != NULL) {
err = walk->a.a.free(&walk->a.a, cap);
if (err_is_ok(err)) {
mca->a.space++;
}
if (err_no(err) != LIB_ERR_SLOT_ALLOC_WRONG_CNODE) {
thread_mutex_unlock(&ca->mutex);
return err;
}
walk = walk->next;
}
thread_mutex_unlock(&ca->mutex);
return LIB_ERR_SLOT_ALLOC_WRONG_CNODE;
}
/**
* \brief Initializer that does not allocate any space
*
* #slot_alloc_init duplicates some of the code below,
* modify it if making changes here.
*
* XXX: top_buf head_buf and reserve_buf each point to a separate buffer of
* size bufsize bytes which can be used for backing storage. bufsize evidently
* needs to be >= sizeof(struct cnode_meta) * nslots / 2. Don't ask me why! -AB
*/
errval_t two_level_slot_alloc_init_raw(struct multi_slot_allocator *ret,
struct capref initial_cap,
struct cnoderef initial_cnode,
struct capref reserve_cap,
struct cnoderef reserve_cnode,
void *head_buf, void *reserve_buf, size_t bufsize)
{
errval_t err;
/* Generic part */
ret->a.alloc = two_level_alloc;
ret->a.free = two_level_free;
ret->a.space = L2_CNODE_SLOTS;
ret->a.nslots = L2_CNODE_SLOTS;
thread_mutex_init(&ret->a.mutex);
// Top unused in two-level allocator
ret->head->next = NULL;
ret->reserve->next = NULL;
/* Head */
err = single_slot_alloc_init_raw(&ret->head->a, initial_cap,
initial_cnode, L2_CNODE_SLOTS, head_buf,
bufsize);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC_INIT);
}
/* Reserve */
err = single_slot_alloc_init_raw(&ret->reserve->a, reserve_cap,
reserve_cnode, L2_CNODE_SLOTS,
reserve_buf, bufsize);
if (err_is_fail(err)) {
return err_push(err, LIB_ERR_SINGLE_SLOT_ALLOC_INIT);
}
/* Slab */
size_t allocation_unit = sizeof(struct slot_allocator_list) +
SINGLE_SLOT_ALLOC_BUFLEN(L2_CNODE_SLOTS);
slab_init(&ret->slab, allocation_unit, NULL);
return SYS_ERR_OK;
}
/**
* \brief Initializer that uses memory
*/
errval_t two_level_slot_alloc_init(struct multi_slot_allocator *ret)
{
errval_t err;
size_t bufsize = SINGLE_SLOT_ALLOC_BUFLEN(L2_CNODE_SLOTS); // XXX?
ret->head = malloc(sizeof(struct slot_allocator_list));
if (!ret->head) {
return LIB_ERR_MALLOC_FAIL;
}
ret->head->next = NULL;
void *head_buf = malloc(bufsize);
if (!head_buf) {
free(ret->head);
return LIB_ERR_MALLOC_FAIL;
}
ret->reserve = malloc(sizeof(struct slot_allocator_list));
if (!ret->reserve) {
free(ret->head);
free(head_buf);
return LIB_ERR_MALLOC_FAIL;
}
void *reserve_buf = malloc(bufsize);
if (!reserve_buf) {
free(ret->head);
free(head_buf);
free(ret->reserve);
return LIB_ERR_MALLOC_FAIL;
}
struct capref initial_cap, reserve_cap;
struct cnoderef initial_cnode, reserve_cnode;
err = cnode_create_l2(&initial_cap, &initial_cnode);
if (err_is_fail(err)) {
free(ret->head);
free(head_buf);
free(ret->reserve);
free(reserve_buf);
return err_push(err, LIB_ERR_CNODE_CREATE);
}
err = cnode_create_l2(&reserve_cap, &reserve_cnode);
if (err_is_fail(err)) {
free(head_buf);
free(ret->head);
free(reserve_buf);
free(ret->reserve);
cap_destroy(initial_cap);
return err_push(err, LIB_ERR_CNODE_CREATE);
}
err = two_level_slot_alloc_init_raw(ret, initial_cap, initial_cnode,
reserve_cap, reserve_cnode, head_buf,
reserve_buf, bufsize);
return SYS_ERR_OK;
}