genode/repos/libports/src/lib/libc/libc_mem_alloc.cc
Norman Feske 4d442bca30 Streamline exception types
This patch reduces the number of exception types by facilitating
globally defined exceptions for common usage patterns shared by most
services. In particular, RPC functions that demand a session-resource
upgrade not longer reflect this condition via a session-specific
exception but via the 'Out_of_ram' or 'Out_of_caps' types.

Furthermore, the 'Parent::Service_denied', 'Parent::Unavailable',
'Root::Invalid_args', 'Root::Unavailable', 'Service::Invalid_args',
'Service::Unavailable', and 'Local_service::Factory::Denied' types have
been replaced by the single 'Service_denied' exception type defined in
'session/session.h'.

This consolidation eases the error handling (there are fewer exceptions
to handle), alleviates the need to convert exceptions along the
session-creation call chain, and avoids possible aliasing problems
(catching the wrong type with the same name but living in a different
scope).
2017-05-31 13:16:07 +02:00

169 lines
4.2 KiB
C++

/*
* \brief Allocator for anonymous memory used by libc
* \author Norman Feske
* \date 2012-05-18
*
* The libc uses a dedicated allocator instead of 'env()->heap()' because the
* 'Allocator' interface of 'env()->heap()' does not allow for aligned
* allocations. Some libc functions, however, rely on aligned memory. For
* example the blocks returned by mmap for allocating anonymous memory are
* assumed to be page-aligned.
*
* The code is largely based on 'base/include/base/heap.h' and
* 'base/src/lib/base/heap.cc'.
*/
/* Genode includes */
#include <base/env.h>
#include <base/allocator_avl.h>
#include <base/sleep.h>
/* local includes */
#include "libc_mem_alloc.h"
#include "libc_init.h"
using namespace Genode;
Libc::Mem_alloc_impl::Dataspace_pool::~Dataspace_pool()
{
/* free all ram_dataspaces */
for (Dataspace *ds; (ds = first()); ) {
/*
* read dataspace capability and modify _ds_list before detaching
* possible backing store for Dataspace - we rely on LIFO list
* manipulation here!
*/
Ram_dataspace_capability ds_cap = ds->cap;
remove(ds);
delete ds;
_region_map->detach(ds->local_addr);
_ram_session->free(ds_cap);
}
}
int Libc::Mem_alloc_impl::Dataspace_pool::expand(size_t size, Range_allocator *alloc)
{
Ram_dataspace_capability new_ds_cap;
void *local_addr, *ds_addr = 0;
/* make new ram dataspace available at our local address space */
try {
new_ds_cap = _ram_session->alloc(size);
local_addr = _region_map->attach(new_ds_cap);
}
catch (Out_of_ram) { return -2; }
catch (Out_of_caps) { return -4; }
catch (Region_map::Region_conflict) {
_ram_session->free(new_ds_cap);
return -3;
}
/* add new local address range to our local allocator */
alloc->add_range((addr_t)local_addr, size);
/* now that we have new backing store, allocate Dataspace structure */
if (alloc->alloc_aligned(sizeof(Dataspace), &ds_addr, 2).error()) {
Genode::warning("libc: could not allocate meta data - this should never happen");
return -1;
}
/* add dataspace information to list of dataspaces */
Dataspace *ds = new (ds_addr) Dataspace(new_ds_cap, local_addr);
insert(ds);
return 0;
}
void *Libc::Mem_alloc_impl::alloc(size_t size, size_t align_log2)
{
/* serialize access of heap functions */
Lock::Guard lock_guard(_lock);
/* try allocation at our local allocator */
void *out_addr = 0;
if (_alloc.alloc_aligned(size, &out_addr, align_log2).ok())
return out_addr;
/*
* Calculate block size of needed backing store. The block must hold the
* requested 'size' with the requested alignment and a new Dataspace
* structure if the allocation above failed.
* Finally, we align the size to a 4K page.
*/
size_t request_size = size + max((1 << align_log2), 1024);
if (request_size < _chunk_size*sizeof(umword_t)) {
request_size = _chunk_size*sizeof(umword_t);
/*
* Exponentially increase chunk size with each allocated chunk until
* we hit 'MAX_CHUNK_SIZE'.
*/
_chunk_size = min(2*_chunk_size, (size_t)MAX_CHUNK_SIZE);
}
if (_ds_pool.expand(align_addr(request_size, 12), &_alloc) < 0) {
Genode::warning("libc: could not expand dataspace pool");
return 0;
}
/* allocate originally requested block */
return _alloc.alloc_aligned(size, &out_addr, align_log2).ok() ? out_addr : 0;
}
void Libc::Mem_alloc_impl::free(void *addr)
{
/* serialize access of heap functions */
Lock::Guard lock_guard(_lock);
/* forward request to our local allocator */
_alloc.free(addr);
}
Genode::size_t Libc::Mem_alloc_impl::size_at(void const *addr) const
{
/* serialize access of heap functions */
Lock::Guard lock_guard(_lock);
/* forward request to our local allocator */
return _alloc.size_at(addr);
}
static Libc::Mem_alloc *_libc_mem_alloc;
static void _init_mem_alloc(Genode::Region_map &rm, Genode::Ram_session &ram)
{
static Libc::Mem_alloc_impl inst(rm, ram);
_libc_mem_alloc = &inst;
}
namespace Libc {
void init_mem_alloc(Genode::Env &env)
{
_init_mem_alloc(env.rm(), env.ram());
}
}
Libc::Mem_alloc *Libc::mem_alloc()
{
if (!_libc_mem_alloc) {
error("attempt to use 'Libc::mem_alloc' before call of 'init_mem_alloc'");
_init_mem_alloc(*env_deprecated()->rm_session(), *env_deprecated()->ram_session());
}
return _libc_mem_alloc;
}