mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 02:40:08 +00:00
base: introduce Ram_allocator::try_alloc
This patch replaces the 'Ram_allocator::alloc' RPC function by a 'try_alloc' function, which reflects errors as 'Attempt' return value instead of an exception. Issue #4322 Issue #3612
This commit is contained in:
parent
959bcae557
commit
231ac187fe
@ -79,7 +79,7 @@ class Stack_area_region_map : public Genode::Region_map
|
||||
|
||||
struct Stack_area_ram_allocator : Genode::Ram_allocator
|
||||
{
|
||||
Genode::Ram_dataspace_capability alloc(Genode::size_t, Genode::Cache) override {
|
||||
Alloc_result try_alloc(Genode::size_t, Genode::Cache) override {
|
||||
return Genode::Ram_dataspace_capability(); }
|
||||
|
||||
void free(Genode::Ram_dataspace_capability) override { }
|
||||
|
@ -114,7 +114,7 @@ class Stack_area_region_map : public Region_map
|
||||
|
||||
struct Stack_area_ram_allocator : Ram_allocator
|
||||
{
|
||||
Ram_dataspace_capability alloc(size_t, Cache) override {
|
||||
Alloc_result try_alloc(size_t, Cache) override {
|
||||
return reinterpret_cap_cast<Ram_dataspace>(Native_capability()); }
|
||||
|
||||
void free(Ram_dataspace_capability) override {
|
||||
|
@ -14,10 +14,11 @@
|
||||
#ifndef _INCLUDE__BASE__IPC_H_
|
||||
#define _INCLUDE__BASE__IPC_H_
|
||||
|
||||
#include <util/attempt.h>
|
||||
#include <util/meta.h>
|
||||
#include <base/ipc_msgbuf.h>
|
||||
#include <base/rpc_args.h>
|
||||
#include <base/log.h>
|
||||
#include <util/meta.h>
|
||||
|
||||
namespace Genode {
|
||||
|
||||
@ -156,6 +157,18 @@ class Genode::Ipc_unmarshaller : Noncopyable
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read 'Attempt' return value from buffer
|
||||
*/
|
||||
template <typename RESULT, typename ERROR>
|
||||
Attempt<RESULT, ERROR> extract(Meta::Overload_selector<Attempt<RESULT, ERROR> >)
|
||||
{
|
||||
bool const ok = extract(Meta::Overload_selector<bool>());
|
||||
|
||||
if (ok) return extract(Meta::Overload_selector<RESULT>());
|
||||
else return extract(Meta::Overload_selector<ERROR>());
|
||||
}
|
||||
|
||||
Ipc_unmarshaller(Msgbuf_base &rcv_msg) : _rcv_msg(rcv_msg) { }
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#ifndef _INCLUDE__BASE__IPC_MSGBUF_H_
|
||||
#define _INCLUDE__BASE__IPC_MSGBUF_H_
|
||||
|
||||
#include <util/attempt.h>
|
||||
#include <util/noncopyable.h>
|
||||
#include <base/capability.h>
|
||||
#include <base/exception.h>
|
||||
@ -225,6 +226,17 @@ class Genode::Msgbuf_base : Noncopyable
|
||||
Native_capability untyped_cap = typed_cap;
|
||||
insert(untyped_cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert 'Attempt' return value into message buffer
|
||||
*/
|
||||
template <typename RESULT, typename ERROR>
|
||||
void insert(Attempt<RESULT, ERROR> const &attempt)
|
||||
{
|
||||
insert(attempt.ok());
|
||||
attempt.with_result([&] (RESULT result) { insert(result); },
|
||||
[&] (ERROR error) { insert(error); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -40,6 +40,11 @@ namespace Genode {
|
||||
class Quota_guard_untyped;
|
||||
|
||||
template <typename> class Quota_guard;
|
||||
|
||||
struct Reservation : Interface
|
||||
{
|
||||
virtual void cancel() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -121,6 +126,38 @@ class Genode::Quota_guard_untyped
|
||||
/* clamp lower bound of used value to zero */
|
||||
_used = underflow ? 0 : _used - amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guard for rolling back a quota reservation
|
||||
*/
|
||||
class Reservation_guard : public Reservation, Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
Quota_guard_untyped &_quota_guard;
|
||||
|
||||
size_t const _amount;
|
||||
|
||||
bool _canceled = false;
|
||||
|
||||
public:
|
||||
|
||||
Reservation_guard(Quota_guard_untyped "a_guard, size_t const amount)
|
||||
:
|
||||
_quota_guard(quota_guard), _amount(amount)
|
||||
{ }
|
||||
|
||||
~Reservation_guard()
|
||||
{
|
||||
if (_canceled)
|
||||
_quota_guard.replenish(_amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reservation interface
|
||||
*/
|
||||
void cancel() override { _canceled = true; }
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -179,6 +216,11 @@ class Genode::Quota_guard
|
||||
|
||||
/**
|
||||
* Utility used for transactional multi-step resource allocations
|
||||
*
|
||||
* \deprecated Use 'with_reservation' instead
|
||||
*
|
||||
* Note that this class is not related to the 'Genode::Reservation'
|
||||
* interface.
|
||||
*/
|
||||
struct Reservation
|
||||
{
|
||||
@ -206,6 +248,33 @@ class Genode::Quota_guard
|
||||
|
||||
void acknowledge() { _ack = true; }
|
||||
};
|
||||
|
||||
template <typename RET, typename FN, typename ERROR_FN>
|
||||
RET with_reservation(UNIT const amount,
|
||||
FN const &fn,
|
||||
ERROR_FN const &error_fn)
|
||||
{
|
||||
if (!_guard.try_withdraw(amount.value))
|
||||
return error_fn();
|
||||
|
||||
/*
|
||||
* The withdrawal was successful. Use reservation guard to
|
||||
* rollback the withdrawal depending on 'fn'.
|
||||
*/
|
||||
|
||||
Quota_guard_untyped::Reservation_guard
|
||||
reservation_guard { _guard, amount.value };
|
||||
|
||||
/* expose only the 'Reservation' interface to 'fn' */
|
||||
::Genode::Reservation &interface = reservation_guard;
|
||||
|
||||
return fn(interface);
|
||||
}
|
||||
|
||||
bool have_avail(UNIT const amount) const
|
||||
{
|
||||
return _guard.avail() >= amount.value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#ifndef _INCLUDE__BASE__RAM_ALLOCATOR_H_
|
||||
#define _INCLUDE__BASE__RAM_ALLOCATOR_H_
|
||||
|
||||
#include <util/attempt.h>
|
||||
#include <base/capability.h>
|
||||
#include <base/quota_guard.h>
|
||||
#include <base/cache.h>
|
||||
@ -33,6 +34,23 @@ namespace Genode {
|
||||
|
||||
struct Genode::Ram_allocator : Interface
|
||||
{
|
||||
enum class Alloc_error { OUT_OF_RAM, OUT_OF_CAPS, DENIED };
|
||||
|
||||
using Alloc_result = Attempt<Ram_dataspace_capability, Alloc_error>;
|
||||
|
||||
struct Denied : Exception { };
|
||||
|
||||
/**
|
||||
* Allocate RAM dataspace
|
||||
*
|
||||
* \param size size of RAM dataspace
|
||||
* \param cache selects cacheability attributes of the memory,
|
||||
* uncached memory, i.e., for DMA buffers
|
||||
*
|
||||
* \return capability to RAM dataspace, or error code of type 'Alloc_error'
|
||||
*/
|
||||
virtual Alloc_result try_alloc(size_t size, Cache cache = CACHED) = 0;
|
||||
|
||||
/**
|
||||
* Allocate RAM dataspace
|
||||
*
|
||||
@ -42,10 +60,25 @@ struct Genode::Ram_allocator : Interface
|
||||
*
|
||||
* \throw Out_of_ram
|
||||
* \throw Out_of_caps
|
||||
* \throw Denied
|
||||
*
|
||||
* \return capability to new RAM dataspace
|
||||
*/
|
||||
virtual Ram_dataspace_capability alloc(size_t size, Cache cache = CACHED) = 0;
|
||||
Ram_dataspace_capability alloc(size_t size, Cache cache = CACHED)
|
||||
{
|
||||
return try_alloc(size, cache).convert<Ram_dataspace_capability>(
|
||||
[&] (Ram_dataspace_capability cap) {
|
||||
return cap; },
|
||||
|
||||
[&] (Alloc_error error) -> Ram_dataspace_capability {
|
||||
switch (error) {
|
||||
case Alloc_error::OUT_OF_RAM: throw Out_of_ram();
|
||||
case Alloc_error::OUT_OF_CAPS: throw Out_of_caps();
|
||||
case Alloc_error::DENIED: break;
|
||||
}
|
||||
throw Denied();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Free RAM dataspace
|
||||
@ -57,7 +90,7 @@ struct Genode::Ram_allocator : Interface
|
||||
/**
|
||||
* Return size of dataspace in bytes
|
||||
*/
|
||||
virtual size_t dataspace_size(Ram_dataspace_capability ds) const = 0;
|
||||
virtual size_t dataspace_size(Ram_dataspace_capability) const = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -81,22 +114,44 @@ class Genode::Constrained_ram_allocator : public Ram_allocator
|
||||
_ram_alloc(ram_alloc), _ram_guard(ram_guard), _cap_guard(cap_guard)
|
||||
{ }
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache cache = CACHED) override
|
||||
Alloc_result try_alloc(size_t size, Cache cache = CACHED) override
|
||||
{
|
||||
size_t page_aligned_size = align_addr(size, 12);
|
||||
using Result = Alloc_result;
|
||||
|
||||
Ram_quota_guard::Reservation ram (_ram_guard, Ram_quota{page_aligned_size});
|
||||
Cap_quota_guard::Reservation caps(_cap_guard, Cap_quota{1});
|
||||
size_t const page_aligned_size = align_addr(size, 12);
|
||||
|
||||
/*
|
||||
* \throw Out_of_caps, Out_of_ram
|
||||
*/
|
||||
Ram_dataspace_capability ds = _ram_alloc.alloc(page_aligned_size, cache);
|
||||
Ram_quota const needed_ram { page_aligned_size };
|
||||
Cap_quota const needed_caps { 1 };
|
||||
|
||||
ram. acknowledge();
|
||||
caps.acknowledge();
|
||||
return _ram_guard.with_reservation<Result>(needed_ram,
|
||||
|
||||
return ds;
|
||||
[&] (Reservation &ram_reservation) {
|
||||
|
||||
return _cap_guard.with_reservation<Result>(needed_caps,
|
||||
|
||||
[&] (Reservation &cap_reservation) -> Result {
|
||||
|
||||
return _ram_alloc.try_alloc(page_aligned_size, cache)
|
||||
.convert<Result>(
|
||||
|
||||
[&] (Ram_dataspace_capability ds) -> Result {
|
||||
return ds; },
|
||||
|
||||
[&] (Alloc_error error) {
|
||||
cap_reservation.cancel();
|
||||
ram_reservation.cancel();
|
||||
return error; }
|
||||
);
|
||||
},
|
||||
[&] () -> Result {
|
||||
ram_reservation.cancel();
|
||||
return Alloc_error::OUT_OF_CAPS;
|
||||
}
|
||||
);
|
||||
},
|
||||
[&] () -> Result {
|
||||
return Alloc_error::OUT_OF_RAM; }
|
||||
);
|
||||
}
|
||||
|
||||
void free(Ram_dataspace_capability ds) override
|
||||
|
@ -21,8 +21,8 @@ namespace Genode {
|
||||
|
||||
template <typename> struct Rpc_client;
|
||||
|
||||
/**
|
||||
* Count capabilities of a RPC_FUNCTION which are out parameters.
|
||||
/*
|
||||
* Number of capabilities received by RPC_FUNCTION as out parameters
|
||||
*/
|
||||
template <typename T> struct Cap_para_out { enum { Value = 0 }; };
|
||||
template <typename T> struct Cap_para_out<Capability<T> *> { enum { Value = 1 }; };
|
||||
@ -30,6 +30,9 @@ namespace Genode {
|
||||
template <> struct Cap_para_out<Native_capability *> { enum { Value = 1 }; };
|
||||
template <> struct Cap_para_out<Native_capability &> { enum { Value = 1 }; };
|
||||
|
||||
/*
|
||||
* Presence of capability received as return value from RPC_FUNCTION
|
||||
*/
|
||||
template <typename T> struct Cap_return { enum { Value = 0 }; };
|
||||
template <typename T> struct Cap_return<Capability<T> > { enum { Value = 1 }; };
|
||||
template <typename T> struct Cap_return<Capability<T> *> { enum { Value = 1 }; };
|
||||
@ -38,11 +41,17 @@ namespace Genode {
|
||||
template <> struct Cap_return<Native_capability *> { enum { Value = 1 }; };
|
||||
template <> struct Cap_return<Native_capability &> { enum { Value = 1 }; };
|
||||
|
||||
/*
|
||||
* Presence of capability received as 'Attempt' return value from RPC_FUNCTION
|
||||
*/
|
||||
template <typename T, typename E>
|
||||
struct Cap_return<Attempt<T, E> > { enum { Value = Cap_return<T>::Value }; };
|
||||
|
||||
template <typename ARGS>
|
||||
struct Rpc_caps_out {
|
||||
enum { Value = Cap_para_out<typename ARGS::Head>::Value
|
||||
+ Rpc_caps_out<typename ARGS::Tail>::Value }; };
|
||||
|
||||
|
||||
template <>
|
||||
struct Rpc_caps_out<Meta::Empty> { enum { Value = 0 }; };
|
||||
|
||||
@ -51,6 +60,7 @@ namespace Genode {
|
||||
enum { Value = Rpc_caps_out<typename RPC_FUNCTION::Server_args>::Value +
|
||||
Cap_return <typename RPC_FUNCTION::Ret_type>::Value}; };
|
||||
|
||||
|
||||
/***************************************************
|
||||
** Implementation of 'Capability:call' functions **
|
||||
***************************************************/
|
||||
|
@ -73,9 +73,9 @@ struct Genode::Pd_session_client : Rpc_client<Pd_session>
|
||||
Cap_quota cap_quota() const override { return call<Rpc_cap_quota>(); }
|
||||
Cap_quota used_caps() const override { return call<Rpc_used_caps>(); }
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache cache = CACHED) override
|
||||
Alloc_result try_alloc(size_t size, Cache cache = CACHED) override
|
||||
{
|
||||
return call<Rpc_alloc>(size, cache);
|
||||
return call<Rpc_try_alloc>(size, cache);
|
||||
}
|
||||
|
||||
void free(Ram_dataspace_capability ds) override { call<Rpc_free>(ds); }
|
||||
|
@ -15,6 +15,7 @@
|
||||
#ifndef _INCLUDE__PD_SESSION__PD_SESSION_H_
|
||||
#define _INCLUDE__PD_SESSION__PD_SESSION_H_
|
||||
|
||||
#include <util/attempt.h>
|
||||
#include <base/exception.h>
|
||||
#include <cpu/cpu_state.h>
|
||||
#include <session/session.h>
|
||||
@ -348,9 +349,7 @@ struct Genode::Pd_session : Session, Ram_allocator
|
||||
GENODE_RPC(Rpc_cap_quota, Cap_quota, cap_quota);
|
||||
GENODE_RPC(Rpc_used_caps, Cap_quota, used_caps);
|
||||
|
||||
GENODE_RPC_THROW(Rpc_alloc, Ram_dataspace_capability, alloc,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps, Undefined_ref_account),
|
||||
size_t, Cache);
|
||||
GENODE_RPC(Rpc_try_alloc, Alloc_result, try_alloc, size_t, Cache);
|
||||
GENODE_RPC(Rpc_free, void, free, Ram_dataspace_capability);
|
||||
GENODE_RPC_THROW(Rpc_transfer_ram_quota, void, transfer_quota,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Invalid_session, Undefined_ref_account),
|
||||
@ -369,7 +368,7 @@ struct Genode::Pd_session : Session, Ram_allocator
|
||||
Rpc_alloc_rpc_cap, Rpc_free_rpc_cap, Rpc_address_space,
|
||||
Rpc_stack_area, Rpc_linker_area, Rpc_ref_account,
|
||||
Rpc_transfer_cap_quota, Rpc_cap_quota, Rpc_used_caps,
|
||||
Rpc_alloc, Rpc_free,
|
||||
Rpc_try_alloc, Rpc_free,
|
||||
Rpc_transfer_ram_quota, Rpc_ram_quota, Rpc_used_ram,
|
||||
Rpc_native_pd, Rpc_managing_system);
|
||||
};
|
||||
|
@ -315,7 +315,7 @@ class Genode::Pd_session_component : public Session_object<Pd_session>
|
||||
** RAM allocation and accounting **
|
||||
***********************************/
|
||||
|
||||
Ram_dataspace_capability alloc(size_t, Cache) override;
|
||||
Alloc_result try_alloc(size_t, Cache) override;
|
||||
|
||||
void free(Ram_dataspace_capability) override;
|
||||
|
||||
|
@ -106,7 +106,7 @@ class Genode::Ram_dataspace_factory : public Ram_allocator,
|
||||
** Ram_allocator interface **
|
||||
*****************************/
|
||||
|
||||
Ram_dataspace_capability alloc(size_t, Cache) override;
|
||||
Alloc_result try_alloc(size_t, Cache) override;
|
||||
void free(Ram_dataspace_capability) override;
|
||||
size_t dataspace_size(Ram_dataspace_capability ds) const override;
|
||||
};
|
||||
|
@ -33,10 +33,10 @@ class Genode::Synced_ram_allocator : public Ram_allocator
|
||||
|
||||
Synced_ram_allocator(Ram_allocator &alloc) : _alloc(alloc) { }
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache cache) override
|
||||
Alloc_result try_alloc(size_t size, Cache cache) override
|
||||
{
|
||||
Mutex::Guard mutex_guard(_mutex);
|
||||
return _alloc.alloc(size, cache);
|
||||
return _alloc.try_alloc(size, cache);
|
||||
}
|
||||
|
||||
void free(Ram_dataspace_capability ds) override
|
||||
|
@ -20,57 +20,57 @@
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Ram_dataspace_capability
|
||||
Pd_session_component::alloc(size_t ds_size, Cache cache)
|
||||
Ram_allocator::Alloc_result
|
||||
Pd_session_component::try_alloc(size_t ds_size, Cache cache)
|
||||
{
|
||||
/* zero-sized dataspaces are not allowed */
|
||||
if (!ds_size) return Ram_dataspace_capability();
|
||||
if (!ds_size)
|
||||
return Alloc_error::DENIED;
|
||||
|
||||
/* dataspace allocation granularity is page size */
|
||||
ds_size = align_addr(ds_size, 12);
|
||||
|
||||
/*
|
||||
* Track quota usage
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
Ram_quota_guard::Reservation
|
||||
dataspace_ram_costs(_ram_quota_guard(), Ram_quota{ds_size});
|
||||
using Result = Ram_allocator::Alloc_result;
|
||||
using Reservation = Genode::Reservation;
|
||||
|
||||
/*
|
||||
* 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 const overhead { Ram_dataspace_factory::SLAB_BLOCK_SIZE };
|
||||
Ram_quota_guard::Reservation sbs_ram_costs(_ram_quota_guard(), overhead);
|
||||
}
|
||||
/* track quota use */
|
||||
return _ram_quota_guard().with_reservation<Result>(Ram_quota{ds_size},
|
||||
|
||||
/*
|
||||
* Each dataspace is an RPC object and thereby consumes a capability.
|
||||
*/
|
||||
Cap_quota_guard::Reservation
|
||||
dataspace_cap_costs(_cap_quota_guard(), Cap_quota{1});
|
||||
[&] (Reservation &ram_reservation) -> Result {
|
||||
|
||||
/*
|
||||
* Allocate physical dataspace
|
||||
*
|
||||
* \throw Out_of_ram
|
||||
* \throw Out_of_caps
|
||||
*/
|
||||
Ram_dataspace_capability ram_ds = _ram_ds_factory.alloc(ds_size, cache);
|
||||
/*
|
||||
* 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 const overhead { Ram_dataspace_factory::SLAB_BLOCK_SIZE };
|
||||
|
||||
/*
|
||||
* We returned from '_ram_ds_factory.alloc' with a valid dataspace.
|
||||
*/
|
||||
dataspace_ram_costs.acknowledge();
|
||||
dataspace_cap_costs.acknowledge();
|
||||
if (!_ram_quota_guard().have_avail(overhead)) {
|
||||
ram_reservation.cancel();
|
||||
return Ram_allocator::Alloc_error::OUT_OF_RAM;
|
||||
}
|
||||
|
||||
return ram_ds;
|
||||
/*
|
||||
* Each dataspace is an RPC object and thereby consumes a
|
||||
* capability.
|
||||
*/
|
||||
return _cap_quota_guard().with_reservation<Result>(Cap_quota{1},
|
||||
|
||||
[&] (Genode::Reservation &) -> Result {
|
||||
return _ram_ds_factory.try_alloc(ds_size, cache);
|
||||
},
|
||||
[&] () -> Result {
|
||||
ram_reservation.cancel();
|
||||
return Ram_allocator::Alloc_error::OUT_OF_CAPS;
|
||||
}
|
||||
);
|
||||
},
|
||||
[&] () -> Result {
|
||||
return Ram_allocator::Alloc_error::OUT_OF_RAM;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,11 +20,12 @@
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
Ram_dataspace_capability
|
||||
Ram_dataspace_factory::alloc(size_t ds_size, Cache cache)
|
||||
Ram_allocator::Alloc_result
|
||||
Ram_dataspace_factory::try_alloc(size_t ds_size, Cache cache)
|
||||
{
|
||||
/* zero-sized dataspaces are not allowed */
|
||||
if (!ds_size) return Ram_dataspace_capability();
|
||||
if (!ds_size)
|
||||
return Alloc_error::DENIED;
|
||||
|
||||
/* dataspace allocation granularity is page size */
|
||||
ds_size = align_addr(ds_size, 12);
|
||||
@ -59,7 +60,7 @@ Ram_dataspace_factory::alloc(size_t ds_size, Cache cache)
|
||||
}
|
||||
}
|
||||
|
||||
/* apply constraints or re-try because higher memory allocation failed */
|
||||
/* apply constraints, or retry if larger memory allocation failed */
|
||||
if (!alloc_succeeded) {
|
||||
for (size_t align_log2 = log2(ds_size); align_log2 >= 12; align_log2--) {
|
||||
if (_phys_alloc.alloc_aligned(ds_size, &ds_addr, align_log2,
|
||||
@ -106,19 +107,24 @@ Ram_dataspace_factory::alloc(size_t ds_size, Cache cache)
|
||||
if (!alloc_succeeded) {
|
||||
error("out of physical memory while allocating ", ds_size, " bytes ",
|
||||
"in range [", Hex(_phys_range.start), "-", Hex(_phys_range.end), "]");
|
||||
throw Out_of_ram();
|
||||
return Alloc_error::OUT_OF_RAM;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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, cache, true, this);
|
||||
Dataspace_component *ds_ptr = nullptr;
|
||||
try {
|
||||
ds_ptr = new (_ds_slab)
|
||||
Dataspace_component(ds_size, (addr_t)ds_addr, cache, true, this);
|
||||
}
|
||||
catch (Out_of_ram) { return Alloc_error::OUT_OF_RAM; }
|
||||
catch (Out_of_caps) { return Alloc_error::OUT_OF_CAPS; }
|
||||
catch (...) { return Alloc_error::DENIED; }
|
||||
|
||||
Dataspace_component &ds = *ds_ptr;
|
||||
|
||||
/* create native shared memory representation of dataspace */
|
||||
try { _export_ram_ds(ds); }
|
||||
@ -127,7 +133,7 @@ Ram_dataspace_factory::alloc(size_t ds_size, Cache cache)
|
||||
|
||||
/* cleanup unneeded resources */
|
||||
destroy(_ds_slab, &ds);
|
||||
throw Out_of_ram();
|
||||
return Alloc_error::DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -137,11 +143,11 @@ Ram_dataspace_factory::alloc(size_t ds_size, Cache cache)
|
||||
*/
|
||||
_clear_ds(ds);
|
||||
|
||||
Dataspace_capability result = _ep.manage(&ds);
|
||||
Dataspace_capability ds_cap = _ep.manage(&ds);
|
||||
|
||||
phys_alloc_guard.ack = true;
|
||||
|
||||
return static_cap_cast<Ram_dataspace>(result);
|
||||
return static_cap_cast<Ram_dataspace>(ds_cap);
|
||||
}
|
||||
|
||||
|
||||
|
@ -121,7 +121,7 @@ class Stack_area_region_map : public Region_map
|
||||
|
||||
struct Stack_area_ram_allocator : Ram_allocator
|
||||
{
|
||||
Ram_dataspace_capability alloc(size_t, Cache) override {
|
||||
Alloc_result try_alloc(size_t, Cache) override {
|
||||
return reinterpret_cap_cast<Ram_dataspace>(Native_capability()); }
|
||||
|
||||
void free(Ram_dataspace_capability) override { }
|
||||
|
@ -38,36 +38,49 @@ struct Genode::Expanding_pd_session_client : Pd_session_client
|
||||
Expanding_pd_session_client(Parent &parent, Pd_session_capability cap)
|
||||
: Pd_session_client(cap), _parent(parent) { }
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache cache = UNCACHED) override
|
||||
Alloc_result try_alloc(size_t size, Cache cache = UNCACHED) override
|
||||
{
|
||||
/*
|
||||
* If the RAM session runs out of quota, issue a resource request
|
||||
* If the PD session runs out of quota, issue a resource request
|
||||
* to the parent and retry.
|
||||
*/
|
||||
enum { NUM_ATTEMPTS = 10 };
|
||||
enum { UPGRADE_CAPS = 4 };
|
||||
return retry<Out_of_ram>(
|
||||
[&] () {
|
||||
return retry<Out_of_caps>(
|
||||
[&] () { return Pd_session_client::alloc(size, cache); },
|
||||
[&] () { _request_caps_from_parent(UPGRADE_CAPS); },
|
||||
NUM_ATTEMPTS);
|
||||
},
|
||||
[&] () {
|
||||
/*
|
||||
* The RAM service withdraws the meta data for the allocator
|
||||
* from the RAM quota. In the worst case, a new slab block
|
||||
* may be needed. To cover the worst case, we need to take
|
||||
* this possible overhead into account when requesting
|
||||
* additional RAM quota from the parent.
|
||||
*
|
||||
* Because the worst case almost never happens, we request
|
||||
* a bit too much quota for the most time.
|
||||
*/
|
||||
enum { OVERHEAD = 4096UL };
|
||||
_request_ram_from_parent(size + OVERHEAD);
|
||||
},
|
||||
NUM_ATTEMPTS);
|
||||
for (;;) {
|
||||
Alloc_result const result = Pd_session_client::try_alloc(size, cache);
|
||||
if (result.ok())
|
||||
return result;
|
||||
|
||||
bool denied = false;
|
||||
result.with_error(
|
||||
[&] (Alloc_error error) {
|
||||
switch (error) {
|
||||
|
||||
case Alloc_error::OUT_OF_RAM:
|
||||
/*
|
||||
* The RAM service withdraws the meta data for the allocator
|
||||
* from the RAM quota. In the worst case, a new slab block
|
||||
* may be needed. To cover the worst case, we need to take
|
||||
* this possible overhead into account when requesting
|
||||
* additional RAM quota from the parent.
|
||||
*
|
||||
* Because the worst case almost never happens, we request
|
||||
* a bit too much quota for the most time.
|
||||
*/
|
||||
enum { OVERHEAD = 4096UL };
|
||||
_request_ram_from_parent(size + OVERHEAD);
|
||||
break;
|
||||
|
||||
case Alloc_error::OUT_OF_CAPS:
|
||||
_request_caps_from_parent(4);
|
||||
break;
|
||||
|
||||
case Alloc_error::DENIED:
|
||||
denied = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (denied)
|
||||
return Alloc_error::DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
void transfer_quota(Pd_session_capability pd_session, Ram_quota amount) override
|
||||
|
@ -46,8 +46,20 @@ bool Sliced_heap::alloc(size_t size, void **out_addr)
|
||||
Ram_dataspace_capability ds_cap;
|
||||
Block *block = nullptr;
|
||||
|
||||
_ram_alloc.try_alloc(size).with_result(
|
||||
[&] (Ram_dataspace_capability cap) { ds_cap = cap; },
|
||||
[&] (Ram_allocator::Alloc_error error) {
|
||||
switch (error) {
|
||||
case Ram_allocator::Alloc_error::OUT_OF_CAPS: throw Out_of_caps();
|
||||
case Ram_allocator::Alloc_error::OUT_OF_RAM: break;
|
||||
case Ram_allocator::Alloc_error::DENIED: break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!ds_cap.valid())
|
||||
return false;
|
||||
|
||||
try {
|
||||
ds_cap = _ram_alloc.alloc(size);
|
||||
block = _region_map.attach(ds_cap);
|
||||
}
|
||||
catch (Region_map::Region_conflict) {
|
||||
|
@ -53,11 +53,16 @@ struct Libc::Malloc_ram_allocator : Ram_allocator
|
||||
_release(ds); });
|
||||
}
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache cache) override
|
||||
Alloc_result try_alloc(size_t size, Cache cache) override
|
||||
{
|
||||
Ram_dataspace_capability cap = _ram.alloc(size, cache);
|
||||
new (_md_alloc) Registered<Dataspace>(_dataspaces, cap);
|
||||
return cap;
|
||||
return _ram.try_alloc(size, cache).convert<Alloc_result>(
|
||||
|
||||
[&] (Ram_dataspace_capability cap) {
|
||||
new (_md_alloc) Registered<Dataspace>(_dataspaces, cap);
|
||||
return cap; },
|
||||
|
||||
[&] (Alloc_error error) {
|
||||
return error; });
|
||||
}
|
||||
|
||||
void free(Ram_dataspace_capability ds_cap) override
|
||||
|
@ -113,18 +113,26 @@ class Genode::Session_env : public Ram_allocator,
|
||||
** Ram_allocator **
|
||||
*******************/
|
||||
|
||||
Ram_dataspace_capability alloc(size_t size, Cache cache) override
|
||||
Alloc_result try_alloc(size_t size, Cache cache) override
|
||||
{
|
||||
enum { MAX_SHARED_CAP = 1 };
|
||||
enum { MAX_SHARED_RAM = 4096 };
|
||||
enum { DS_SIZE_GRANULARITY_LOG2 = 12 };
|
||||
|
||||
Alloc_result result = Alloc_error::DENIED;
|
||||
|
||||
size_t const ds_size = align_addr(size, DS_SIZE_GRANULARITY_LOG2);
|
||||
Ram_dataspace_capability ds;
|
||||
_consume(ds_size, MAX_SHARED_RAM, 1, MAX_SHARED_CAP, [&] () {
|
||||
ds = _env.pd().alloc(ds_size, cache);
|
||||
});
|
||||
return ds;
|
||||
|
||||
try {
|
||||
_consume(ds_size, MAX_SHARED_RAM, 1, MAX_SHARED_CAP, [&] ()
|
||||
{
|
||||
result = _env.pd().try_alloc(ds_size, cache);
|
||||
});
|
||||
}
|
||||
catch (Out_of_ram) { result = Alloc_error::OUT_OF_RAM; }
|
||||
catch (Out_of_caps) { result = Alloc_error::OUT_OF_CAPS; }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,8 +128,8 @@ class Gdb_monitor::Pd_session_component : public Rpc_object<Pd_session>
|
||||
Cap_quota cap_quota() const override { return _pd.cap_quota(); }
|
||||
Cap_quota used_caps() const override { return _pd.used_caps(); }
|
||||
|
||||
Ram_dataspace_capability alloc(size_t amount, Cache cache) override {
|
||||
return _pd.alloc(amount, cache); }
|
||||
Alloc_result try_alloc(size_t amount, Cache cache) override {
|
||||
return _pd.try_alloc(amount, cache); }
|
||||
|
||||
void free(Ram_dataspace_capability ds) override { _pd.free(ds); }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user