genode/repos/dde_rump/include/util/allocator_fap.h
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

228 lines
5.5 KiB
C++

/**
* \brief Fast allocator for porting
* \author Sebastian Sumpf
* \date 2013-06-12
*/
/*
* Copyright (C) 2013-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__UTIL__ALLOCATOR_FAP_H_
#define _INCLUDE__UTIL__ALLOCATOR_FAP_H_
#include <base/allocator_avl.h>
#include <dataspace/client.h>
#include <rm_session/connection.h>
#include <region_map/client.h>
#include <rump/env.h>
namespace Allocator {
template <unsigned VM_SIZE, typename POLICY> class Backend_alloc;
template <unsigned VM_SIZE, typename POLICY> class Fap;
}
namespace Allocator {
using namespace Genode;
using Genode::size_t;
struct Default_allocator_policy
{
static int block() { return 0; }
static void unblock(int) { }
};
template <typename POLICY>
struct Policy_guard
{
int val;
Policy_guard() { val = POLICY::block(); }
~Policy_guard() { POLICY::unblock(val); }
};
/**
* Back-end allocator for Genode's slab allocator
*/
template <unsigned VM_SIZE, typename POLICY = Default_allocator_policy>
class Backend_alloc : public Genode::Allocator,
public Genode::Rm_connection,
public Genode::Region_map_client
{
private:
enum {
BLOCK_SIZE = 1024 * 1024, /* 1 MB */
ELEMENTS = VM_SIZE / BLOCK_SIZE, /* MAX number of dataspaces in VM */
};
typedef Genode::addr_t addr_t;
typedef Genode::Ram_dataspace_capability Ram_dataspace_capability;
typedef Genode::Allocator_avl Allocator_avl;
addr_t _base; /* virt. base address */
Cache_attribute _cached; /* non-/cached RAM */
Ram_dataspace_capability _ds_cap[ELEMENTS]; /* dataspaces to put in VM */
addr_t _ds_phys[ELEMENTS]; /* physical bases of dataspaces */
int _index = 0; /* current index in ds_cap */
Allocator_avl _range; /* manage allocations */
bool _quota_exceeded = false;
bool _alloc_block()
{
if (_quota_exceeded)
return false;
if (_index == ELEMENTS) {
error("slab backend exhausted!");
return false;
}
Policy_guard<POLICY> guard;
try {
_ds_cap[_index] = Rump::env().env().ram().alloc(BLOCK_SIZE, _cached);
/* attach at index * BLOCK_SIZE */
Region_map_client::attach_at(_ds_cap[_index], _index * BLOCK_SIZE, BLOCK_SIZE, 0);
/* lookup phys. address */
_ds_phys[_index] = Genode::Dataspace_client(_ds_cap[_index]).phys_addr();
} catch (Genode::Out_of_ram) {
warning("backend allocator exhausted (out of RAM)");
_quota_exceeded = true;
return false;
} catch (Genode::Out_of_caps) {
warning("backend allocator exhausted (out of caps)");
_quota_exceeded = true;
return false;
} catch (Genode::Region_map::Region_conflict) {
warning("backend VM region exhausted");
_quota_exceeded = true;
return false;
}
/* return base + offset in VM area */
addr_t block_base = _base + (_index * BLOCK_SIZE);
++_index;
_range.add_range(block_base, BLOCK_SIZE);
return true;
}
public:
Backend_alloc(Cache_attribute cached)
:
Rm_connection(Rump::env().env()),
Region_map_client(Rm_connection::create(VM_SIZE)),
_cached(cached),
_range(&Rump::env().heap())
{
/* reserver attach us, anywere */
_base = Rump::env().env().rm().attach(dataspace());
}
/**
* Allocate
*/
bool alloc(size_t size, void **out_addr)
{
bool done = _range.alloc(size, out_addr);
if (done)
return done;
done = _alloc_block();
if (!done)
return false;
return _range.alloc(size, out_addr);
}
void *alloc_aligned(size_t size, int align = 0)
{
void *addr;
if (!_range.alloc_aligned(size, &addr, align).error())
return addr;
if (!_alloc_block())
return 0;
if (_range.alloc_aligned(size, &addr, align).error()) {
error("backend allocator: Unable to allocate memory "
"(size: ", size, " align: ", align, ")");
return 0;
}
return addr;
}
void free(void *addr, size_t size) override { _range.free(addr, size); }
size_t overhead(size_t size) const override { return 0; }
bool need_size_for_free() const override { return false; }
/**
* Return phys address for given virtual addr.
*/
addr_t phys_addr(addr_t addr)
{
if (addr < _base || addr >= (_base + VM_SIZE))
return ~0UL;
int index = (addr - _base) / BLOCK_SIZE;
/* physical base of dataspace */
addr_t phys = _ds_phys[index];
if (!phys)
return ~0UL;
/* add offset */
phys += (addr - _base - (index * BLOCK_SIZE));
return phys;
}
bool inside(addr_t addr) const { return (addr >= _base) && (addr < (_base + VM_SIZE)); }
};
/**
* Interface
*/
template <unsigned VM_SIZE, typename POLICY = Default_allocator_policy>
class Fap
{
private:
typedef Allocator::Backend_alloc<VM_SIZE, POLICY> Backend_alloc;
Backend_alloc _back_allocator;
public:
Fap(bool cached)
: _back_allocator(cached ? CACHED : UNCACHED) { }
void *alloc(size_t size, int align = 0)
{
return _back_allocator.alloc_aligned(size, align);
}
void free(void *addr, size_t size)
{
_back_allocator.free(addr, size);
}
addr_t phys_addr(void *addr)
{
return _back_allocator.phys_addr((addr_t)addr);
}
};
} /* namespace Allocator */
#endif /* _INCLUDE__UTIL__ALLOCATOR_FAP_H_ */