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:
Norman Feske
2021-11-08 21:05:11 +01:00
committed by Christian Helmuth
parent 959bcae557
commit 231ac187fe
20 changed files with 324 additions and 122 deletions

View File

@ -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) { }
};

View File

@ -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); });
}
};

View File

@ -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 &quota_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;
}
};

View File

@ -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

View File

@ -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 **
***************************************************/

View File

@ -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); }

View File

@ -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);
};