core: RAM service based on 'Session_object'

This patch reworks the implementation of core's RAM service to make use
of the 'Session_object' and to remove the distinction between the
"metadata" quota and the managed RAM quota. With the new implementation,
the session implicitly allocates its metadata from its own account. So
there is not need to handle 'Out_of_metadata' and 'Quota_exceeded' via
different exceptions. Instead, the new version solely uses the
'Out_of_ram' exception.

Furthermore, the 'Allocator::Out_of_memory' exception has become an alias
for 'Out_of_ram', which simplifies the error handling.

Issue #2398
This commit is contained in:
Norman Feske 2017-05-08 19:55:54 +02:00 committed by Christian Helmuth
parent 028e633af4
commit e44f65f3b2
32 changed files with 486 additions and 366 deletions

View File

@ -115,8 +115,6 @@ namespace Genode {
Ram_session_component _ram_session;
Synced_ram_session _synced_ram_session { _ram_session };
Ram_session_capability const _ram_session_cap;
/*
* The core-local PD session is provided by a real RPC object
* dispatched by the same entrypoint as the signal-source RPC
@ -133,6 +131,14 @@ namespace Genode {
Core_parent _core_parent { _heap, _services };
typedef String<100> Ram_args;
static Session::Resources _ram_resources()
{
return { Ram_quota { platform()->ram_alloc()->avail() },
Cap_quota { 1000 } };
}
public:
/**
@ -143,13 +149,18 @@ namespace Genode {
Platform_env_base(Ram_session_capability(),
Cpu_session_capability(),
Pd_session_capability()),
_ram_session(&_entrypoint, &_entrypoint,
platform()->ram_alloc(), platform()->core_mem_alloc(),
"ram_quota=4M", platform()->ram_alloc()->avail()),
_ram_session_cap(_entrypoint.manage(&_ram_session)),
_ram_session(_entrypoint,
_ram_resources(),
Session::Label("core"),
Session::Diag{false},
*platform()->ram_alloc(),
*Platform_env_base::rm_session(),
Ram_session_component::any_phys_range()),
_pd_session_component(_entrypoint),
_pd_session_client(_entrypoint.manage(&_pd_session_component))
{ }
{
_ram_session.init_ram_account();
}
/**
* Destructor
@ -165,7 +176,7 @@ namespace Genode {
******************************/
Parent *parent() override { return &_core_parent; }
Ram_session *ram_session() override { return &_synced_ram_session; }
Ram_session *ram_session() override { return &_ram_session; }
Ram_session_capability ram_session_cap() override { return _ram_session.cap(); }
Pd_session *pd_session() override { return &_pd_session_client; }
Allocator *heap() override { log(__func__, ": not implemented"); return nullptr; }

View File

@ -26,8 +26,7 @@ Native_capability Native_pd_component::alloc_rpc_cap(Native_capability ep,
try {
return _pd_session._rpc_cap_factory.alloc(ep, entry, mtd); }
catch (Allocator::Out_of_memory) {
throw Pd_session::Out_of_metadata(); }
catch (Allocator::Out_of_memory) { throw Out_of_ram(); }
}

View File

@ -81,7 +81,7 @@ void Ram_session_component::_export_ram_ds(Dataspace_component *ds) {
/* allocate the virtual region contiguous for the dataspace */
void * virt_ptr = alloc_region(ds, page_rounded_size);
if (!virt_ptr)
throw Out_of_metadata();
throw Core_virtual_memory_exhausted();
/* map it writeable for _clear_ds */
Nova::Utcb * const utcb = reinterpret_cast<Nova::Utcb *>(Thread::myself()->utcb());
@ -90,7 +90,7 @@ void Ram_session_component::_export_ram_ds(Dataspace_component *ds) {
if (map_local(utcb, ds->phys_addr(), reinterpret_cast<addr_t>(virt_ptr),
page_rounded_size >> get_page_size_log2(), rights_rw, true)) {
platform()->region_alloc()->free(virt_ptr, page_rounded_size);
throw Out_of_metadata();
throw Core_virtual_memory_exhausted();
}
/* assign virtual address to the dataspace to be used by clear_ds */

View File

@ -16,6 +16,7 @@
#include <base/stdint.h>
#include <base/exception.h>
#include <base/quota_guard.h>
namespace Genode {
@ -59,7 +60,7 @@ struct Genode::Allocator : Deallocator
/**
* Exception type
*/
class Out_of_memory : public Exception { };
typedef Out_of_ram Out_of_memory;
/**
* Destructor
@ -72,6 +73,9 @@ struct Genode::Allocator : Deallocator
* \param size block size to allocate
* \param out_addr resulting pointer to the new block,
* undefined in the error case
*
* \throw Out_of_ram
*
* \return true on success
*/
virtual bool alloc(size_t size, void **out_addr) = 0;
@ -83,6 +87,8 @@ struct Genode::Allocator : Deallocator
* a non-void type. By providing this method, we prevent the
* compiler from warning us about "dereferencing type-punned
* pointer will break strict-aliasing rules".
*
* \throw Out_of_ram
*/
template <typename T> bool alloc(size_t size, T **out_addr)
{
@ -105,9 +111,11 @@ struct Genode::Allocator : Deallocator
/**
* Allocate block and signal error as an exception
*
* \param size block size to allocate
* \return pointer to the new block
* \throw Out_of_memory
* \param size block size to allocate
*
* \throw Out_of_ram
*
* \return pointer to the new block
*/
void *alloc(size_t size)
{

View File

@ -48,6 +48,7 @@ class Genode::Attached_io_mem_dataspace
* \throw Parent::Service_denied
* \throw Insufficient_ram_quota
* \throw Parent::Unavailable
* \throw Out_of_ram
* \throw Rm_session::Attach_failed
*/
Attached_io_mem_dataspace(Env &env, Genode::addr_t base, Genode::size_t size,

View File

@ -90,7 +90,7 @@ class Genode::Attached_ram_dataspace
/**
* Constructor
*
* \throw Ram_session::Alloc_failed
* \throw Out_of_ram
* \throw Rm_session::Attach_failed
*/
Attached_ram_dataspace(Ram_session &ram, Region_map &rm,

View File

@ -392,7 +392,7 @@ class Genode::Child : protected Rpc_object<Parent>,
* \throw Missing_dynamic_linker
* \throw Invalid_executable
* \throw Region_map::Attach_failed
* \throw Ram_session::Alloc_failed
* \throw Out_of_ram
*
* The other arguments correspond to those of 'Child::Child'.
*

View File

@ -115,7 +115,7 @@ struct Genode::Env
* \param id ID of recipient session
* \param args description of the amount of quota to transfer
*
* \throw Quota_exceeded quota could not be transferred
* \throw Out_of_ram
*
* The 'args' argument has the same principle format as the 'args'
* argument of the 'session' operation.
@ -135,7 +135,7 @@ struct Genode::Env
* constructors in the binary and shared libraries the binary depends on. If
* the component requires static construction it needs to call this function
* at construction time explicitly. For example, the libc implementation
* executes this function before constructing libc components.
* executes this function before constructing libc components.
*/
virtual void exec_static_constructors() = 0;
};

View File

@ -33,11 +33,6 @@ namespace Genode {
struct Genode::Ram_allocator
{
class Alloc_failed : public Exception { };
class Quota_exceeded : public Alloc_failed { };
class Out_of_metadata : public Alloc_failed { };
/**
* Allocate RAM dataspace
*
@ -45,8 +40,8 @@ struct Genode::Ram_allocator
* \param cached selects cacheability attributes of the memory,
* uncached memory, i.e., for DMA buffers
*
* \throw Quota_exceeded
* \throw Out_of_metadata
* \throw Out_of_ram
* \throw Out_of_caps
*
* \return capability to new RAM dataspace
*/

View File

@ -65,14 +65,12 @@ class Genode::Session_object : public Ram_quota_guard,
Cap_quota_guard(resources.cap_quota),
_ep(ep), _diag(diag), _label(label)
{
Cap_quota_guard::withdraw(Cap_quota{1});
_ep.manage(this);
}
~Session_object()
{
_ep.dissolve(this);
Cap_quota_guard::replenish(Cap_quota{1});
}
/**

View File

@ -45,6 +45,8 @@ struct Genode::Ram_session : Session, Ram_allocator
class Invalid_session : public Exception { };
class Undefined_ref_account : public Exception { };
/* deprecated */
typedef Out_of_ram Quota_exceeded;
/**
* Destructor
@ -100,7 +102,7 @@ struct Genode::Ram_session : Session, Ram_allocator
*********************/
GENODE_RPC_THROW(Rpc_alloc, Ram_dataspace_capability, alloc,
GENODE_TYPE_LIST(Quota_exceeded, Out_of_metadata, Undefined_ref_account),
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps, Undefined_ref_account),
size_t, Cache_attribute);
GENODE_RPC(Rpc_free, void, free, Ram_dataspace_capability);
GENODE_RPC(Rpc_ref_account, void, ref_account, Capability<Ram_session>);

View File

@ -164,10 +164,7 @@ class Genode::Root_component : public Rpc_object<Typed_root<SESSION_TYPE> >,
SESSION_TYPE *s = 0;
try { s = _create_session(adjusted_args, affinity); }
catch (Allocator::Out_of_memory) {
error("out of memory for session creation, '", args, "'");
throw Root::Unavailable();
}
catch (Out_of_ram) { throw Insufficient_ram_quota(); }
/*
* Consider that the session-object constructor may already have

View File

@ -0,0 +1,175 @@
/*
* \brief Resource account handling
* \author Norman Feske
* \date 2017-04-24
*/
/*
* Copyright (C) 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 _CORE__INCLUDE__ACCOUNT_H_
#define _CORE__INCLUDE__ACCOUNT_H_
#include <util/reconstructible.h>
#include <base/registry.h>
#include <base/quota_guard.h>
namespace Genode { template <typename> class Account; }
template <typename UNIT>
class Genode::Account
{
private:
Quota_guard<UNIT> &_quota_guard;
Session::Label const &_label;
UNIT const _initial_used = _quota_guard.used();
Lock mutable _lock;
/*
* Reference account
*/
Account *_ref_account = nullptr;
/*
* Registry of accounts that have this account as their reference
* account.
*/
Registry<Account> _ref_account_members;
/*
* Role as reference-account user
*/
Constructible<typename Registry<Account>::Element> _ref_account_member;
/**
* Assign 'this' as reference account of 'account'
*/
void _adopt(Account &account)
{
account._ref_account_member.construct(_ref_account_members, account);
account._ref_account = this;
}
public:
typedef typename Quota_guard<UNIT>::Limit_exceeded Limit_exceeded;
class Unrelated_account : Exception { };
/**
* Constructor for creating a regular account that is rechargeable by
* the specified reference account
*/
Account(Quota_guard<UNIT> &quota_guard, Session_label const &label,
Account &ref_account)
: _quota_guard(quota_guard), _label(label) { ref_account._adopt(*this); }
/**
* Constructor used for creating the initial account
*/
Account(Quota_guard<UNIT> &quota_guard, Session_label const &label)
: _quota_guard(quota_guard), _label(label) { }
~Account()
{
if (!_ref_account) return;
Lock::Guard guard(_lock);
if (_quota_guard.used().value > _initial_used.value) {
UNIT const dangling { _quota_guard.used().value - _initial_used.value };
warning("destroying account with allocated (possibly leaking?) "
"resources (", dangling, " ", UNIT::name(),")");
_quota_guard.replenish(dangling);
}
/* transfer remaining quota to our reference account */
_ref_account->_quota_guard.upgrade(_quota_guard.limit());
/* assign all sub accounts to our reference account */
_ref_account_members.for_each([&] (Account &orphan) {
_ref_account->_adopt(orphan); });
}
/**
* Transfer quota to/from other account
*
* \throw Unrelated_account
* \throw Limit_exceeded
*/
void transfer_quota(Account &other, UNIT amount)
{
{
Lock::Guard guard(_lock);
/* transfers are permitted only from/to the reference account */
if (_ref_account != &other && other._ref_account != this)
throw Unrelated_account();
/* downgrade from this account */
if (!_quota_guard.try_downgrade(amount))
throw Limit_exceeded();
}
/* credit to 'other' */
Lock::Guard guard(other._lock);
other._quota_guard.upgrade(amount);
}
UNIT limit() const
{
Lock::Guard guard(_lock);
return _quota_guard.limit();
}
UNIT used() const
{
Lock::Guard guard(_lock);
return _quota_guard.used();
}
UNIT avail() const
{
Lock::Guard guard(_lock);
return _quota_guard.avail();
}
/**
* Withdraw quota from account
*
* Called when allocating physical resources
*
* \throw Limit_exceeded
*/
void withdraw(UNIT amount)
{
Lock::Guard guard(_lock);
_quota_guard.withdraw(amount);
}
/**
* Replenish quota to account
*
* Called when releasing physical resources
*/
void replenish(UNIT amount)
{
Lock::Guard guard(_lock);
_quota_guard.replenish(amount);
}
void print(Output &out) const { Genode::print(out, _quota_guard); }
Session::Label label() const { return _label; }
};
#endif /* _CORE__INCLUDE__ACCOUNT_H_ */

View File

@ -134,6 +134,14 @@ namespace Genode {
Core_parent _core_parent { _heap, _services };
typedef String<100> Ram_args;
static Session::Resources _ram_resources()
{
return { Ram_quota { platform()->ram_alloc()->avail() },
Cap_quota { platform()->max_caps() } };
}
public:
/**
@ -143,13 +151,18 @@ namespace Genode {
:
_entrypoint(nullptr, ENTRYPOINT_STACK_SIZE, "entrypoint"),
_region_map(_entrypoint),
_ram_session(&_entrypoint, &_entrypoint,
platform()->ram_alloc(), platform()->core_mem_alloc(),
"ram_quota=4M", platform()->ram_alloc()->avail()),
_ram_session_cap(_entrypoint.manage(&_ram_session)),
_ram_session(_entrypoint,
_ram_resources(),
Session::Label("core"),
Session::Diag{false},
*platform()->ram_alloc(),
_region_map,
Ram_session_component::any_phys_range()),
_pd_session_component(_entrypoint),
_pd_session_client(_entrypoint.manage(&_pd_session_component))
{ }
{
_ram_session.init_ram_account();
}
/**
* Destructor

View File

@ -24,41 +24,50 @@ namespace Genode {
{
private:
Range_allocator *_ram_alloc;
Rpc_entrypoint *_ds_ep;
Rpc_entrypoint &_ep;
Range_allocator &_phys_alloc;
Region_map &_local_rm;
static Ram_session_component::Phys_range phys_range_from_args(char const *args)
{
addr_t const start = Arg_string::find_arg(args, "phys_start").ulong_value(0);
addr_t const size = Arg_string::find_arg(args, "phys_size").ulong_value(0);
addr_t const end = start + size - 1;
return (start <= end) ? Ram_session_component::Phys_range { start, end }
: Ram_session_component::any_phys_range();
}
protected:
Ram_session_component *_create_session(const char *args)
{
return new (md_alloc())
Ram_session_component(_ds_ep, ep(), _ram_alloc,
md_alloc(), args);
Ram_session_component(_ep,
session_resources_from_args(args),
session_label_from_args(args),
session_diag_from_args(args),
_phys_alloc, _local_rm,
phys_range_from_args(args));
}
void _upgrade_session(Ram_session_component *ram, const char *args)
{
size_t ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0);
ram->upgrade_ram_quota(ram_quota);
ram->Ram_quota_guard::upgrade(ram_quota_from_args(args));
ram->Cap_quota_guard::upgrade(cap_quota_from_args(args));
ram->session_quota_upgraded();
}
public:
/**
* Constructor
*
* \param session_ep entry point for managing ram session objects
* \param ds_ep entry point for managing dataspaces
* \param ram_alloc pool of memory to be assigned to ram sessions
* \param md_alloc meta-data allocator to be used by root component
*/
Ram_root(Rpc_entrypoint *session_ep,
Rpc_entrypoint *ds_ep,
Range_allocator *ram_alloc,
Allocator *md_alloc)
Ram_root(Rpc_entrypoint &ep,
Range_allocator &phys_alloc,
Region_map &local_rm,
Allocator &md_alloc)
:
Root_component<Ram_session_component>(session_ep, md_alloc),
_ram_alloc(ram_alloc), _ds_ep(ds_ep) { }
Root_component<Ram_session_component>(&ep, &md_alloc),
_ep(ep), _phys_alloc(phys_alloc), _local_rm(local_rm)
{ }
};
}

View File

@ -18,25 +18,28 @@
#include <util/list.h>
#include <base/heap.h>
#include <base/tslab.h>
#include <base/rpc_server.h>
#include <base/session_object.h>
#include <base/allocator_guard.h>
#include <base/synced_allocator.h>
#include <base/session_label.h>
/* core includes */
#include <dataspace_component.h>
#include <util.h>
#include <account.h>
namespace Genode {
class Ram_session_component;
typedef List<Ram_session_component> Ram_ref_account_members;
}
namespace Genode { class Ram_session_component; }
class Genode::Ram_session_component : public Rpc_object<Ram_session>,
public Ram_ref_account_members::Element,
class Genode::Ram_session_component : public Session_object<Ram_session>,
public Dataspace_owner
{
public:
struct Phys_range { addr_t start, end; };
static Phys_range any_phys_range() { return { 0UL, ~0UL }; }
private:
class Invalid_dataspace : public Exception { };
@ -47,61 +50,46 @@ class Genode::Ram_session_component : public Rpc_object<Ram_session>,
*/
static constexpr size_t SBS = get_page_size() - Sliced_heap::meta_data_size();
using Ds_slab = Synced_allocator<Tslab<Dataspace_component, SBS> >;
using Ds_slab = Tslab<Dataspace_component, SBS>;
Rpc_entrypoint *_ds_ep;
Rpc_entrypoint *_ram_session_ep;
Range_allocator *_ram_alloc;
size_t _quota_limit;
size_t _payload; /* quota used for payload */
Allocator_guard _md_alloc; /* guarded meta-data allocator */
Ds_slab _ds_slab; /* meta-data allocator */
Ram_session_component *_ref_account; /* reference ram session */
addr_t _phys_start;
addr_t _phys_end;
Rpc_entrypoint &_ep;
enum { MAX_LABEL_LEN = 64 };
char _label[MAX_LABEL_LEN];
Range_allocator &_phys_alloc;
/**
* List of RAM sessions that use us as their reference account
Constrained_ram_allocator _constrained_md_ram_alloc;
Constructible<Sliced_heap> _sliced_heap;
/*
* Statically allocated initial slab block for '_ds_slab', needed to
* untangle the hen-and-egg problem of allocating the meta data for
* core's RAM allocator from itself. I also saves the allocation
* of one dataspace (along with a dataspace capability) per session.
*/
Ram_ref_account_members _ref_members;
Lock _ref_members_lock; /* protect '_ref_members' */
uint8_t _initial_sb[SBS];
/**
* Register RAM session to use us as reference account
*/
void _register_ref_account_member(Ram_session_component *new_member);
Constructible<Ds_slab> _ds_slab;
/**
* Dissolve reference-account relationship of a member account
*/
void _remove_ref_account_member(Ram_session_component *member);
void _unsynchronized_remove_ref_account_member(Ram_session_component *member);
Phys_range const _phys_range;
/**
* Return portion of RAM quota that is currently in use
*/
size_t used_quota() { return _payload; }
Constructible<Account<Ram_quota> > _ram_account;
/**
* Free dataspace
*/
void _free_ds(Dataspace_capability ds_cap);
/**
* Transfer quota to another RAM session
*/
void _transfer_quota(Ram_session_component *dst, size_t amount);
/********************************************
** Platform-implemented support functions **
********************************************/
struct Core_virtual_memory_exhausted : Exception { };
/**
* Export RAM dataspace as shared memory block
*
* \throw Core_virtual_memory_exhausted
*/
void _export_ram_ds(Dataspace_component *ds);
@ -117,44 +105,24 @@ class Genode::Ram_session_component : public Rpc_object<Ram_session>,
public:
/**
* Constructor
*
* \param ds_ep server entry point to manage the
* dataspaces created by the Ram session
* \param ram_session_ep entry point that manages Ram sessions,
* used for looking up another ram session
* in transfer_quota()
* \param ram_alloc memory pool to manage
* \param md_alloc meta-data allocator
* \param md_ram_quota limit of meta-data backing store
* \param quota_limit initial quota limit
*
* The 'quota_limit' parameter is only used for the very
* first ram session in the system. All other ram session
* load their quota via 'transfer_quota'.
*/
Ram_session_component(Rpc_entrypoint *ds_ep,
Rpc_entrypoint *ram_session_ep,
Range_allocator *ram_alloc,
Allocator *md_alloc,
const char *args,
size_t quota_limit = 0);
Ram_session_component(Rpc_entrypoint &ep,
Resources resources,
Session_label const &label,
Diag diag,
Range_allocator &phys_alloc,
Region_map &local_rm,
Phys_range phys_range);
/**
* Destructor
*/
~Ram_session_component();
/**
* Accessors
* Initialize RAM account without providing a reference account
*
* This method is solely used to set up the initial RAM session within
* core. The RAM accounts of regular RAM session are initialized via
* 'ref_account'.
*/
Ram_session_component *ref_account() { return _ref_account; }
/**
* Register quota donation at allocator guard
*/
void upgrade_ram_quota(size_t ram_quota) { _md_alloc.upgrade(ram_quota); }
void init_ram_account() { _ram_account.construct(*this, _label); }
/**
* Get physical address of the RAM that backs a dataspace
@ -181,10 +149,19 @@ class Genode::Ram_session_component : public Rpc_object<Ram_session>,
** RAM Session interface **
***************************/
void ref_account(Ram_session_capability);
void transfer_quota(Ram_session_capability, Ram_quota);
Ram_quota ram_quota() const override { return { _quota_limit}; }
Ram_quota used_ram() const override { return { _payload}; }
void ref_account(Ram_session_capability) override;
void transfer_quota(Ram_session_capability, Ram_quota) override;
Ram_quota ram_quota() const override
{
return _ram_account.constructed() ? _ram_account->limit() : Ram_quota { 0 };
}
Ram_quota used_ram() const override
{
return _ram_account.constructed() ? _ram_account->used() : Ram_quota { 0 };
}
};
#endif /* _CORE__INCLUDE__RAM_SESSION_COMPONENT_H_ */

View File

@ -140,9 +140,10 @@ class Core_child : public Child_policy
/**
* Constructor
*/
Core_child(Registry<Service> &services, Ram_session &core_ram,
Capability<Ram_session> core_ram_cap, Ram_quota ram_quota,
Cpu_session &core_cpu, Capability<Cpu_session> core_cpu_cap)
Core_child(Registry<Service> &services,
Ram_session &core_ram, Capability<Ram_session> core_ram_cap,
Cpu_session &core_cpu, Capability<Cpu_session> core_cpu_cap,
Ram_quota ram_quota)
:
_entrypoint(nullptr, STACK_SIZE, "init_child", false),
_services(services),
@ -249,11 +250,14 @@ int main()
Registry<Service> &services = core_env()->services();
static Ram_allocator &core_ram_alloc = *core_env()->ram_session();
static Region_map &local_rm = *core_env()->rm_session();
/*
* Allocate session meta data on distinct dataspaces to enable independent
* destruction (to enable quota trading) of session component objects.
*/
static Sliced_heap sliced_heap(env_deprecated()->ram_session(), env_deprecated()->rm_session());
static Sliced_heap sliced_heap(core_ram_alloc, local_rm);
/*
* Factory for creating RPC capabilities within core
@ -262,7 +266,7 @@ int main()
static Pager_entrypoint pager_ep(rpc_cap_factory);
static Ram_root ram_root (e, e, platform()->ram_alloc(), &sliced_heap);
static Ram_root ram_root (*e, *platform()->ram_alloc(), local_rm, sliced_heap);
static Rom_root rom_root (e, e, platform()->rom_fs(), &sliced_heap);
static Rm_root rm_root (e, &sliced_heap, pager_ep);
static Cpu_root cpu_root (e, e, &pager_ep, &sliced_heap,
@ -309,8 +313,10 @@ int main()
"assigned to init");
static Reconstructible<Core_child>
init(services, *env_deprecated()->ram_session(), env_deprecated()->ram_session_cap(),
Ram_quota{avail_ram_quota}, core_cpu, core_cpu_cap);
init(services,
*env_deprecated()->ram_session(), env_deprecated()->ram_session_cap(),
core_cpu, core_cpu_cap,
Ram_quota{avail_ram_quota});
platform()->wait_for_exit();

View File

@ -28,14 +28,14 @@ addr_t Ram_session_component::phys_addr(Ram_dataspace_capability ds)
return dsc->phys_addr();
};
return _ds_ep->apply(ds, lambda);
return _ep.apply(ds, lambda);
}
void Ram_session_component::_free_ds(Dataspace_capability ds_cap)
{
Dataspace_component *ds = nullptr;
_ds_ep->apply(ds_cap, [&] (Dataspace_component *c)
_ep.apply(ds_cap, [&] (Dataspace_component *c)
{
if (!c) return;
if (!c->owner(this)) return;
@ -45,7 +45,7 @@ void Ram_session_component::_free_ds(Dataspace_capability ds_cap)
size_t ds_size = ds->size();
/* tell entry point to forget the dataspace */
_ds_ep->dissolve(ds);
_ep.dissolve(ds);
/* remove dataspace from all RM sessions */
ds->detach_from_rm_sessions();
@ -54,64 +54,14 @@ void Ram_session_component::_free_ds(Dataspace_capability ds_cap)
_revoke_ram_ds(ds);
/* free physical memory that was backing the dataspace */
_ram_alloc->free((void *)ds->phys_addr(), ds_size);
_phys_alloc.free((void *)ds->phys_addr(), ds_size);
/* adjust payload */
Lock::Guard lock_guard(_ref_members_lock);
_payload -= ds_size;
_ram_account->replenish(Ram_quota{ds_size});
});
/* call dataspace destructors and free memory */
if (ds)
destroy(&_ds_slab, ds);
}
void Ram_session_component::_transfer_quota(Ram_session_component *dst, size_t amount)
{
/* check if recipient is a valid Ram_session_component */
if (!dst)
throw Invalid_session();
/* check for reference account relationship */
if ((ref_account() != dst) && (dst->ref_account() != this))
throw Invalid_session();
/* decrease quota limit of this session - check against used quota */
if (_quota_limit < amount + _payload) {
warning("insufficient quota for transfer: "
"'", Cstring(_label), "' to '", Cstring(dst->_label), "' "
"have ", (_quota_limit - _payload)/1024, " KiB, "
"need ", amount/1024, " KiB");
throw Out_of_ram();
}
_quota_limit -= amount;
/* increase quota_limit of recipient */
dst->_quota_limit += amount;
}
void Ram_session_component::_register_ref_account_member(Ram_session_component *new_member)
{
Lock::Guard lock_guard(_ref_members_lock);
_ref_members.insert(new_member);
new_member->_ref_account = this;
}
void Ram_session_component::_unsynchronized_remove_ref_account_member(Ram_session_component *member)
{
member->_ref_account = 0;
_ref_members.remove(member);
}
void Ram_session_component::_remove_ref_account_member(Ram_session_component *member)
{
Lock::Guard lock_guard(_ref_members_lock);
_unsynchronized_remove_ref_account_member(member);
destroy(*_ds_slab, ds);
}
@ -124,14 +74,23 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, Cache_attr
ds_size = align_addr(ds_size, 12);
/*
* Check quota!
* Track quota usage
*
* In the worst case, we need to allocate a new slab block for the
* meta data of the dataspace to be created - therefore, we add
* the slab block size here.
* We use a guard to roll back the withdrawal of the quota whenever
* we leave the method scope via an exception. The withdrawal is
* acknowledge just before successfully leaving the method.
*/
if (used_quota() + SBS + ds_size > _quota_limit)
throw Quota_exceeded();
Ram_quota_guard::Reservation dataspace_ram_costs(*this, Ram_quota{ds_size});
/*
* In the worst case, we need to allocate a new slab block for the
* meta data of the dataspace to be created. Therefore, we temporarily
* withdraw the slab block size here to trigger an exception if the
* account does not have enough room for the meta data.
*/
{
Ram_quota_guard::Reservation sbs_ram_costs(*this, Ram_quota{SBS});
}
/*
* Allocate physical backing store
@ -150,11 +109,11 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, Cache_attr
* preserve lower physical regions for device drivers, which may have DMA
* constraints.
*/
if (_phys_start == 0 && _phys_end == ~0UL) {
if (_phys_range.start == 0 && _phys_range.end == ~0UL) {
addr_t const high_start = (sizeof(void *) == 4 ? 3UL : 4UL) << 30;
for (size_t align_log2 = log2(ds_size); align_log2 >= 12; align_log2--) {
if (_ram_alloc->alloc_aligned(ds_size, &ds_addr, align_log2,
high_start, _phys_end).ok()) {
if (_phys_alloc.alloc_aligned(ds_size, &ds_addr, align_log2,
high_start, _phys_range.end).ok()) {
alloc_succeeded = true;
break;
}
@ -164,14 +123,31 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, Cache_attr
/* apply constraints or re-try because higher memory allocation failed */
if (!alloc_succeeded) {
for (size_t align_log2 = log2(ds_size); align_log2 >= 12; align_log2--) {
if (_ram_alloc->alloc_aligned(ds_size, &ds_addr, align_log2,
_phys_start, _phys_end).ok()) {
if (_phys_alloc.alloc_aligned(ds_size, &ds_addr, align_log2,
_phys_range.start, _phys_range.end).ok()) {
alloc_succeeded = true;
break;
}
}
}
/*
* Helper to release the allocated physical memory whenever we leave the
* scope via an exception.
*/
struct Phys_alloc_guard
{
Range_allocator &phys_alloc;
void * const ds_addr;
bool ack = false;
Phys_alloc_guard(Range_allocator &phys_alloc, void *ds_addr)
: phys_alloc(phys_alloc), ds_addr(ds_addr) { }
~Phys_alloc_guard() { if (!ack) phys_alloc.free(ds_addr); }
} phys_alloc_guard(_phys_alloc, ds_addr);
/*
* Normally, init's quota equals the size of physical memory and this quota
* is distributed among the processes. As we check the quota before
@ -180,38 +156,29 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, Cache_attr
*/
if (!alloc_succeeded) {
error("out of physical memory while allocating ", ds_size, " bytes ",
"in range [", Hex(_phys_start), "-", Hex(_phys_end), "] - label ",
Cstring(_label));
throw Quota_exceeded();
"in range [", Hex(_phys_range.start), "-", Hex(_phys_range.end), "]");
throw Out_of_ram();
}
Dataspace_component *ds;
try {
/*
* For non-cached RAM dataspaces, we mark the dataspace as write
* combined and expect the pager to evaluate this dataspace property
* when resolving page faults.
*/
ds = new (&_ds_slab)
Dataspace_component(ds_size, (addr_t)ds_addr, cached, true, this);
} catch (Allocator::Out_of_memory) {
warning("could not allocate metadata");
/* cleanup unneeded resources */
_ram_alloc->free(ds_addr);
throw Out_of_metadata();
}
/*
* For non-cached RAM dataspaces, we mark the dataspace as write
* combined and expect the pager to evaluate this dataspace property
* when resolving page faults.
*
* \throw Out_of_ram
* \throw Out_of_caps
*/
Dataspace_component *ds = new (*_ds_slab)
Dataspace_component(ds_size, (addr_t)ds_addr, cached, true, this);
/* create native shared memory representation of dataspace */
try {
_export_ram_ds(ds);
} catch (Out_of_metadata) {
try { _export_ram_ds(ds); }
catch (Core_virtual_memory_exhausted) {
warning("could not export RAM dataspace of size ", ds->size());
/* cleanup unneeded resources */
destroy(&_ds_slab, ds);
_ram_alloc->free(ds_addr);
throw Quota_exceeded();
/* cleanup unneeded resources */
destroy(*_ds_slab, ds);
throw Out_of_ram();
}
/*
@ -221,18 +188,19 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, Cache_attr
*/
_clear_ds(ds);
Dataspace_capability result = _ds_ep->manage(ds);
Dataspace_capability result = _ep.manage(ds);
Lock::Guard lock_guard(_ref_members_lock);
/* keep track of the used quota for actual payload */
_payload += ds_size;
dataspace_ram_costs.acknowledge();
phys_alloc_guard.ack = true;
return static_cap_cast<Ram_dataspace>(result);
}
void Ram_session_component::free(Ram_dataspace_capability ds_cap) {
_free_ds(ds_cap); }
void Ram_session_component::free(Ram_dataspace_capability ds_cap)
{
_free_ds(ds_cap);
}
size_t Ram_session_component::dataspace_size(Ram_dataspace_capability ds_cap) const
@ -241,7 +209,7 @@ size_t Ram_session_component::dataspace_size(Ram_dataspace_capability ds_cap) co
return 0;
size_t result = 0;
_ds_ep->apply(ds_cap, [&] (Dataspace_component *c) {
_ep.apply(ds_cap, [&] (Dataspace_component *c) {
if (c && c->owner(this))
result = c->size(); });
@ -251,102 +219,76 @@ size_t Ram_session_component::dataspace_size(Ram_dataspace_capability ds_cap) co
void Ram_session_component::ref_account(Ram_session_capability ram_session_cap)
{
/* the reference account cannot be defined twice */
if (_ref_account)
/* the reference account can be defined only once */
if (_ram_account.constructed())
return;
if (this->cap() == ram_session_cap)
return;
auto lambda = [this] (Ram_session_component *ref) {
_ep.apply(ram_session_cap, [&] (Ram_session_component *ram) {
/* check if recipient is a valid Ram_session_component */
if (!ref)
if (!ram || !ram->_ram_account.constructed()) {
error("invalid RAM session specified as ref account");
throw Invalid_session();
}
_ref_account = ref;
_ref_account->_register_ref_account_member(this);
};
_ram_session_ep->apply(ram_session_cap, lambda);
_ram_account.construct(*this, _label, *ram->_ram_account);
});
}
void Ram_session_component::transfer_quota(Ram_session_capability ram_session_cap,
Ram_quota amount)
{
auto lambda = [&] (Ram_session_component *dst) {
_transfer_quota(dst, amount.value); };
/* the reference account can be defined only once */
if (!_ram_account.constructed())
throw Undefined_ref_account();
if (this->cap() == ram_session_cap)
return;
return _ram_session_ep->apply(ram_session_cap, lambda);
_ep.apply(ram_session_cap, [&] (Ram_session_component *ram) {
if (!ram || !ram->_ram_account.constructed())
throw Invalid_session();
try {
_ram_account->transfer_quota(*ram->_ram_account, amount); }
catch (Account<Ram_quota>::Unrelated_account) {
warning("attempt to transfer RAM quota to unrelated RAM session");
throw Invalid_session(); }
catch (Account<Ram_quota>::Limit_exceeded) {
warning("RAM limit (", *_ram_account, ") exceeded "
"during transfer_quota(", amount, ")");
throw Out_of_ram(); }
});
}
Ram_session_component::Ram_session_component(Rpc_entrypoint *ds_ep,
Rpc_entrypoint *ram_session_ep,
Range_allocator *ram_alloc,
Allocator *md_alloc,
const char *args,
size_t quota_limit)
Ram_session_component::Ram_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &label,
Diag diag,
Range_allocator &phys_alloc,
Region_map &local_rm,
Phys_range phys_range)
:
_ds_ep(ds_ep), _ram_session_ep(ram_session_ep), _ram_alloc(ram_alloc),
_quota_limit(quota_limit), _payload(0),
_md_alloc(md_alloc, Arg_string::find_arg(args, "ram_quota").ulong_value(0)),
_ds_slab(&_md_alloc), _ref_account(0),
_phys_start(Arg_string::find_arg(args, "phys_start").ulong_value(0))
Session_object(ep, resources, label, diag),
_ep(ep),
_phys_alloc(phys_alloc),
_constrained_md_ram_alloc(*this, *this, *this),
_phys_range(phys_range)
{
Arg_string::find_arg(args, "label").string(_label, sizeof(_label), "");
size_t phys_size = Arg_string::find_arg(args, "phys_size").ulong_value(0);
/* sanitize overflow and interpret phys_size==0 as maximum phys address */
if (_phys_start + phys_size <= _phys_start)
_phys_end = ~0UL;
else
_phys_end = _phys_start + phys_size - 1;
_sliced_heap.construct(_constrained_md_ram_alloc, local_rm);
_ds_slab.construct(*_sliced_heap, _initial_sb);
}
Ram_session_component::~Ram_session_component()
{
/* destroy all dataspaces */
for (Dataspace_component *ds; (ds = _ds_slab()->first_object());
Ds_slab &ds_slab = *_ds_slab;
for (Dataspace_component *ds; (ds = ds_slab.first_object());
_free_ds(ds->cap()));
if (_payload != 0)
warning("remaining payload of ", _payload, " in ram session to destroy");
if (!_ref_account) return;
/* transfer remaining quota to reference account */
try { _transfer_quota(_ref_account, _quota_limit); } catch (...) { }
/* remember our original reference account */
Ram_session_component *orig_ref_account = _ref_account;
/* remove reference to us from the reference account */
_ref_account->_remove_ref_account_member(this);
/*
* Now, the '_ref_account' member has become invalid.
*/
Lock::Guard lock_guard(_ref_members_lock);
/* assign all sub accounts to our original reference account */
for (Ram_session_component *rsc; (rsc = _ref_members.first()); ) {
_unsynchronized_remove_ref_account_member(rsc);
/*
* This function grabs the '_ref_account_lock' of the '_ref_account',
* which is never identical to ourself. Hence, deadlock cannot happen
* here.
*/
orig_ref_account->_register_ref_account_member(rsc);
}
_ref_account = 0;
}

View File

@ -108,8 +108,8 @@ class Genode::Expanding_parent_client : public Parent_client
* immediately. The second upgrade attempt may fail too if the
* parent handles the resource request asynchronously. In this
* case, we escalate the problem to caller by propagating the
* 'Parent::Quota_exceeded' exception. Now, it is the job of the
* caller to issue (and respond to) a resource request.
* 'Out_of_ram' exception. Now, it is the job of the caller to
* issue (and respond to) a resource request.
*/
enum { NUM_ATTEMPTS = 2 };
return retry<Out_of_ram>(

View File

@ -41,16 +41,8 @@ struct Genode::Expanding_ram_session_client : Upgradeable_client<Genode::Ram_ses
* to the parent and retry.
*/
enum { NUM_ATTEMPTS = 2 };
return retry<Ram_session::Quota_exceeded>(
[&] () {
/*
* If the RAM session runs out of meta data, upgrade the
* session quota and retry.
*/
return retry<Ram_session::Out_of_metadata>(
[&] () { return Ram_session_client::alloc(size, cached); },
[&] () { upgrade_ram(8*1024); });
},
return retry<Out_of_ram>(
[&] () { return Ram_session_client::alloc(size, cached); },
[&] () {
/*
* The RAM service withdraws the meta data for the allocator

View File

@ -87,6 +87,7 @@ void Child::session_sigh(Signal_context_capability sigh)
/**
* Create session-state object for a dynamically created session
*
* \throw Out_of_ram
* \throw Insufficient_ram_quota
* \throw Parent::Service_denied
*/
@ -648,7 +649,6 @@ void Child::_try_construct_env_dependent_members()
_parent_cap);
}
catch (Out_of_ram) { _error("out of RAM during ELF loading"); }
catch (Ram_session::Alloc_failed) { _error("RAM allocation failed during ELF loading"); }
catch (Cpu_session::Thread_creation_failed) { _error("unable to create initial thread"); }
catch (Cpu_session::Out_of_metadata) { _error("CPU session quota exhausted"); }
catch (Process::Missing_dynamic_linker) { _error("dynamic linker unavailable"); }

View File

@ -102,7 +102,7 @@ Child::Process::Loaded_executable::Loaded_executable(Dataspace_capability elf_ds
/* alloc dataspace */
Dataspace_capability ds_cap;
try { ds_cap = ram.alloc(size); }
catch (Ram_session::Alloc_failed) {
catch (Out_of_ram) {
error("allocation of read-write segment failed"); throw; };
/* attach dataspace */

View File

@ -89,9 +89,9 @@ Heap::Dataspace *Heap::_allocate_dataspace(size_t size, bool enforce_separate_me
try {
new_ds_cap = _ds_pool.ram_alloc->alloc(size);
ds_addr = _ds_pool.region_map->attach(new_ds_cap);
} catch (Ram_session::Alloc_failed) {
return 0;
} catch (Region_map::Attach_failed) {
}
catch (Out_of_ram) { return nullptr; }
catch (Region_map::Attach_failed) {
warning("could not attach dataspace");
_ds_pool.ram_alloc->free(new_ds_cap);
return 0;

View File

@ -49,11 +49,13 @@ bool Sliced_heap::alloc(size_t size, void **out_addr)
try {
ds_cap = _ram_alloc.alloc(size);
block = _region_map.attach(ds_cap);
} catch (Region_map::Attach_failed) {
}
catch (Region_map::Attach_failed) {
error("could not attach dataspace to local address space");
_ram_alloc.free(ds_cap);
return false;
} catch (Ram_allocator::Alloc_failed) {
}
catch (Out_of_ram) {
error("could not allocate dataspace with size ", size);
return false;
}

View File

@ -65,9 +65,7 @@ void Stack::size(size_t const size)
if (ds_addr != (addr_t)attach_addr)
throw Thread::Out_of_stack_space();
}
catch (Ram_session::Alloc_failed) {
throw Thread::Stack_alloc_failed();
}
catch (Out_of_ram) { throw Thread::Stack_alloc_failed(); }
/* update stack information */
_base -= ds_size;
@ -110,7 +108,7 @@ Thread::_alloc_stack(size_t stack_size, char const *name, bool main_thread)
if (attach_addr != (addr_t)env_stack_area_region_map->attach_at(ds_cap, attach_addr, ds_size))
throw Stack_alloc_failed();
}
catch (Ram_session::Alloc_failed) { throw Stack_alloc_failed(); }
catch (Out_of_ram) { throw Stack_alloc_failed(); }
/*
* Now the stack is backed by memory, so it is safe to access its members.

View File

@ -90,7 +90,7 @@ namespace Allocator {
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::Ram_session::Quota_exceeded) {
} catch (Genode::Out_of_ram) {
warning("backend allocator exhausted");
_quota_exceeded = true;
return false;

View File

@ -55,9 +55,10 @@ int Libc::Mem_alloc_impl::Dataspace_pool::expand(size_t size, Range_allocator *a
try {
new_ds_cap = _ram_session->alloc(size);
local_addr = _region_map->attach(new_ds_cap);
} catch (Ram_session::Alloc_failed) {
return -2;
} catch (Region_map::Attach_failed) {
}
catch (Out_of_ram) { return -2; }
catch (Out_of_caps) { return -4; }
catch (Region_map::Attach_failed) {
_ram_session->free(new_ds_cap);
return -3;
}

View File

@ -83,9 +83,9 @@ class Genode::Dynamic_rom_session : public Rpc_object<Rom_session>
ds_reallocated = true;
}
}
catch (Ram_session::Quota_exceeded) {
catch (Out_of_ram) {
error("ouf of child quota while delivering dynamic ROM");
error("ouf of child RAM quota while delivering dynamic ROM");
/*
* XXX We may try to generate a resource request on
@ -98,10 +98,6 @@ class Genode::Dynamic_rom_session : public Rpc_object<Rom_session>
*/
return true;
}
catch (Ram_session::Out_of_metadata) {
error("ouf of RAM session quota while delivering dynamic ROM");
return true;
}
try {
_content_producer.produce_content(_ds->local_addr<char>(),

View File

@ -96,7 +96,7 @@ class Genode::Ram_session_guard : public Genode::Ram_session
Cache_attribute cached = CACHED) override
{
if (_used + size <= _used || _used + size > _quota)
throw Quota_exceeded();
throw Out_of_ram();
Ram_dataspace_capability cap = _session.alloc(size, cached);

View File

@ -330,7 +330,7 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
/* thrown by 'Quota_reservation' */
catch (Out_of_metadata) { throw; }
/* thrown by 'Device_pd_policy' or 'Child' */
catch (Genode::Ram_session::Alloc_failed) { throw Out_of_metadata(); }
catch (Genode::Out_of_ram) { throw Out_of_metadata(); }
/* throw by 'Slave::Connection' */
catch (Genode::Insufficient_ram_quota) { throw Out_of_metadata(); }
@ -373,7 +373,7 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
}
/* thrown by '_md_alloc' */
catch (Genode::Allocator::Out_of_memory) { throw Out_of_metadata(); }
catch (Genode::Out_of_ram) { throw Out_of_metadata(); }
/* thrown by 'Device_pd' */
catch (Out_of_metadata) { throw; }
@ -944,25 +944,25 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
/* transfer ram quota to session specific ram session */
try { _env_ram.transfer_quota(_ram, Genode::Ram_quota{size}); }
catch (Genode::Out_of_ram) { throw Out_of_metadata(); }
catch (...) { throw Fatal(); }
enum { UPGRADE_QUOTA = 4096 };
/* allocate dataspace from session specific ram session */
Ram_capability ram_cap = Genode::retry<Genode::Ram_session::Quota_exceeded>(
Ram_capability ram_cap = Genode::retry<Genode::Out_of_ram>(
[&] () {
Ram_capability ram = Genode::retry<Genode::Ram_session::Out_of_metadata>(
[&] () { return _ram.alloc(size, Genode::UNCACHED); },
[&] () {
if (!_env_ram.withdraw(UPGRADE_QUOTA)) {
_rollback(size);
}
try {
return _ram.alloc(size, Genode::UNCACHED);
}
catch (Genode::Out_of_ram) {
/* upgrade meta-data quota */
_ram.upgrade_ram(UPGRADE_QUOTA);
});
if (!_env_ram.withdraw(UPGRADE_QUOTA))
_rollback(size);
throw;
}
return ram;
},
[&] () {
/*
@ -973,7 +973,7 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
* UPGRADE_QUOTA steps.
*/
try { _env_ram.transfer_quota(_ram, Genode::Ram_quota{UPGRADE_QUOTA}); }
catch (...) { throw Out_of_metadata(); }
catch (...) { throw Genode::Out_of_ram(); }
});
if (!ram_cap.valid())

View File

@ -297,7 +297,7 @@ void Init::Main::_handle_config()
if (used_ram.value > avail_ram.value) {
error("RAM exhausted while starting childen");
throw Ram_session::Alloc_failed();
throw Out_of_ram();
}
try {
@ -326,8 +326,6 @@ void Init::Main::_handle_config()
}
catch (Out_of_ram) {
warning("memory exhausted during child creation"); }
catch (Ram_session::Alloc_failed) {
warning("failed to allocate memory during child construction"); }
catch (Child::Missing_name_attribute) {
warning("skipped startup of nameless child"); }
catch (Region_map::Attach_failed) {

View File

@ -220,7 +220,7 @@ class Net::Root : public Genode::Root_component<Net::Session_component>
} catch (Mac_allocator::Alloc_failed) {
Genode::warning("Mac address allocation failed!");
throw Root::Unavailable();
} catch(Ram_session::Quota_exceeded) {
} catch (Out_of_ram) {
Genode::warning("insufficient 'ram_quota'");
throw Insufficient_ram_quota();
}