mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-05 10:13:15 +00:00
302 lines
6.4 KiB
C++
302 lines
6.4 KiB
C++
/*
|
|
* \brief Memory subsystem
|
|
* \author Christian Helmuth
|
|
* \date 2008-08-15
|
|
*
|
|
* Open issues:
|
|
*
|
|
* - Rethink file split up
|
|
* - What about slabs for large malloc blocks?
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2008-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#include <base/env.h>
|
|
#include <base/lock.h>
|
|
#include <base/printf.h>
|
|
#include <base/slab.h>
|
|
#include <util/avl_tree.h>
|
|
#include <util/misc_math.h>
|
|
#include <dataspace/client.h>
|
|
|
|
#include <os/attached_ram_dataspace.h>
|
|
|
|
extern "C" {
|
|
#include <dde_kit/memory.h>
|
|
#include <dde_kit/pgtab.h>
|
|
}
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
/*****************************
|
|
** Backing store allocator **
|
|
*****************************/
|
|
|
|
/*
|
|
* The backing store allocator allocates RAM dataspaces and maintains virtual
|
|
* page-table entries.
|
|
*/
|
|
class Backing_store_allocator : public Allocator
|
|
{
|
|
private:
|
|
|
|
class Block : public Avl_node<Block>
|
|
{
|
|
private:
|
|
|
|
Attached_ram_dataspace *_ram_ds;
|
|
|
|
public:
|
|
|
|
Block(Attached_ram_dataspace *ram_ds) : _ram_ds(ram_ds) { }
|
|
|
|
Attached_ram_dataspace *ram_ds() const { return _ram_ds; }
|
|
|
|
/* AVL node comparison */
|
|
bool higher(Block *b) {
|
|
return _ram_ds->local_addr<void>() < b->_ram_ds->local_addr<void>(); }
|
|
|
|
/* AVL node lookup */
|
|
Block *lookup(void *virt)
|
|
{
|
|
if (virt == _ram_ds->local_addr<void>()) return this;
|
|
|
|
Block *b = child(_ram_ds->local_addr<void>() < virt);
|
|
return b ? b->lookup(virt) : 0;
|
|
}
|
|
};
|
|
|
|
Avl_tree<Block> _map; /* backing store block map */
|
|
size_t _consumed; /* allocated memory size */
|
|
|
|
Lock _lock; /* synchronize access to allocator */
|
|
|
|
public:
|
|
|
|
Backing_store_allocator() { }
|
|
virtual ~Backing_store_allocator() { }
|
|
|
|
/***************
|
|
** Allocator **
|
|
***************/
|
|
|
|
bool alloc(size_t size, void **out_addr)
|
|
{
|
|
Lock::Guard lock_guard(_lock);
|
|
|
|
Attached_ram_dataspace *ram_ds;
|
|
|
|
/* alloc and attach RAM dataspace */
|
|
try {
|
|
ram_ds = new (env()->heap()) Attached_ram_dataspace(env()->ram_session(), size);
|
|
} catch (...) {
|
|
PERR("RAM allocation failed (size=%zx)", size);
|
|
return false;
|
|
}
|
|
|
|
/* setup virt->phys and phys->virt mappings */
|
|
void *virt = ram_ds->local_addr<void>();
|
|
addr_t phys = Dataspace_client(ram_ds->cap()).phys_addr();
|
|
|
|
dde_kit_pgtab_set_region_with_size(virt, phys, size);
|
|
|
|
/* store allocation in our map */
|
|
Block *b = new (env()->heap()) Block(ram_ds);
|
|
|
|
/* cleanup on error */
|
|
if (!b) {
|
|
dde_kit_pgtab_clear_region(virt);
|
|
destroy(env()->heap(), ram_ds);
|
|
return false;
|
|
}
|
|
|
|
_map.insert(b);
|
|
_consumed += size;
|
|
|
|
*out_addr = virt;
|
|
return true;
|
|
}
|
|
|
|
void free(void *virt, size_t size)
|
|
{
|
|
Lock::Guard lock_guard(_lock);
|
|
|
|
/* lookup address */
|
|
Block *b = _map.first() ? _map.first()->lookup(virt) : 0;
|
|
if (!b) return;
|
|
|
|
Attached_ram_dataspace *ram_ds = b->ram_ds();
|
|
|
|
/*
|
|
* We support freeing of the whole block only.
|
|
*/
|
|
if (size && (size != dde_kit_pgtab_get_size(virt)))
|
|
PERR("Cannot split RAM allocations - the whole block is free'd.");
|
|
|
|
/* remove allocation from our map */
|
|
_map.remove(b);
|
|
|
|
/* delete virt->phys and phys->virt mappings */
|
|
dde_kit_pgtab_clear_region(virt);
|
|
|
|
/* detach and free RAM dataspace */
|
|
destroy(env()->heap(), ram_ds);
|
|
|
|
/* free meta data */
|
|
destroy(env()->heap(), b);
|
|
}
|
|
|
|
size_t consumed() { return _consumed; }
|
|
|
|
size_t overhead(size_t size) { return 0; }
|
|
};
|
|
|
|
|
|
/**
|
|
* Get backing store allocator
|
|
*
|
|
* \return reference to static backing store object
|
|
*/
|
|
static Backing_store_allocator * backing_store_allocator()
|
|
{
|
|
static Backing_store_allocator _backing_store_allocator;
|
|
|
|
return &_backing_store_allocator;
|
|
}
|
|
|
|
|
|
/*******************
|
|
** Slab facility **
|
|
*******************/
|
|
|
|
struct dde_kit_slab : public Slab
|
|
{
|
|
private:
|
|
|
|
void *_data;
|
|
size_t _object_size;
|
|
|
|
/*
|
|
* Each slab in the slab cache contains about 8 objects as proposed in
|
|
* the paper by Bonwick and block sizes are multiples of page size.
|
|
*/
|
|
size_t _calculate_block_size(size_t object_size)
|
|
{
|
|
size_t block_size = 8 * (object_size + sizeof(Slab_entry)) + sizeof(Slab_block);
|
|
|
|
return align_addr(block_size, DDE_KIT_PAGE_SHIFT);
|
|
}
|
|
|
|
public:
|
|
|
|
dde_kit_slab(size_t object_size)
|
|
:
|
|
Slab(object_size, _calculate_block_size(object_size), 0, backing_store_allocator()),
|
|
_object_size(object_size) { }
|
|
|
|
inline void *alloc()
|
|
{
|
|
void *result;
|
|
return (Slab::alloc(_object_size, &result) ? result : 0);
|
|
}
|
|
|
|
inline void *data() const { return _data; }
|
|
inline void data(void *data) { _data = data; }
|
|
};
|
|
|
|
|
|
extern "C" void dde_kit_slab_set_data(struct dde_kit_slab * slab, void *data) {
|
|
slab->data(data); }
|
|
|
|
|
|
extern "C" void *dde_kit_slab_get_data(struct dde_kit_slab * slab) {
|
|
return slab->data(); }
|
|
|
|
|
|
|
|
extern "C" void *dde_kit_slab_alloc(struct dde_kit_slab * slab) {
|
|
return slab->alloc(); }
|
|
|
|
|
|
extern "C" void dde_kit_slab_free(struct dde_kit_slab * slab, void *objp) {
|
|
slab->free(objp); }
|
|
|
|
|
|
extern "C" void dde_kit_slab_destroy(struct dde_kit_slab * slab) {
|
|
destroy(env()->heap(), slab); }
|
|
|
|
|
|
extern "C" struct dde_kit_slab * dde_kit_slab_init(unsigned size)
|
|
{
|
|
dde_kit_slab *slab_cache;
|
|
|
|
try {
|
|
slab_cache = new (env()->heap()) dde_kit_slab(size);
|
|
} catch (...) {
|
|
PERR("allocation failed (size=%zd)", sizeof(*slab_cache));
|
|
return 0;
|
|
}
|
|
|
|
return slab_cache;
|
|
}
|
|
|
|
|
|
/**********************************
|
|
** Large-block memory allocator **
|
|
**********************************/
|
|
|
|
extern "C" void *dde_kit_large_malloc(dde_kit_size_t size)
|
|
{
|
|
void *virt;
|
|
|
|
if (!backing_store_allocator()->alloc(size, &virt))
|
|
return 0;
|
|
|
|
return virt;
|
|
}
|
|
|
|
|
|
extern "C" void dde_kit_large_free(void *virt) {
|
|
backing_store_allocator()->free(virt, 0);
|
|
}
|
|
|
|
|
|
/*****************************
|
|
** Simple memory allocator **
|
|
*****************************/
|
|
|
|
extern "C" void *dde_kit_simple_malloc(dde_kit_size_t size)
|
|
{
|
|
/*
|
|
* We store the size of the allocation at the very
|
|
* beginning of the allocated block and return
|
|
* the subsequent address. This way, we can retrieve
|
|
* the size information when freeing the block.
|
|
*/
|
|
size_t real_size = size + sizeof(size_t);
|
|
size_t *addr;
|
|
try {
|
|
addr = (size_t *)env()->heap()->alloc(real_size);
|
|
} catch (...) {
|
|
return 0;
|
|
}
|
|
|
|
*addr = real_size;
|
|
return addr + 1;
|
|
}
|
|
|
|
|
|
extern "C" void dde_kit_simple_free(void *p)
|
|
{
|
|
if (!p) return;
|
|
|
|
size_t *addr = ((size_t *)p) - 1;
|
|
env()->heap()->free(addr, *addr);
|
|
}
|