diff --git a/repos/base-linux/src/core/stack_area.cc b/repos/base-linux/src/core/stack_area.cc index 261fd045d5..fc43ca216a 100644 --- a/repos/base-linux/src/core/stack_area.cc +++ b/repos/base-linux/src/core/stack_area.cc @@ -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 { } diff --git a/repos/base-sel4/src/core/stack_area.cc b/repos/base-sel4/src/core/stack_area.cc index 93bceb735c..ac0bebe97b 100644 --- a/repos/base-sel4/src/core/stack_area.cc +++ b/repos/base-sel4/src/core/stack_area.cc @@ -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(Native_capability()); } void free(Ram_dataspace_capability) override { diff --git a/repos/base/include/base/ipc.h b/repos/base/include/base/ipc.h index 9d227a2b13..ccc07c9892 100644 --- a/repos/base/include/base/ipc.h +++ b/repos/base/include/base/ipc.h @@ -14,10 +14,11 @@ #ifndef _INCLUDE__BASE__IPC_H_ #define _INCLUDE__BASE__IPC_H_ +#include +#include #include #include #include -#include namespace Genode { @@ -156,6 +157,18 @@ class Genode::Ipc_unmarshaller : Noncopyable return value; } + /** + * Read 'Attempt' return value from buffer + */ + template + Attempt extract(Meta::Overload_selector >) + { + bool const ok = extract(Meta::Overload_selector()); + + if (ok) return extract(Meta::Overload_selector()); + else return extract(Meta::Overload_selector()); + } + Ipc_unmarshaller(Msgbuf_base &rcv_msg) : _rcv_msg(rcv_msg) { } }; diff --git a/repos/base/include/base/ipc_msgbuf.h b/repos/base/include/base/ipc_msgbuf.h index dd8dba142f..c4059c3fc0 100644 --- a/repos/base/include/base/ipc_msgbuf.h +++ b/repos/base/include/base/ipc_msgbuf.h @@ -14,6 +14,7 @@ #ifndef _INCLUDE__BASE__IPC_MSGBUF_H_ #define _INCLUDE__BASE__IPC_MSGBUF_H_ +#include #include #include #include @@ -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 + void insert(Attempt const &attempt) + { + insert(attempt.ok()); + attempt.with_result([&] (RESULT result) { insert(result); }, + [&] (ERROR error) { insert(error); }); + } }; diff --git a/repos/base/include/base/quota_guard.h b/repos/base/include/base/quota_guard.h index edcf526aec..f12105e631 100644 --- a/repos/base/include/base/quota_guard.h +++ b/repos/base/include/base/quota_guard.h @@ -40,6 +40,11 @@ namespace Genode { class Quota_guard_untyped; template 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 + 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; + } }; diff --git a/repos/base/include/base/ram_allocator.h b/repos/base/include/base/ram_allocator.h index 591434796f..46225e4592 100644 --- a/repos/base/include/base/ram_allocator.h +++ b/repos/base/include/base/ram_allocator.h @@ -14,6 +14,7 @@ #ifndef _INCLUDE__BASE__RAM_ALLOCATOR_H_ #define _INCLUDE__BASE__RAM_ALLOCATOR_H_ +#include #include #include #include @@ -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; + + 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 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(needed_ram, - return ds; + [&] (Reservation &ram_reservation) { + + return _cap_guard.with_reservation(needed_caps, + + [&] (Reservation &cap_reservation) -> Result { + + return _ram_alloc.try_alloc(page_aligned_size, cache) + .convert( + + [&] (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 diff --git a/repos/base/include/base/rpc_client.h b/repos/base/include/base/rpc_client.h index 7cd9fb34e4..491a28f3d0 100644 --- a/repos/base/include/base/rpc_client.h +++ b/repos/base/include/base/rpc_client.h @@ -21,8 +21,8 @@ namespace Genode { template struct Rpc_client; - /** - * Count capabilities of a RPC_FUNCTION which are out parameters. + /* + * Number of capabilities received by RPC_FUNCTION as out parameters */ template struct Cap_para_out { enum { Value = 0 }; }; template struct Cap_para_out *> { enum { Value = 1 }; }; @@ -30,6 +30,9 @@ namespace Genode { template <> struct Cap_para_out { enum { Value = 1 }; }; template <> struct Cap_para_out { enum { Value = 1 }; }; + /* + * Presence of capability received as return value from RPC_FUNCTION + */ template struct Cap_return { enum { Value = 0 }; }; template struct Cap_return > { enum { Value = 1 }; }; template struct Cap_return *> { enum { Value = 1 }; }; @@ -38,11 +41,17 @@ namespace Genode { template <> struct Cap_return { enum { Value = 1 }; }; template <> struct Cap_return { enum { Value = 1 }; }; + /* + * Presence of capability received as 'Attempt' return value from RPC_FUNCTION + */ + template + struct Cap_return > { enum { Value = Cap_return::Value }; }; + template struct Rpc_caps_out { enum { Value = Cap_para_out::Value + Rpc_caps_out::Value }; }; - + template <> struct Rpc_caps_out { enum { Value = 0 }; }; @@ -51,6 +60,7 @@ namespace Genode { enum { Value = Rpc_caps_out::Value + Cap_return ::Value}; }; + /*************************************************** ** Implementation of 'Capability:call' functions ** ***************************************************/ diff --git a/repos/base/include/pd_session/client.h b/repos/base/include/pd_session/client.h index 393c7d80d7..70239720ab 100644 --- a/repos/base/include/pd_session/client.h +++ b/repos/base/include/pd_session/client.h @@ -73,9 +73,9 @@ struct Genode::Pd_session_client : Rpc_client Cap_quota cap_quota() const override { return call(); } Cap_quota used_caps() const override { return call(); } - Ram_dataspace_capability alloc(size_t size, Cache cache = CACHED) override + Alloc_result try_alloc(size_t size, Cache cache = CACHED) override { - return call(size, cache); + return call(size, cache); } void free(Ram_dataspace_capability ds) override { call(ds); } diff --git a/repos/base/include/pd_session/pd_session.h b/repos/base/include/pd_session/pd_session.h index 0e4bb3fe7d..dfc81365b8 100644 --- a/repos/base/include/pd_session/pd_session.h +++ b/repos/base/include/pd_session/pd_session.h @@ -15,6 +15,7 @@ #ifndef _INCLUDE__PD_SESSION__PD_SESSION_H_ #define _INCLUDE__PD_SESSION__PD_SESSION_H_ +#include #include #include #include @@ -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); }; diff --git a/repos/base/src/core/include/pd_session_component.h b/repos/base/src/core/include/pd_session_component.h index 2712e75947..d18991e26e 100644 --- a/repos/base/src/core/include/pd_session_component.h +++ b/repos/base/src/core/include/pd_session_component.h @@ -315,7 +315,7 @@ class Genode::Pd_session_component : public Session_object ** 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; diff --git a/repos/base/src/core/include/ram_dataspace_factory.h b/repos/base/src/core/include/ram_dataspace_factory.h index 9302b996d5..1171701260 100644 --- a/repos/base/src/core/include/ram_dataspace_factory.h +++ b/repos/base/src/core/include/ram_dataspace_factory.h @@ -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; }; diff --git a/repos/base/src/core/include/synced_ram_allocator.h b/repos/base/src/core/include/synced_ram_allocator.h index 1feb05ce5b..a7cd605fc6 100644 --- a/repos/base/src/core/include/synced_ram_allocator.h +++ b/repos/base/src/core/include/synced_ram_allocator.h @@ -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 diff --git a/repos/base/src/core/pd_session_component.cc b/repos/base/src/core/pd_session_component.cc index a28eef4f0c..0716b8ae6d 100644 --- a/repos/base/src/core/pd_session_component.cc +++ b/repos/base/src/core/pd_session_component.cc @@ -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(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(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; + } + ); } diff --git a/repos/base/src/core/ram_dataspace_factory.cc b/repos/base/src/core/ram_dataspace_factory.cc index 4091d6c3b7..42f1dbb941 100644 --- a/repos/base/src/core/ram_dataspace_factory.cc +++ b/repos/base/src/core/ram_dataspace_factory.cc @@ -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(result); + return static_cap_cast(ds_cap); } diff --git a/repos/base/src/core/stack_area.cc b/repos/base/src/core/stack_area.cc index c3a0912efb..931044be02 100644 --- a/repos/base/src/core/stack_area.cc +++ b/repos/base/src/core/stack_area.cc @@ -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(Native_capability()); } void free(Ram_dataspace_capability) override { } diff --git a/repos/base/src/include/base/internal/expanding_pd_session_client.h b/repos/base/src/include/base/internal/expanding_pd_session_client.h index 9cb0986292..bd2fd35687 100644 --- a/repos/base/src/include/base/internal/expanding_pd_session_client.h +++ b/repos/base/src/include/base/internal/expanding_pd_session_client.h @@ -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( - [&] () { - return retry( - [&] () { 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 diff --git a/repos/base/src/lib/base/sliced_heap.cc b/repos/base/src/lib/base/sliced_heap.cc index 274365e942..a7418132fc 100644 --- a/repos/base/src/lib/base/sliced_heap.cc +++ b/repos/base/src/lib/base/sliced_heap.cc @@ -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) { diff --git a/repos/libports/src/lib/libc/internal/malloc_ram_allocator.h b/repos/libports/src/lib/libc/internal/malloc_ram_allocator.h index 5b6d7bc2bb..d2ee44c7c4 100644 --- a/repos/libports/src/lib/libc/internal/malloc_ram_allocator.h +++ b/repos/libports/src/lib/libc/internal/malloc_ram_allocator.h @@ -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(_dataspaces, cap); - return cap; + return _ram.try_alloc(size, cache).convert( + + [&] (Ram_dataspace_capability cap) { + new (_md_alloc) Registered(_dataspaces, cap); + return cap; }, + + [&] (Alloc_error error) { + return error; }); } void free(Ram_dataspace_capability ds_cap) override diff --git a/repos/os/src/server/nic_router/session_env.h b/repos/os/src/server/nic_router/session_env.h index a23692e10b..8b5fa4061b 100644 --- a/repos/os/src/server/nic_router/session_env.h +++ b/repos/os/src/server/nic_router/session_env.h @@ -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; } diff --git a/repos/ports/src/app/gdb_monitor/pd_session_component.h b/repos/ports/src/app/gdb_monitor/pd_session_component.h index 37c1680bc4..bc70f3d2d0 100644 --- a/repos/ports/src/app/gdb_monitor/pd_session_component.h +++ b/repos/ports/src/app/gdb_monitor/pd_session_component.h @@ -128,8 +128,8 @@ class Gdb_monitor::Pd_session_component : public Rpc_object 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); }