463 lines
12 KiB
C
463 lines
12 KiB
C
/*
|
|
* 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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
|
|
#include <aos/aos.h>
|
|
#include "region_pool.h"
|
|
#include "dqi_debug.h"
|
|
|
|
#define INIT_POOL_SIZE 16
|
|
|
|
STATIC_ASSERT((INIT_POOL_SIZE & (INIT_POOL_SIZE - 1)) == 0, "must be a power of two");
|
|
|
|
struct region_pool {
|
|
|
|
// IDs are inserted and may have to increase size at some point
|
|
uint16_t size;
|
|
// number of regions in pool
|
|
uint16_t num_regions;
|
|
|
|
// random offset where regions ids start from
|
|
uint64_t region_offset;
|
|
|
|
// if we have to serach for a slot, need an offset
|
|
uint16_t last_offset;
|
|
|
|
//region_alloc
|
|
struct slab_allocator region_alloc;
|
|
|
|
// structure to store regions
|
|
struct region** pool;
|
|
};
|
|
|
|
|
|
#define INIT_SIZE 128
|
|
struct buffer {
|
|
bufferid_t id;
|
|
struct buffer* next;
|
|
};
|
|
|
|
struct region {
|
|
// ID of the region
|
|
regionid_t id;
|
|
// Base address of the region
|
|
lpaddr_t base_addr;
|
|
// Capability of the region
|
|
struct capref* cap;
|
|
// Lenght of the memory region
|
|
size_t len;
|
|
};
|
|
|
|
|
|
/**
|
|
* @brief initialized a region from which only fixed size buffers are used
|
|
*
|
|
* @param region Return pointer to the region
|
|
* @param region_id The ID of the region,
|
|
* @param cap Capability of the memory region
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
|
|
/*
|
|
static errval_t region_init(struct region* region,
|
|
regionid_t region_id,
|
|
lpaddr_t base_addr,
|
|
size_t len,
|
|
struct capref* cap)
|
|
{
|
|
if (region == NULL) {
|
|
return LIB_ERR_MALLOC_FAIL;
|
|
}
|
|
|
|
region->id = region_id;
|
|
region->cap = cap;
|
|
regoin->base_addr = base_addr;
|
|
regon->len = len;
|
|
|
|
DQI_DEBUG_REGION("Initialize Region size=%ld addr=%16lx\n",
|
|
region->len, region->base_addr);
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* @brief free up a region
|
|
*
|
|
* @param region The region to free up
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
/*
|
|
static errval_t region_destroy(struct region* region)
|
|
{
|
|
free(region);
|
|
return SYS_ERR_OK;
|
|
}
|
|
*/
|
|
|
|
|
|
/**
|
|
* @brief initialized a pool of regions
|
|
*
|
|
* @param pool Return pointer to the region pool
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
errval_t region_pool_init(struct region_pool** pool)
|
|
{
|
|
// Allocate pool struct itself including pointers to region
|
|
(*pool) = calloc(1, sizeof(struct region_pool));
|
|
if (*pool == NULL) {
|
|
DQI_DEBUG_REGION("Allocationg inital pool failed \n");
|
|
return LIB_ERR_MALLOC_FAIL;
|
|
}
|
|
|
|
(*pool)->num_regions = 0;
|
|
|
|
srand(rdtsc());
|
|
|
|
// Initialize region id offset
|
|
(*pool)->region_offset = (rand() >> 12) ;
|
|
(*pool)->size = INIT_POOL_SIZE;
|
|
|
|
(*pool)->pool = calloc(INIT_POOL_SIZE, sizeof(struct region*));
|
|
if ((*pool)->pool == NULL) {
|
|
free(*pool);
|
|
DQI_DEBUG_REGION("Allocationg inital pool failed \n");
|
|
return LIB_ERR_MALLOC_FAIL;
|
|
}
|
|
|
|
|
|
slab_init(&(*pool)->region_alloc, sizeof(struct region),
|
|
slab_default_refill);
|
|
|
|
DQI_DEBUG_REGION("Init region pool size=%d addr=%p\n", INIT_POOL_SIZE, *pool);
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
|
|
* @brief freeing region pool
|
|
*
|
|
* @param pool The region pool to free
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
errval_t region_pool_destroy(struct region_pool* pool)
|
|
{
|
|
errval_t err;
|
|
struct capref cap;
|
|
// Check if there are any regions left
|
|
if (pool->num_regions == 0) {
|
|
free(pool->pool);
|
|
free(pool);
|
|
return SYS_ERR_OK;
|
|
} else {
|
|
// There are regions left -> remove them
|
|
for (int i = 0; i < pool->size; i++) {
|
|
if ((void*) pool->pool[i] != NULL) {
|
|
err = region_pool_remove_region(pool, pool->pool[i]->id,
|
|
&cap);
|
|
if (err_is_fail(err)){
|
|
printf("Region pool has regions that are still used,"
|
|
" can not free them \n");
|
|
return err;
|
|
}
|
|
}
|
|
}
|
|
free(pool->pool);
|
|
free(pool);
|
|
}
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief increase the region pool size by a factor of 2
|
|
*
|
|
* @param pool the regin pool that has not enough region slots
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
|
|
|
|
static errval_t region_pool_grow(struct region_pool* pool)
|
|
{
|
|
struct region** tmp;
|
|
|
|
uint16_t new_size = (pool->size)*2;
|
|
// Allocate new pool twice the size
|
|
tmp = calloc(new_size, sizeof(struct region*));
|
|
if (tmp == NULL) {
|
|
DQI_DEBUG_REGION("Allocationg larger pool failed \n");
|
|
return LIB_ERR_MALLOC_FAIL;
|
|
}
|
|
|
|
// Copy all the pointers
|
|
for (int i = 0; i < new_size; i++) {
|
|
tmp[i] = NULL;
|
|
}
|
|
|
|
struct region* region;
|
|
for (int i = 0; i < pool->size; i++) {
|
|
region = pool->pool[i];
|
|
uint16_t index = region->id & (new_size - 1);
|
|
tmp[index] = pool->pool[i];
|
|
}
|
|
|
|
free(pool->pool);
|
|
|
|
pool->pool = tmp;
|
|
pool->size = new_size;
|
|
pool->last_offset = 0;
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief add a memory region to the region pool
|
|
*
|
|
* @param pool The pool to add the region to
|
|
* @param cap The cap of the region
|
|
* @param region_id Return pointer to the region id
|
|
* that is assigned by the pool
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
errval_t region_pool_add_region(struct region_pool* pool,
|
|
struct capref cap,
|
|
regionid_t* region_id)
|
|
{
|
|
errval_t err;
|
|
struct region* region;
|
|
struct frame_identity id;
|
|
|
|
err = frame_identify(cap, &id);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
// for now just loop over all entries
|
|
for (int i = 0; i < pool->size; i++) {
|
|
struct region* tmp;
|
|
tmp = pool->pool[i];
|
|
|
|
if (tmp == NULL) {
|
|
continue;
|
|
}
|
|
|
|
// check if region is already registered
|
|
if (tmp->base_addr == id.base) {
|
|
return DEVQ_ERR_INVALID_REGION_ARGS;
|
|
}
|
|
|
|
/* if region if entierly before other region or
|
|
entierly after region, otherwise there is an overlap
|
|
*/
|
|
if (!((id.base + id.bytes <= tmp->base_addr) ||
|
|
(tmp->base_addr + tmp->len <= id.base))) {
|
|
return DEVQ_ERR_INVALID_REGION_ARGS;
|
|
}
|
|
|
|
}
|
|
|
|
// Check if pool size is large enough
|
|
if (!(pool->num_regions < pool->size)) {
|
|
DQI_DEBUG_REGION("Increasing pool size to %d \n", pool->size*2);
|
|
err = region_pool_grow(pool);
|
|
if (err_is_fail(err)) {
|
|
DQI_DEBUG_REGION("Increasing pool size failed\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
pool->num_regions++;
|
|
uint16_t offset = pool->last_offset;
|
|
uint16_t index = 0;
|
|
|
|
// find slot
|
|
while (true) {
|
|
index = (pool->region_offset + pool->num_regions + offset) & (pool->size - 1);
|
|
DQI_DEBUG_REGION("Trying insert index %d \n", index);
|
|
if (pool->pool[index] == NULL) {
|
|
break;
|
|
} else {
|
|
offset++;
|
|
}
|
|
}
|
|
|
|
pool->last_offset = offset;
|
|
|
|
region = slab_alloc(&pool->region_alloc);
|
|
if (region == NULL) {
|
|
return LIB_ERR_MALLOC_FAIL;
|
|
}
|
|
|
|
region->id = pool->region_offset + pool->num_regions + offset;
|
|
region->cap = ∩
|
|
region->base_addr = id.base;
|
|
region->len = id.bytes;
|
|
|
|
// insert into pool
|
|
pool->pool[region->id & (pool->size - 1)] = region;
|
|
*region_id = region->id;
|
|
DQI_DEBUG_REGION("Inserting region into pool at %d \n", region->id % pool->size);
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief add a memory region to the region pool using an already
|
|
* existing id
|
|
*
|
|
* @param pool The pool to add the region to
|
|
* @param cap The cap of the region
|
|
* @param region_id The region id to add to the pool
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
errval_t region_pool_add_region_with_id(struct region_pool* pool,
|
|
struct capref cap,
|
|
regionid_t region_id)
|
|
{
|
|
errval_t err;
|
|
// Check if pool size is large enough
|
|
if (!(pool->num_regions < pool->size)) {
|
|
DQI_DEBUG_REGION("Increasing pool size to %d \n", pool->size*2);
|
|
err = region_pool_grow(pool);
|
|
if (err_is_fail(err)) {
|
|
DQI_DEBUG_REGION("Increasing pool size failed\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
struct region* region = pool->pool[region_id & (pool->size - 1)];
|
|
if (region != NULL) {
|
|
return DEVQ_ERR_INVALID_REGION_ID;
|
|
} else {
|
|
struct frame_identity id;
|
|
|
|
err = frame_identify(cap, &id);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
region = slab_alloc(&pool->region_alloc);
|
|
if (region == NULL) {
|
|
return LIB_ERR_MALLOC_FAIL;
|
|
}
|
|
|
|
region->id = region_id;
|
|
region->cap = ∩
|
|
region->base_addr = id.base;
|
|
region->len = id.bytes;
|
|
|
|
pool->pool[region_id & (pool->size - 1)] = region;
|
|
}
|
|
|
|
pool->num_regions++;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief remove a memory region from the region pool
|
|
*
|
|
* @param pool The pool to remove the region from
|
|
* @param region_id The id of the region to remove
|
|
* @param cap Return pointer to the cap of the removed region
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
errval_t region_pool_remove_region(struct region_pool* pool,
|
|
regionid_t region_id,
|
|
struct capref* cap)
|
|
{
|
|
//errval_t err;
|
|
struct region* region;
|
|
region = pool->pool[region_id & (pool->size - 1)];
|
|
if (region == NULL) {
|
|
return DEVQ_ERR_INVALID_REGION_ID;
|
|
}
|
|
|
|
cap = region->cap;
|
|
|
|
slab_free(&pool->region_alloc, region);
|
|
/*
|
|
err = region_destroy(region);
|
|
if (err_is_fail(err)) {
|
|
DQI_DEBUG_REGION("Failed to destroy region, some buffers might still be in use \n");
|
|
return err;
|
|
}
|
|
*/
|
|
DQI_DEBUG_REGION("Removing slot %d \n", region_id % pool->size);
|
|
pool->pool[region_id & (pool->size - 1)] = NULL;
|
|
|
|
pool->num_regions--;
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief get memory region from pool
|
|
*
|
|
* @param pool The pool to get the region from
|
|
* @param region_id The id of the region to get
|
|
* @param region Return pointer to the region
|
|
*
|
|
* @returns error on failure or SYS_ERR_OK on success
|
|
*/
|
|
/*
|
|
static errval_t region_pool_get_region(struct region_pool* pool,
|
|
regionid_t region_id,
|
|
struct region** region)
|
|
{
|
|
*region = pool->pool[region_id % pool->size];
|
|
if (*region == NULL) {
|
|
return DEVQ_ERR_INVALID_REGION_ID;
|
|
}
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* @brief check if buffer is valid
|
|
*
|
|
* @param pool The pool to get the region from
|
|
* @param region_id The id of the region
|
|
* @param offset offset into the region
|
|
* @param length length of the buffer
|
|
* @param valid_data offset into the buffer
|
|
* @param valid_length length of the valid_data
|
|
*
|
|
* @returns true if the buffer is valid otherwise false
|
|
*/
|
|
bool region_pool_buffer_check_bounds(struct region_pool* pool,
|
|
regionid_t region_id,
|
|
genoffset_t offset,
|
|
genoffset_t length,
|
|
genoffset_t valid_data,
|
|
genoffset_t valid_length)
|
|
{
|
|
struct region* region;
|
|
region = pool->pool[region_id & (pool->size - 1)];
|
|
if (region == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// check validity of buffer within region
|
|
// and check validity of valid data values
|
|
if ((length + offset > region->len) ||
|
|
(valid_data + valid_length > length)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|