From 1f4f119b1ed25d2b4b4007691e46f63059f2cb4f Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Mon, 8 May 2017 21:35:43 +0200 Subject: [PATCH] Capability quota accounting and trading This patch mirrors the accounting and trading scheme that Genode employs for physical memory to the accounting of capability allocations. Capability quotas must now be explicitly assigned to subsystems by specifying a 'caps=' attribute to init's start nodes. Analogously to RAM quotas, cap quotas can be traded between clients and servers as part of the session protocol. The capability budget of each component is maintained by the component's corresponding PD session at core. At the current stage, the accounting is applied to RPC capabilities, signal-context capabilities, and dataspace capabilities. Capabilities that are dynamically allocated via core's CPU and TRACE service are not yet covered. Also, the capabilities allocated by resource multiplexers outside of core (like nitpicker) must be accounted by the respective servers, which is not covered yet. If a component runs out of capabilities, core's PD service prints a warning to the log. To observe the consumption of capabilities per component in detail, the PD service is equipped with a diagnostic mode, which can be enabled via the 'diag' attribute in the target node of init's routing rules. E.g., the following route enables the diagnostic mode for the PD session of the "timer" component: ... For subsystems based on a sub-init instance, init can be configured to report the capability-quota information of its subsystems by adding the attribute 'child_caps="yes"' to init's '' config node. Init's own capability quota can be reported by adding the attribute 'init_caps="yes"'. Fixes #2398 --- .../base-foc/src/core/native_pd_component.cc | 4 +- repos/base-hw/src/core/env.cc | 5 +- .../base-hw/src/core/pd_upgrade_ram_quota.cc | 5 +- .../src/include/base/internal/native_env.h | 3 +- repos/base-hw/src/lib/base/env.cc | 5 +- repos/base-hw/src/lib/base/ipc.cc | 6 +- repos/base-hw/src/lib/base/signal.cc | 37 ++- repos/base-hw/src/test/cpu_quota/sync/main.cc | 2 +- repos/base-linux/src/core/include/core_env.h | 8 +- .../src/core/native_pd_component.cc | 8 +- .../include/nova_native_pd/nova_native_pd.h | 5 +- .../base-nova/src/core/native_pd_component.cc | 5 +- repos/base-nova/src/lib/base/rpc_cap_alloc.cc | 25 +- repos/base/include/base/allocator.h | 3 + .../include/base/attached_io_mem_dataspace.h | 2 + .../include/base/attached_ram_dataspace.h | 1 + repos/base/include/base/child.h | 46 ++++ repos/base/include/base/connection.h | 11 +- repos/base/include/base/env.h | 9 +- repos/base/include/base/local_connection.h | 33 ++- repos/base/include/base/service.h | 45 +++- repos/base/include/base/session_object.h | 2 + repos/base/include/base/session_state.h | 10 +- repos/base/include/parent/parent.h | 20 +- repos/base/include/pd_session/client.h | 9 + repos/base/include/pd_session/pd_session.h | 74 +++++- repos/base/include/rom_session/connection.h | 2 +- repos/base/include/root/component.h | 24 ++ repos/base/include/root/root.h | 9 +- repos/base/lib/symbols/ld | 2 +- repos/base/src/core/include/core_env.h | 14 +- repos/base/src/core/include/core_pd_session.h | 27 +- repos/base/src/core/include/pd_root.h | 46 ++-- .../src/core/include/pd_session_component.h | 244 +++++++++++++----- .../src/core/include/trace/subject_registry.h | 2 +- repos/base/src/core/main.cc | 47 +++- repos/base/src/core/pd_upgrade_ram_quota.cc | 5 +- repos/base/src/core/ram_session_component.cc | 10 +- .../spec/x86/io_port_session_component.cc | 5 - .../internal/expanding_ram_session_client.h | 25 +- .../src/include/base/internal/platform_env.h | 4 +- .../base/internal/upgradeable_client.h | 8 +- repos/base/src/lib/base/child.cc | 66 ++++- repos/base/src/lib/base/component.cc | 59 +++-- repos/base/src/lib/base/root_proxy.cc | 5 +- repos/base/src/lib/base/rpc_cap_alloc.cc | 18 +- repos/base/src/lib/base/session_state.cc | 7 +- repos/base/src/lib/base/signal.cc | 29 ++- repos/base/src/lib/base/slab.cc | 1 - repos/base/src/test/rm_fault/main.cc | 7 +- repos/demo/include/launchpad/launchpad.h | 21 +- repos/demo/src/app/launchpad/launch_entry.h | 5 +- repos/demo/src/app/launchpad/launcher.cc | 1 + .../demo/src/app/launchpad/launchpad_window.h | 4 +- repos/demo/src/app/scout/doc.cc | 2 +- repos/demo/src/app/scout/elements.h | 37 ++- repos/demo/src/app/scout/launcher.cc | 1 + repos/demo/src/lib/launchpad/launchpad.cc | 20 +- repos/gems/include/gems/report_rom_slave.h | 11 +- repos/gems/src/app/launcher/fading_dialog.h | 7 +- repos/gems/src/app/launcher/main.cc | 2 +- repos/gems/src/app/launcher/menu_view_slave.h | 13 +- repos/gems/src/app/launcher/nit_fader_slave.h | 8 +- .../gems/src/app/launcher/subsystem_manager.h | 17 +- repos/hello_tutorial/doc/hello_tutorial.txt | 13 +- repos/libports/run/ldso.run | 3 +- repos/os/include/cli_monitor/child.h | 24 +- repos/os/include/loader_session/client.h | 3 + repos/os/include/loader_session/connection.h | 7 +- .../include/loader_session/loader_session.h | 10 +- repos/os/include/os/attached_mmio.h | 4 +- .../os/include/os/child_policy_dynamic_rom.h | 4 + repos/os/include/os/dynamic_rom_session.h | 18 ++ repos/os/include/os/slave.h | 51 +++- repos/os/run/report_rom.run | 2 +- repos/os/src/app/cli_monitor/child.h | 8 +- repos/os/src/app/cli_monitor/main.cc | 3 +- repos/os/src/app/cli_monitor/start_command.h | 19 +- .../src/drivers/platform/spec/x86/device_pd.h | 4 + .../platform/spec/x86/pci_session_component.h | 78 ++++-- repos/os/src/init/child.cc | 38 ++- repos/os/src/init/child.h | 74 +++++- repos/os/src/init/main.cc | 63 ++++- repos/os/src/init/report.h | 6 + repos/os/src/init/server.cc | 28 +- repos/os/src/init/service.h | 26 +- repos/os/src/init/types.h | 1 + repos/os/src/init/utils.h | 9 +- repos/os/src/server/loader/child.h | 12 + repos/os/src/server/loader/main.cc | 22 +- repos/os/src/test/bomb/main.cc | 33 ++- .../os/src/test/dynamic_config/loader/main.cc | 2 +- .../os/src/test/dynamic_config/master/main.cc | 1 + repos/os/src/test/fault_detection/main.cc | 13 +- repos/os/src/test/loader/main.cc | 2 +- repos/os/src/test/resource_request/main.cc | 1 + repos/os/src/test/resource_yield/main.cc | 5 +- repos/ports/src/app/gdb_monitor/app_child.h | 20 +- .../app/gdb_monitor/gdbserver/genode-low.cc | 12 + .../app/gdb_monitor/pd_session_component.h | 9 + repos/ports/src/noux/child.h | 10 +- repos/ports/src/noux/child_policy.h | 9 +- repos/ports/src/noux/main.cc | 2 + repos/ports/src/noux/pd_session_component.h | 51 +++- repos/ports/src/noux/syscall.cc | 1 + 105 files changed, 1494 insertions(+), 405 deletions(-) diff --git a/repos/base-foc/src/core/native_pd_component.cc b/repos/base-foc/src/core/native_pd_component.cc index 98889d221..6b479a275 100644 --- a/repos/base-foc/src/core/native_pd_component.cc +++ b/repos/base-foc/src/core/native_pd_component.cc @@ -28,13 +28,13 @@ Native_pd_component::Native_pd_component(Pd_session_component &pd_session, : _pd_session(pd_session) { - _pd_session._thread_ep.manage(this); + _pd_session._ep.manage(this); } Native_pd_component::~Native_pd_component() { - _pd_session._thread_ep.dissolve(this); + _pd_session._ep.dissolve(this); } diff --git a/repos/base-hw/src/core/env.cc b/repos/base-hw/src/core/env.cc index 410d4f3d3..45b3afdae 100644 --- a/repos/base-hw/src/core/env.cc +++ b/repos/base-hw/src/core/env.cc @@ -14,5 +14,6 @@ #include #include -void Genode::upgrade_pd_quota_non_blocking(Genode::size_t quota) { - ASSERT_NEVER_CALLED; } +using namespace Genode; + +void Genode::upgrade_pd_quota_non_blocking(Ram_quota, Cap_quota) { ASSERT_NEVER_CALLED; } diff --git a/repos/base-hw/src/core/pd_upgrade_ram_quota.cc b/repos/base-hw/src/core/pd_upgrade_ram_quota.cc index 9b49d3f8c..d44cd5a57 100644 --- a/repos/base-hw/src/core/pd_upgrade_ram_quota.cc +++ b/repos/base-hw/src/core/pd_upgrade_ram_quota.cc @@ -17,9 +17,8 @@ using namespace Genode; -void Pd_session_component::upgrade_ram_quota(size_t ram_quota) +void Pd_session_component::session_quota_upgraded() { - _md_alloc.upgrade(ram_quota); - _pd.upgrade_slab(_md_alloc); + _pd.upgrade_slab(_sliced_heap); } diff --git a/repos/base-hw/src/include/base/internal/native_env.h b/repos/base-hw/src/include/base/internal/native_env.h index ed7397dc0..0ec6579c6 100644 --- a/repos/base-hw/src/include/base/internal/native_env.h +++ b/repos/base-hw/src/include/base/internal/native_env.h @@ -16,6 +16,7 @@ /* Genode includes */ #include +#include namespace Genode { @@ -26,7 +27,7 @@ namespace Genode * needed when doing upgrades in situations where the environment is * already locked due to the operation that triggered the upgrade. */ - void upgrade_pd_quota_non_blocking(size_t); + void upgrade_pd_quota_non_blocking(Ram_quota, Cap_quota); }; #endif /* _INCLUDE__BASE__INTERNAL__NATIVE_ENV_H_ */ diff --git a/repos/base-hw/src/lib/base/env.cc b/repos/base-hw/src/lib/base/env.cc index ca352a4bd..e38f367b0 100644 --- a/repos/base-hw/src/lib/base/env.cc +++ b/repos/base-hw/src/lib/base/env.cc @@ -18,8 +18,9 @@ #include #include -void Genode::upgrade_pd_quota_non_blocking(size_t quota) +void Genode::upgrade_pd_quota_non_blocking(Ram_quota ram, Cap_quota caps) { internal_env().parent().upgrade(Parent::Env::pd(), - String<64>("ram_quota=", quota).string()); + String<100>("ram_quota=", ram, ", " + "cap_quota=", caps).string()); } diff --git a/repos/base-hw/src/lib/base/ipc.cc b/repos/base-hw/src/lib/base/ipc.cc index 3884faaa5..5da1381bf 100644 --- a/repos/base-hw/src/lib/base/ipc.cc +++ b/repos/base-hw/src/lib/base/ipc.cc @@ -109,7 +109,8 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst, } }, - [&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); }); + [&] () { upgrade_pd_quota_non_blocking(Ram_quota{3 * 1024 * sizeof(addr_t)}, + Cap_quota{0}); }); return Rpc_exception_code(utcb.exception_code()); } @@ -154,7 +155,8 @@ Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &, default: break; } }, - [&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); }); + [&] () { upgrade_pd_quota_non_blocking(Ram_quota{3 * 1024 * sizeof(addr_t)}, + Cap_quota{0}); }); copy_utcb_to_msg(utcb, request_msg); diff --git a/repos/base-hw/src/lib/base/signal.cc b/repos/base-hw/src/lib/base/signal.cc index a03ac7ecc..41c4987c2 100644 --- a/repos/base-hw/src/lib/base/signal.cc +++ b/repos/base-hw/src/lib/base/signal.cc @@ -66,13 +66,22 @@ void Signal_transmitter::submit(unsigned cnt) Signal_receiver::Signal_receiver() { - retry( - [&] () { _cap = internal_env().pd().alloc_signal_source(); }, - [&] () { - log("upgrading quota donation for PD session"); - internal_env().upgrade(Parent::Env::pd(), "ram_quota=8K"); + for (;;) { + + Ram_quota ram_upgrade { 0 }; + Cap_quota cap_upgrade { 0 }; + + try { + _cap = internal_env().pd().alloc_signal_source(); + break; } - ); + catch (Out_of_ram) { ram_upgrade = Ram_quota { 2*1024*sizeof(long) }; } + catch (Out_of_caps) { cap_upgrade = Cap_quota { 4 }; } + + internal_env().upgrade(Parent::Env::pd(), + String<100>("ram_quota=", ram_upgrade, ", " + "cap_quota=", cap_upgrade).string()); + } } @@ -98,17 +107,23 @@ Signal_context_capability Signal_receiver::manage(Signal_context * const c) Lock::Guard context_guard(c->_lock); if (c->_receiver) { throw Context_already_in_use(); } - retry( - [&] () { + for (;;) { + + Ram_quota ram_upgrade { 0 }; + Cap_quota cap_upgrade { 0 }; + + try { /* use signal context as imprint */ c->_cap = env_deprecated()->pd_session()->alloc_context(_cap, (unsigned long)c); c->_receiver = this; _contexts.insert(&c->_receiver_le); return c->_cap; - }, - [&] () { upgrade_pd_quota_non_blocking(1024 * sizeof(addr_t)); }); + } + catch (Out_of_ram) { ram_upgrade = Ram_quota { 1024*sizeof(long) }; } + catch (Out_of_caps) { cap_upgrade = Cap_quota { 4 }; } - return c->_cap; + upgrade_pd_quota_non_blocking(ram_upgrade, cap_upgrade); + } } diff --git a/repos/base-hw/src/test/cpu_quota/sync/main.cc b/repos/base-hw/src/test/cpu_quota/sync/main.cc index b9c015468..4d306c090 100644 --- a/repos/base-hw/src/test/cpu_quota/sync/main.cc +++ b/repos/base-hw/src/test/cpu_quota/sync/main.cc @@ -52,7 +52,7 @@ struct Sync_root : public Root_component Session_component *_create_session(char const *args) override { try { return new (md_alloc()) Session_component(*this); } - catch (...) { throw Root::Exception(); } + catch (...) { throw Root::Invalid_args(); } } Sync_root(Entrypoint &ep, Allocator &md_alloc) diff --git a/repos/base-linux/src/core/include/core_env.h b/repos/base-linux/src/core/include/core_env.h index 41cfda208..817061925 100644 --- a/repos/base-linux/src/core/include/core_env.h +++ b/repos/base-linux/src/core/include/core_env.h @@ -122,8 +122,8 @@ namespace Genode { * method to issue out-of-order replies to * 'Signal_source::wait_for_signal' calls. */ - Core_pd_session_component _pd_session_component; - Pd_session_client _pd_session_client; + Core_pd_session_component _pd_session_component { _entrypoint }; + Pd_session_client _pd_session_client { _pd_session_component.cap() }; Registry _services; @@ -155,9 +155,7 @@ namespace Genode { 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_component::any_phys_range()) { _ram_session.init_ram_account(); } diff --git a/repos/base-linux/src/core/native_pd_component.cc b/repos/base-linux/src/core/native_pd_component.cc index 09a119150..4337939bc 100644 --- a/repos/base-linux/src/core/native_pd_component.cc +++ b/repos/base-linux/src/core/native_pd_component.cc @@ -143,7 +143,7 @@ void Native_pd_component::_start(Dataspace_component &ds) /* prefix name of Linux program (helps killing some zombies) */ char const *prefix = "[Genode] "; char pname_buf[sizeof(_pd_session._label) + sizeof(prefix)]; - snprintf(pname_buf, sizeof(pname_buf), "%s%s", prefix, _pd_session._label.string); + snprintf(pname_buf, sizeof(pname_buf), "%s%s", prefix, _pd_session._label.string()); char *argv_buf[2]; argv_buf[0] = pname_buf; argv_buf[1] = 0; @@ -184,7 +184,7 @@ Native_pd_component::Native_pd_component(Pd_session_component &pd_session, : _pd_session(pd_session) { - _pd_session._thread_ep.manage(this); + _pd_session._ep.manage(this); } @@ -193,14 +193,14 @@ Native_pd_component::~Native_pd_component() if (_pid) lx_kill(_pid, 9); - _pd_session._thread_ep.dissolve(this); + _pd_session._ep.dissolve(this); } void Native_pd_component::start(Capability binary) { /* lookup binary dataspace */ - _pd_session._thread_ep.apply(binary, [&] (Dataspace_component *ds) { + _pd_session._ep.apply(binary, [&] (Dataspace_component *ds) { if (ds) _start(*ds); diff --git a/repos/base-nova/include/nova_native_pd/nova_native_pd.h b/repos/base-nova/include/nova_native_pd/nova_native_pd.h index b5efdc375..a1250227f 100644 --- a/repos/base-nova/include/nova_native_pd/nova_native_pd.h +++ b/repos/base-nova/include/nova_native_pd/nova_native_pd.h @@ -28,7 +28,8 @@ struct Genode::Nova_native_pd : Pd_session::Native_pd * \param entry server-side instruction pointer of the RPC handler * \param mtd NOVA message transfer descriptor * - * \throw Pd_session::Out_of_metadata + * \throw Out_of_ram + * \throw Out_of_caps * * \return new RPC object capability */ @@ -41,7 +42,7 @@ struct Genode::Nova_native_pd : Pd_session::Native_pd virtual void imprint_rpc_cap(Native_capability cap, unsigned long badge) = 0; GENODE_RPC_THROW(Rpc_alloc_rpc_cap, Native_capability, alloc_rpc_cap, - GENODE_TYPE_LIST(Pd_session::Out_of_metadata), + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps), Native_capability, addr_t, addr_t); GENODE_RPC(Rpc_imprint_rpc_cap, void, imprint_rpc_cap, Native_capability, unsigned long); diff --git a/repos/base-nova/src/core/native_pd_component.cc b/repos/base-nova/src/core/native_pd_component.cc index 31c23efd8..a0b7e4838 100644 --- a/repos/base-nova/src/core/native_pd_component.cc +++ b/repos/base-nova/src/core/native_pd_component.cc @@ -24,6 +24,7 @@ Native_capability Native_pd_component::alloc_rpc_cap(Native_capability ep, addr_t entry, addr_t mtd) { try { + _pd_session._consume_cap(Pd_session_component::RPC_CAP); return _pd_session._rpc_cap_factory.alloc(ep, entry, mtd); } catch (Allocator::Out_of_memory) { throw Out_of_ram(); } @@ -42,13 +43,13 @@ Native_pd_component::Native_pd_component(Pd_session_component &pd_session, : _pd_session(pd_session) { - _pd_session._thread_ep.manage(this); + _pd_session._ep.manage(this); } Native_pd_component::~Native_pd_component() { - _pd_session._thread_ep.dissolve(this); + _pd_session._ep.dissolve(this); } diff --git a/repos/base-nova/src/lib/base/rpc_cap_alloc.cc b/repos/base-nova/src/lib/base/rpc_cap_alloc.cc index 59c70a785..ec8a379bf 100644 --- a/repos/base-nova/src/lib/base/rpc_cap_alloc.cc +++ b/repos/base-nova/src/lib/base/rpc_cap_alloc.cc @@ -34,18 +34,23 @@ Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd, Native_capabili Nova_native_pd_client native_pd(_native_pd_cap); - Untyped_capability new_obj_cap = - retry( - [&] () { - return native_pd.alloc_rpc_cap(ep, entry, 0); - }, - [&] () { - internal_env().upgrade(Parent::Env::pd(), "ram_quota=16K"); - }); + for (;;) { - native_pd.imprint_rpc_cap(new_obj_cap, new_obj_cap.local_name()); + Ram_quota ram_upgrade { 0 }; + Cap_quota cap_upgrade { 0 }; - return new_obj_cap; + try { + Untyped_capability new_obj_cap = native_pd.alloc_rpc_cap(ep, entry, 0); + native_pd.imprint_rpc_cap(new_obj_cap, new_obj_cap.local_name()); + return new_obj_cap; + } + catch (Out_of_ram) { ram_upgrade = Ram_quota { 2*1024*sizeof(long) }; } + catch (Out_of_caps) { cap_upgrade = Cap_quota { 4 }; } + + env_deprecated()->parent()->upgrade(Parent::Env::pd(), + String<100>("ram_quota=", ram_upgrade, ", " + "cap_quota=", cap_upgrade).string()); + } } diff --git a/repos/base/include/base/allocator.h b/repos/base/include/base/allocator.h index 2deead4aa..788947cc0 100644 --- a/repos/base/include/base/allocator.h +++ b/repos/base/include/base/allocator.h @@ -75,6 +75,7 @@ struct Genode::Allocator : Deallocator * undefined in the error case * * \throw Out_of_ram + * \throw Out_of_caps * * \return true on success */ @@ -89,6 +90,7 @@ struct Genode::Allocator : Deallocator * pointer will break strict-aliasing rules". * * \throw Out_of_ram + * \throw Out_of_caps */ template bool alloc(size_t size, T **out_addr) { @@ -114,6 +116,7 @@ struct Genode::Allocator : Deallocator * \param size block size to allocate * * \throw Out_of_ram + * \throw Out_of_caps * * \return pointer to the new block */ diff --git a/repos/base/include/base/attached_io_mem_dataspace.h b/repos/base/include/base/attached_io_mem_dataspace.h index 0b6050293..8f16a45fd 100644 --- a/repos/base/include/base/attached_io_mem_dataspace.h +++ b/repos/base/include/base/attached_io_mem_dataspace.h @@ -47,8 +47,10 @@ class Genode::Attached_io_mem_dataspace * * \throw Parent::Service_denied * \throw Insufficient_ram_quota + * \throw Insufficient_cap_quota * \throw Parent::Unavailable * \throw Out_of_ram + * \throw Out_of_caps * \throw Rm_session::Attach_failed */ Attached_io_mem_dataspace(Env &env, Genode::addr_t base, Genode::size_t size, diff --git a/repos/base/include/base/attached_ram_dataspace.h b/repos/base/include/base/attached_ram_dataspace.h index dc0b0f634..d82645eba 100644 --- a/repos/base/include/base/attached_ram_dataspace.h +++ b/repos/base/include/base/attached_ram_dataspace.h @@ -91,6 +91,7 @@ class Genode::Attached_ram_dataspace * Constructor * * \throw Out_of_ram + * \throw Out_of_caps * \throw Rm_session::Attach_failed */ Attached_ram_dataspace(Ram_session &ram, Region_map &rm, diff --git a/repos/base/include/base/child.h b/repos/base/include/base/child.h index 78612b2b1..a51f189ba 100644 --- a/repos/base/include/base/child.h +++ b/repos/base/include/base/child.h @@ -134,6 +134,15 @@ struct Genode::Child_policy log("child \"", name(), "\" exited with exit value ", exit_value); } + /** + * Reference PD session + * + * The PD session returned by this method is used for session cap-quota + * transfers. + */ + virtual Pd_session &ref_pd() = 0; + virtual Pd_session_capability ref_pd_cap() const = 0; + /** * Reference RAM session * @@ -393,6 +402,7 @@ class Genode::Child : protected Rpc_object, * \throw Invalid_executable * \throw Region_map::Attach_failed * \throw Out_of_ram + * \throw Out_of_caps * * The other arguments correspond to those of 'Child::Child'. * @@ -487,6 +497,25 @@ class Genode::Child : protected Rpc_object, Ram_transfer::Account &to = _service; return to.cap(Ram_quota()); } + + /** + * Service (Cap_transfer::Account) interface + */ + void transfer(Pd_session_capability to, Cap_quota amount) override + { + Cap_transfer::Account &from = _service; + from.transfer(to, amount); + } + + /** + * Service (Cap_transfer::Account) interface + */ + Pd_session_capability cap(Cap_quota) const override + { + Cap_transfer::Account &to = _service; + return to.cap(Cap_quota()); + } + void wakeup() override { _service.wakeup(); } bool operator == (Service const &other) const override @@ -662,6 +691,13 @@ class Genode::Child : protected Rpc_object, 2*Rom_connection::RAM_QUOTA }; } + static Cap_quota env_cap_quota() + { + return { Cpu_connection::CAP_QUOTA + Ram_connection::CAP_QUOTA + + Pd_connection::CAP_QUOTA + Log_connection::CAP_QUOTA + + 2*Rom_connection::CAP_QUOTA + 1 /* parent cap */ }; + } + template void for_each_session(FN const &fn) const { @@ -676,7 +712,16 @@ class Genode::Child : protected Rpc_object, return _effective_quota(quota, env_ram_quota()); } + /** + * Deduce env session costs from usable cap quota + */ + static Cap_quota effective_quota(Cap_quota quota) + { + return _effective_quota(quota, env_cap_quota()); + } + Ram_session_capability ram_session_cap() const { return _ram.cap(); } + Pd_session_capability pd_session_cap() const { return _pd.cap(); } Parent_capability parent_cap() const { return cap(); } @@ -684,6 +729,7 @@ class Genode::Child : protected Rpc_object, Ram_session const &ram() const { return _ram.session(); } Cpu_session &cpu() { return _cpu.session(); } Pd_session &pd() { return _pd .session(); } + Pd_session const &pd() const { return _pd .session(); } /** * Request factory for creating session-state objects diff --git a/repos/base/include/base/connection.h b/repos/base/include/base/connection.h index 98117ab11..27b13b53f 100644 --- a/repos/base/include/base/connection.h +++ b/repos/base/include/base/connection.h @@ -52,10 +52,17 @@ class Genode::Connection_base : public Noncopyable */ Connection_base(); + + void upgrade(Session::Resources resources) + { + String<80> const args("ram_quota=", resources.ram_quota, ", " + "cap_quota=", resources.cap_quota); + _env.upgrade(_id_space_element.id(), args.string()); + } + void upgrade_ram(size_t bytes) { - String<64> const args("ram_quota=", Ram_quota{bytes}); - _env.upgrade(_id_space_element.id(), args.string()); + upgrade(Session::Resources { Ram_quota{bytes}, Cap_quota{0} }); } }; diff --git a/repos/base/include/base/env.h b/repos/base/include/base/env.h index 5cb66a546..da9657cd0 100644 --- a/repos/base/include/base/env.h +++ b/repos/base/include/base/env.h @@ -93,8 +93,12 @@ struct Genode::Env * \param affinity preferred CPU affinity for the session * * \throw Service_denied + * \throw Insufficient_cap_quota * \throw Insufficient_ram_quota - * \throw Unavailable + * \throw Out_of_caps + * \throw Out_of_ram + * + * See the documentation of 'Parent::session'. * * This method blocks until the session is available or an error * occurred. @@ -116,6 +120,9 @@ struct Genode::Env * \param args description of the amount of quota to transfer * * \throw Out_of_ram + * \throw Out_of_caps + * + * See the documentation of 'Parent::upgrade'. * * The 'args' argument has the same principle format as the 'args' * argument of the 'session' operation. diff --git a/repos/base/include/base/local_connection.h b/repos/base/include/base/local_connection.h index 02337bbae..6e925a404 100644 --- a/repos/base/include/base/local_connection.h +++ b/repos/base/include/base/local_connection.h @@ -39,7 +39,7 @@ struct Genode::Local_connection_base : Noncopyable private: - static Args _init_args(Args const &args, size_t const &ram_quota, + static Args _init_args(Args const &args, Session::Resources resources, Session::Diag diag) { /* copy original arguments into modifiable buffer */ @@ -47,7 +47,9 @@ struct Genode::Local_connection_base : Noncopyable strncpy(buf, args.string(), sizeof(buf)); Arg_string::set_arg(buf, sizeof(buf), "ram_quota", - String<64>(ram_quota).string()); + String<64>(resources.ram_quota.value).string()); + Arg_string::set_arg(buf, sizeof(buf), "cap_quota", + String<64>(resources.cap_quota.value).string()); Arg_string::set_arg(buf, sizeof(buf), "diag", diag.enabled); /* return result as a copy */ @@ -62,23 +64,35 @@ struct Genode::Local_connection_base : Noncopyable Args const &args, Affinity const &affinity, Session::Label const &label, Session::Diag diag, - size_t ram_quota) + Session::Resources resources) { enum { NUM_ATTEMPTS = 10 }; for (unsigned i = 0; i < NUM_ATTEMPTS; i++) { _session_state.construct(service, id_space, id, label, - _init_args(args, ram_quota, diag), + _init_args(args, resources, diag), affinity); _session_state->service().initiate_request(*_session_state); - if (_session_state->phase == Session_state::INSUFFICIENT_RAM_QUOTA) - ram_quota += 4096; - else + if (_session_state->alive()) break; + + switch (_session_state->phase) { + + case Session_state::INSUFFICIENT_RAM_QUOTA: + resources.ram_quota.value += 4096; + break; + + case Session_state::INSUFFICIENT_CAP_QUOTA: + resources.cap_quota.value += 1; + break; + + default: break; + } } - if (_session_state->phase == Session_state::INSUFFICIENT_RAM_QUOTA) + if (_session_state->phase == Session_state::INSUFFICIENT_RAM_QUOTA + || _session_state->phase == Session_state::INSUFFICIENT_CAP_QUOTA) warning("giving up to increase session quota for ", service.name(), " session " "after ", (int)NUM_ATTEMPTS, " attempts"); } @@ -153,7 +167,8 @@ class Genode::Local_connection : Local_connection_base Local_connection_base(service, id_space, id, args, affinity, label.valid() ? label : label_from_args(args.string()), diag, - CONNECTION::RAM_QUOTA) + Session::Resources { Ram_quota { CONNECTION::RAM_QUOTA }, + Cap_quota { CONNECTION::CAP_QUOTA } }) { service.wakeup(); } diff --git a/repos/base/include/base/service.h b/repos/base/include/base/service.h index 473283280..1e1f958f1 100644 --- a/repos/base/include/base/service.h +++ b/repos/base/include/base/service.h @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,8 @@ namespace Genode { } -class Genode::Service : public Ram_transfer::Account +class Genode::Service : public Ram_transfer::Account, + public Cap_transfer::Account { public: @@ -126,6 +128,7 @@ class Genode::Local_service : public Service * * \throw Denied * \throw Insufficient_ram_quota + * \throw Insufficient_cap_quota */ virtual SESSION &create(Args const &, Affinity) = 0; @@ -192,6 +195,8 @@ class Genode::Local_service : public Service } catch (typename Factory::Denied) { session.phase = Session_state::INVALID_ARGS; } + catch (Insufficient_cap_quota) { + session.phase = Session_state::INSUFFICIENT_CAP_QUOTA; } catch (Insufficient_ram_quota) { session.phase = Session_state::INSUFFICIENT_RAM_QUOTA; } @@ -199,7 +204,8 @@ class Genode::Local_service : public Service case Session_state::UPGRADE_REQUESTED: { - String<64> const args("ram_quota=", session.ram_upgrade); + String<100> const args("ram_quota=", session.ram_upgrade, ", " + "cap_quota=", session.cap_upgrade); _apply_to_rpc_obj(session, [&] (SESSION &rpc_obj) { _factory.upgrade(rpc_obj, args.string()); }); @@ -220,6 +226,7 @@ class Genode::Local_service : public Service case Session_state::INVALID_ARGS: case Session_state::INSUFFICIENT_RAM_QUOTA: + case Session_state::INSUFFICIENT_CAP_QUOTA: case Session_state::AVAILABLE: case Session_state::CAP_HANDED_OUT: case Session_state::CLOSED: @@ -279,10 +286,19 @@ class Genode::Parent_service : public Service catch (Out_of_ram) { session.id_at_parent.destruct(); session.phase = Session_state::INVALID_ARGS; } + + catch (Out_of_caps) { + session.id_at_parent.destruct(); + session.phase = Session_state::INVALID_ARGS; } + catch (Insufficient_ram_quota) { session.id_at_parent.destruct(); session.phase = Session_state::INSUFFICIENT_RAM_QUOTA; } + catch (Insufficient_cap_quota) { + session.id_at_parent.destruct(); + session.phase = Session_state::INSUFFICIENT_CAP_QUOTA; } + catch (Parent::Service_denied) { session.id_at_parent.destruct(); session.phase = Session_state::INVALID_ARGS; } @@ -291,7 +307,8 @@ class Genode::Parent_service : public Service case Session_state::UPGRADE_REQUESTED: { - String<64> const args("ram_quota=", session.ram_upgrade); + String<100> const args("ram_quota=", session.ram_upgrade, ", " + "cap_quota=", session.cap_upgrade); if (!session.id_at_parent.constructed()) error("invalid parent-session state: ", session); @@ -300,6 +317,8 @@ class Genode::Parent_service : public Service _env.upgrade(session.id_at_parent->id(), args.string()); } catch (Out_of_ram) { warning("RAM quota exceeded while upgrading parent session"); } + catch (Out_of_caps) { + warning("cap quota exceeded while upgrading parent session"); } session.confirm_ram_upgrade(); session.phase = Session_state::CAP_HANDED_OUT; @@ -317,6 +336,7 @@ class Genode::Parent_service : public Service case Session_state::INVALID_ARGS: case Session_state::INSUFFICIENT_RAM_QUOTA: + case Session_state::INSUFFICIENT_CAP_QUOTA: case Session_state::AVAILABLE: case Session_state::CAP_HANDED_OUT: case Session_state::CLOSED: @@ -398,6 +418,7 @@ class Genode::Child_service : public Async_service private: Ram_session_client _ram; + Pd_session_client _pd; public: @@ -408,10 +429,11 @@ class Genode::Child_service : public Async_service Id_space &server_id_space, Session_state::Factory &factory, Wakeup &wakeup, - Ram_session_capability ram) + Ram_session_capability ram, + Pd_session_capability pd) : Async_service(name, server_id_space, factory, wakeup), - _ram(ram) + _ram(ram), _pd(pd) { } /** @@ -426,6 +448,19 @@ class Genode::Child_service : public Async_service * Ram_transfer::Account interface */ Ram_session_capability cap(Ram_quota) const override { return _ram; } + + /** + * Cap_transfer::Account interface + */ + void transfer(Pd_session_capability to, Cap_quota amount) override + { + if (to.valid()) _pd.transfer_quota(to, amount); + } + + /** + * Cap_transfer::Account interface + */ + Pd_session_capability cap(Cap_quota) const override { return _pd; } }; #endif /* _INCLUDE__BASE__SERVICE_H_ */ diff --git a/repos/base/include/base/session_object.h b/repos/base/include/base/session_object.h index e06c19c87..78943e211 100644 --- a/repos/base/include/base/session_object.h +++ b/repos/base/include/base/session_object.h @@ -65,12 +65,14 @@ 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}); } /** diff --git a/repos/base/include/base/session_state.h b/repos/base/include/base/session_state.h index a009ec472..d2f96b5ff 100644 --- a/repos/base/include/base/session_state.h +++ b/repos/base/include/base/session_state.h @@ -59,6 +59,7 @@ class Genode::Session_state : public Parent::Client, public Parent::Server, * Total of quota associated with this session */ Ram_quota _donated_ram_quota { 0 }; + Cap_quota _donated_cap_quota { 0 }; Factory *_factory = nullptr; @@ -80,6 +81,7 @@ class Genode::Session_state : public Parent::Client, public Parent::Server, enum Phase { CREATE_REQUESTED, INVALID_ARGS, INSUFFICIENT_RAM_QUOTA, + INSUFFICIENT_CAP_QUOTA, AVAILABLE, CAP_HANDED_OUT, UPGRADE_REQUESTED, @@ -107,6 +109,7 @@ class Genode::Session_state : public Parent::Client, public Parent::Server, Session_capability cap; Ram_quota ram_upgrade { 0 }; + Cap_quota cap_upgrade { 0 }; void print(Output &out) const; @@ -150,10 +153,13 @@ class Genode::Session_state : public Parent::Client, public Parent::Server, ram_upgrade = Ram_quota { 0 }; } - void increase_donated_quota(Ram_quota added_ram_quota) + void increase_donated_quota(Ram_quota added_ram_quota, + Cap_quota added_cap_quota) { _donated_ram_quota.value += added_ram_quota.value; + _donated_cap_quota.value += added_cap_quota.value; ram_upgrade = added_ram_quota; + cap_upgrade = added_cap_quota; } Parent::Client::Id id_at_client() const @@ -178,6 +184,7 @@ class Genode::Session_state : public Parent::Client, public Parent::Server, void generate_server_side_info(Xml_generator &, Detail detail) const; Ram_quota donated_ram_quota() const { return _donated_ram_quota; } + Cap_quota donated_cap_quota() const { return _donated_cap_quota; } bool alive() const { @@ -186,6 +193,7 @@ class Genode::Session_state : public Parent::Client, public Parent::Server, case CREATE_REQUESTED: case INVALID_ARGS: case INSUFFICIENT_RAM_QUOTA: + case INSUFFICIENT_CAP_QUOTA: case CLOSED: return false; diff --git a/repos/base/include/parent/parent.h b/repos/base/include/parent/parent.h index 7c9a11dcd..f57f0d901 100644 --- a/repos/base/include/parent/parent.h +++ b/repos/base/include/parent/parent.h @@ -57,9 +57,8 @@ class Genode::Parent ** Exception types ** *********************/ - class Exception : public ::Genode::Exception { }; - class Service_denied : public Exception { }; - class Unavailable : public Exception { }; + struct Service_denied : Exception { }; + struct Unavailable : Exception { }; typedef Rpc_in_buffer<64> Service_name; typedef Rpc_in_buffer<160> Session_args; @@ -160,7 +159,9 @@ class Genode::Parent * \param affinity preferred CPU affinity for the session * * \throw Service_denied parent denies session request + * \throw Insufficient_cap_quota donated cap quota does not suffice * \throw Insufficient_ram_quota donated RAM quota does not suffice + * \throw Out_of_caps session CAP quota exceeds our resources * \throw Out_of_ram session RAM quota exceeds our resources * * \return session capability of the new session is immediately @@ -182,8 +183,11 @@ class Genode::Parent * Request session capability * * \throw Service_denied + * \throw Insufficient_cap_quota * \throw Insufficient_ram_quota * + * See 'session' for more documentation. + * * In the exception case, the parent implicitly closes the session. */ virtual Session_capability session_cap(Client::Id id) = 0; @@ -196,6 +200,7 @@ class Genode::Parent * \param id ID of recipient session * \param args description of the amount of quota to transfer * + * \throw Out_of_caps * \throw Out_of_ram * * The 'args' argument has the same principle format as the 'args' @@ -216,7 +221,7 @@ class Genode::Parent */ enum Session_response { SESSION_OK, SESSION_CLOSED, INVALID_ARGS, - INSUFFICIENT_RAM_QUOTA }; + INSUFFICIENT_RAM_QUOTA, INSUFFICIENT_CAP_QUOTA }; /** * Set state of a session provided by the child service @@ -294,16 +299,17 @@ class Genode::Parent Service_name const &); GENODE_RPC(Rpc_session_sigh, void, session_sigh, Signal_context_capability); GENODE_RPC_THROW(Rpc_session, Session_capability, session, - GENODE_TYPE_LIST(Service_denied, Out_of_ram, + GENODE_TYPE_LIST(Service_denied, Out_of_caps, + Out_of_ram, Insufficient_cap_quota, Insufficient_ram_quota, Unavailable), Client::Id, Service_name const &, Session_args const &, Affinity const &); GENODE_RPC_THROW(Rpc_session_cap, Session_capability, session_cap, - GENODE_TYPE_LIST(Service_denied, + GENODE_TYPE_LIST(Service_denied, Insufficient_cap_quota, Insufficient_ram_quota, Unavailable), Client::Id); GENODE_RPC_THROW(Rpc_upgrade, Upgrade_result, upgrade, - GENODE_TYPE_LIST(Out_of_ram), + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps), Client::Id, Upgrade_args const &); GENODE_RPC(Rpc_close, Close_result, close, Client::Id); GENODE_RPC(Rpc_session_response, void, session_response, diff --git a/repos/base/include/pd_session/client.h b/repos/base/include/pd_session/client.h index 9a0d7050b..1beffd435 100644 --- a/repos/base/include/pd_session/client.h +++ b/repos/base/include/pd_session/client.h @@ -62,6 +62,15 @@ struct Genode::Pd_session_client : Rpc_client Capability linker_area() override { return call(); } + void ref_account(Capability pd) override { + call(pd); } + + void transfer_quota(Capability pd, Cap_quota amount) override { + call(pd, amount); } + + Cap_quota cap_quota() const { return call(); } + Cap_quota used_caps() const { return call(); } + Capability native_pd() override { return call(); } }; diff --git a/repos/base/include/pd_session/pd_session.h b/repos/base/include/pd_session/pd_session.h index ebbfca0ac..782ad939f 100644 --- a/repos/base/include/pd_session/pd_session.h +++ b/repos/base/include/pd_session/pd_session.h @@ -71,7 +71,8 @@ struct Genode::Pd_session : Session typedef Capability Signal_source_capability; - class Out_of_metadata : public Exception { }; + class Invalid_session : public Exception { }; + class Undefined_ref_account : public Exception { }; class Invalid_signal_source : public Exception { }; /** @@ -81,7 +82,8 @@ struct Genode::Pd_session : Session * * The signal source provides an interface to wait for incoming signals. * - * \throw Out_of_metadata + * \throw Out_of_ram + * \throw Out_of_caps */ virtual Signal_source_capability alloc_signal_source() = 0; @@ -102,7 +104,8 @@ struct Genode::Pd_session : Session * originating from the allocated signal-context capability * \return new signal-context capability * - * \throw Out_of_metadata + * \throw Out_of_ram + * \throw Out_of_caps * \throw Invalid_signal_source */ virtual Capability @@ -141,7 +144,8 @@ struct Genode::Pd_session : Session * * \param ep entry point that will use this capability * - * \throw Out_of_metadata if meta-data backing store is exhausted + * \throw Out_of_ram if meta-data backing store is exhausted + * \throw Out_of_caps if 'cap_quota' is exceeded * * \return new RPC capability */ @@ -177,6 +181,49 @@ struct Genode::Pd_session : Session virtual Capability linker_area() = 0; + /******************************************* + ** Accounting for capability allocations ** + *******************************************/ + + /** + * Define reference account for the PD session + * + * \throw Invalid_session + */ + virtual void ref_account(Capability) = 0; + + /** + * Transfer capability quota to another PD session + * + * \param pd_session receiver of quota donation + * \param amount amount of quota to donate + * + * \throw Out_of_caps + * \throw Invalid_session + * \throw Undefined_ref_account + * + * Quota can only be transfered if the specified PD session is either the + * reference account for this session or vice versa. + */ + virtual void transfer_quota(Capability pd_session, + Cap_quota amount) = 0; + + /** + * Return current capability-quota limit + */ + virtual Cap_quota cap_quota() const = 0; + + /** + * Return number of capabilities allocated from the session + */ + virtual Cap_quota used_caps() const = 0; + + Cap_quota avail_caps() const + { + return Cap_quota { cap_quota().value - used_caps().value }; + } + + /***************************************** ** Access to kernel-specific interface ** *****************************************/ @@ -200,30 +247,41 @@ struct Genode::Pd_session : Session GENODE_RPC(Rpc_assign_pci, bool, assign_pci, addr_t, uint16_t); GENODE_RPC_THROW(Rpc_alloc_signal_source, Signal_source_capability, - alloc_signal_source, GENODE_TYPE_LIST(Out_of_metadata)); + alloc_signal_source, + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps)); GENODE_RPC(Rpc_free_signal_source, void, free_signal_source, Signal_source_capability); GENODE_RPC_THROW(Rpc_alloc_context, Capability, alloc_context, - GENODE_TYPE_LIST(Out_of_metadata, Invalid_signal_source), + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps, Invalid_signal_source), Signal_source_capability, unsigned long); GENODE_RPC(Rpc_free_context, void, free_context, Capability); GENODE_RPC(Rpc_submit, void, submit, Capability, unsigned); GENODE_RPC_THROW(Rpc_alloc_rpc_cap, Native_capability, alloc_rpc_cap, - GENODE_TYPE_LIST(Out_of_metadata), Native_capability); + GENODE_TYPE_LIST(Out_of_ram, Out_of_caps), Native_capability); GENODE_RPC(Rpc_free_rpc_cap, void, free_rpc_cap, Native_capability); GENODE_RPC(Rpc_address_space, Capability, address_space); GENODE_RPC(Rpc_stack_area, Capability, stack_area); GENODE_RPC(Rpc_linker_area, Capability, linker_area); + GENODE_RPC_THROW(Rpc_ref_account, void, ref_account, + GENODE_TYPE_LIST(Invalid_session), Capability); + GENODE_RPC_THROW(Rpc_transfer_cap_quota, void, transfer_quota, + GENODE_TYPE_LIST(Out_of_caps, Invalid_session, Undefined_ref_account), + Capability, Cap_quota); + GENODE_RPC(Rpc_cap_quota, Cap_quota, cap_quota); + GENODE_RPC(Rpc_used_caps, Cap_quota, used_caps); + GENODE_RPC(Rpc_native_pd, Capability, native_pd); GENODE_RPC_INTERFACE(Rpc_assign_parent, Rpc_assign_pci, Rpc_alloc_signal_source, Rpc_free_signal_source, Rpc_alloc_context, Rpc_free_context, Rpc_submit, Rpc_alloc_rpc_cap, Rpc_free_rpc_cap, Rpc_address_space, - Rpc_stack_area, Rpc_linker_area, Rpc_native_pd); + Rpc_stack_area, Rpc_linker_area, Rpc_ref_account, + Rpc_transfer_cap_quota, Rpc_cap_quota, Rpc_used_caps, + Rpc_native_pd); }; #endif /* _INCLUDE__PD_SESSION__PD_SESSION_H_ */ diff --git a/repos/base/include/rom_session/connection.h b/repos/base/include/rom_session/connection.h index c68ec3ade..5834d1736 100644 --- a/repos/base/include/rom_session/connection.h +++ b/repos/base/include/rom_session/connection.h @@ -26,7 +26,7 @@ class Genode::Rom_connection : public Connection, { public: - class Rom_connection_failed : public Parent::Exception { }; + class Rom_connection_failed : public Parent::Service_denied { }; enum { RAM_QUOTA = 6*1024UL }; diff --git a/repos/base/include/root/component.h b/repos/base/include/root/component.h index c9bc07967..060476635 100644 --- a/repos/base/include/root/component.h +++ b/repos/base/include/root/component.h @@ -151,6 +151,24 @@ class Genode::Root_component : public Rpc_object >, Ram_quota const remaining_ram_quota { ram_quota.value - needed }; + /* + * Validate that the client provided the amount of caps as mandated + * for the session interface. + */ + Cap_quota const cap_quota = cap_quota_from_args(args.string()); + + if (cap_quota.value < SESSION_TYPE::CAP_QUOTA) + throw Insufficient_cap_quota(); + + /* + * Account for the dataspace capability needed for allocating the + * session object from the sliced heap. + */ + if (cap_quota.value < 1) + throw Insufficient_cap_quota(); + + Cap_quota const remaining_cap_quota { cap_quota.value - 1 }; + /* * Deduce ram quota needed for allocating the session object from the * donated ram quota. @@ -162,9 +180,13 @@ class Genode::Root_component : public Rpc_object >, Arg_string::set_arg(adjusted_args, sizeof(adjusted_args), "ram_quota", String<64>(remaining_ram_quota).string()); + Arg_string::set_arg(adjusted_args, sizeof(adjusted_args), + "cap_quota", String<64>(remaining_cap_quota).string()); + SESSION_TYPE *s = 0; try { s = _create_session(adjusted_args, affinity); } catch (Out_of_ram) { throw Insufficient_ram_quota(); } + catch (Out_of_caps) { throw Insufficient_cap_quota(); } /* * Consider that the session-object constructor may already have @@ -281,7 +303,9 @@ class Genode::Root_component : public Rpc_object >, { try { return _create(args, affinity); } + catch (Insufficient_ram_quota) { throw; } + catch (Insufficient_cap_quota) { throw; } catch (...) { throw typename Local_service::Factory::Denied(); } } diff --git a/repos/base/include/root/root.h b/repos/base/include/root/root.h index 585d297ef..fd58d42c5 100644 --- a/repos/base/include/root/root.h +++ b/repos/base/include/root/root.h @@ -33,10 +33,8 @@ struct Genode::Root ** Exception types ** *********************/ - class Exception : public ::Genode::Exception { }; - class Unavailable : public Exception { }; - class Quota_exceeded : public Exception { }; - class Invalid_args : public Exception { }; + class Unavailable : public Exception { }; + class Invalid_args : public Exception { }; typedef Rpc_in_buffer<160> Session_args; typedef Rpc_in_buffer<160> Upgrade_args; @@ -48,6 +46,7 @@ struct Genode::Root * * \throw Unavailable * \throw Insufficient_ram_quota + * \throw Insufficient_cap_quota * \throw Invalid_args * * \return capability to new session @@ -72,7 +71,7 @@ struct Genode::Root GENODE_RPC_THROW(Rpc_session, Session_capability, session, GENODE_TYPE_LIST(Unavailable, Insufficient_ram_quota, - Invalid_args), + Insufficient_cap_quota, Invalid_args), Session_args const &, Affinity const &); GENODE_RPC_THROW(Rpc_upgrade, void, upgrade, GENODE_TYPE_LIST(Invalid_args), diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 3cb2bf6b9..6994b5eab 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -188,7 +188,7 @@ _ZN6Genode18server_socket_pairEv T _ZN6Genode20env_session_id_spaceEv T _ZN6Genode25env_stack_area_region_mapE B 4 _ZN6Genode26env_stack_area_ram_sessionE B 4 -_ZN6Genode29upgrade_pd_quota_non_blockingEm T +_ZN6Genode29upgrade_pd_quota_non_blockingENS_9Ram_quotaENS_9Cap_quotaE T _ZN6Genode3Log3logEv T _ZN6Genode3Log8_acquireENS0_4TypeE T _ZN6Genode3Log8_releaseEv T diff --git a/repos/base/src/core/include/core_env.h b/repos/base/src/core/include/core_env.h index 397396cd2..ad62f5927 100644 --- a/repos/base/src/core/include/core_env.h +++ b/repos/base/src/core/include/core_env.h @@ -112,11 +112,10 @@ namespace Genode { bool _init_stack_area() { init_stack_area(); return true; } bool _stack_area_initialized = _init_stack_area(); - Rpc_entrypoint _entrypoint; - Core_region_map _region_map; - Ram_session_component _ram_session; - Ram_session_capability _ram_session_cap; - Synced_ram_session _synced_ram_session { _ram_session }; + Rpc_entrypoint _entrypoint; + Core_region_map _region_map; + Ram_session_component _ram_session; + Synced_ram_session _synced_ram_session { _ram_session }; /* * The core-local PD session is provided by a real RPC object @@ -159,7 +158,8 @@ namespace Genode { _region_map, Ram_session_component::any_phys_range()), _pd_session_component(_entrypoint), - _pd_session_client(_entrypoint.manage(&_pd_session_component)) + _pd_session_client(_pd_session_component.cap()), + _heap(_ram_session, _region_map) { _ram_session.init_ram_account(); } @@ -192,7 +192,7 @@ namespace Genode { Cpu_session_capability cpu_session_cap() override { - warning(__func__, " not implemented"); + warning(__FILE__, ":", __LINE__, " not implemented"); return Cpu_session_capability(); } diff --git a/repos/base/src/core/include/core_pd_session.h b/repos/base/src/core/include/core_pd_session.h index a07b99296..f95bbd109 100644 --- a/repos/base/src/core/include/core_pd_session.h +++ b/repos/base/src/core/include/core_pd_session.h @@ -30,20 +30,17 @@ class Genode::Core_pd_session_component : public Rpc_object { private: - Rpc_entrypoint &_signal_source_ep; + Rpc_entrypoint &_ep; public: /** * Constructor - * - * \param context_ep entrypoint that serves the signal-source - * components */ - Core_pd_session_component(Rpc_entrypoint &signal_source_ep) - : - _signal_source_ep(signal_source_ep) - { } + Core_pd_session_component(Rpc_entrypoint &ep) : _ep(ep) + { + ep.manage(this); + } void assign_parent(Capability parent) override { @@ -83,7 +80,7 @@ class Genode::Core_pd_session_component : public Rpc_object void submit(Capability cap, unsigned cnt = 1) override { - _signal_source_ep.apply(cap, [&] (Signal_context_component *context) { + _ep.apply(cap, [&] (Signal_context_component *context) { if (!context) { warning("invalid signal-context capability"); return; @@ -103,11 +100,17 @@ class Genode::Core_pd_session_component : public Rpc_object ASSERT_NEVER_CALLED; } - Capability address_space() { ASSERT_NEVER_CALLED; } + Capability address_space() override { ASSERT_NEVER_CALLED; } + Capability stack_area() override { ASSERT_NEVER_CALLED; } + Capability linker_area() override { ASSERT_NEVER_CALLED; } - Capability stack_area() { ASSERT_NEVER_CALLED; } + void ref_account(Capability) override { ASSERT_NEVER_CALLED; } - Capability linker_area() { ASSERT_NEVER_CALLED; } + void transfer_quota(Capability, Cap_quota) override { + ASSERT_NEVER_CALLED; } + + Cap_quota cap_quota() const { ASSERT_NEVER_CALLED; } + Cap_quota used_caps() const { ASSERT_NEVER_CALLED; } Capability native_pd() override { ASSERT_NEVER_CALLED; } }; diff --git a/repos/base/src/core/include/pd_root.h b/repos/base/src/core/include/pd_root.h index 018d52ece..fa46d834b 100644 --- a/repos/base/src/core/include/pd_root.h +++ b/repos/base/src/core/include/pd_root.h @@ -29,27 +29,29 @@ class Genode::Pd_root : public Genode::Root_componentupgrade_ram_quota(ram_quota); + pd->Ram_quota_guard::upgrade(ram_quota_from_args(args)); + pd->Cap_quota_guard::upgrade(cap_quota_from_args(args)); + pd->session_quota_upgraded(); } public: @@ -57,16 +59,20 @@ class Genode::Pd_root : public Genode::Root_component(session_ep, md_alloc), - _thread_ep(*thread_ep), _pager_ep(pager_ep), _md_alloc(*md_alloc) { } + Ram_allocator &ram_alloc, + Region_map &local_rm, + Allocator &md_alloc) + : + Root_component(&ep, &md_alloc), + _ep(ep), _pager_ep(pager_ep), _ram_alloc(ram_alloc), _local_rm(local_rm) + { } }; #endif /* _CORE__INCLUDE__PD_ROOT_H_ */ diff --git a/repos/base/src/core/include/pd_session_component.h b/repos/base/src/core/include/pd_session_component.h index 1e6cc3ac4..2a46db2be 100644 --- a/repos/base/src/core/include/pd_session_component.h +++ b/repos/base/src/core/include/pd_session_component.h @@ -17,9 +17,11 @@ #define _CORE__INCLUDE__PD_SESSION_COMPONENT_H_ /* Genode includes */ +#include #include -#include -#include +#include +#include +#include #include #include @@ -33,88 +35,111 @@ #include #include #include +#include namespace Genode { class Pd_session_component; } -class Genode::Pd_session_component : public Rpc_object +class Genode::Pd_session_component : public Session_object { private: - /** - * Read and store the PD label - */ - struct Label { - - enum { MAX_LEN = 64 }; - char string[MAX_LEN]; - - Label(char const *args) - { - Arg_string::find_arg(args, "label").string(string, - sizeof(string), ""); - } - } const _label; - - Allocator_guard _md_alloc; /* guarded meta-data allocator */ - Platform_pd _pd; - Capability _parent; - Rpc_entrypoint &_thread_ep; - Pager_entrypoint &_pager_ep; - Signal_broker _signal_broker; - Rpc_cap_factory _rpc_cap_factory; - Native_pd_component _native_pd; + Constrained_ram_allocator _constrained_md_ram_alloc; + Sliced_heap _sliced_heap; + Platform_pd _pd { &_sliced_heap, _label.string() }; + Capability _parent; + Rpc_entrypoint &_ep; + Pager_entrypoint &_pager_ep; + Signal_broker _signal_broker { _sliced_heap, _ep, _ep }; + Rpc_cap_factory _rpc_cap_factory { _sliced_heap }; + Native_pd_component _native_pd; Region_map_component _address_space; Region_map_component _stack_area; Region_map_component _linker_area; - size_t _ram_quota(char const * args) { - return Arg_string::find_arg(args, "ram_quota").long_value(0); } + Constructible > _cap_account; friend class Native_pd_component; + enum Cap_type { RPC_CAP, SIG_SOURCE_CAP, SIG_CONTEXT_CAP, IGN_CAP }; + + char const *_name(Cap_type type) + { + switch (type) { + case RPC_CAP: return "RPC"; + case SIG_SOURCE_CAP: return "signal-source"; + case SIG_CONTEXT_CAP: return "signal-context"; + default: return ""; + } + } + + /* + * \throw Out_of_caps + */ + void _consume_cap(Cap_type type) + { + try { Cap_quota_guard::withdraw(Cap_quota{1}); } + catch (Out_of_caps) { + diag("out of caps while consuming ", _name(type), " cap " + "(", _cap_account, ")"); + throw; + } + diag("consumed ", _name(type), " cap (", _cap_account, ")"); + } + + void _released_cap_silent() { Cap_quota_guard::replenish(Cap_quota{1}); } + + void _released_cap(Cap_type type) + { + _released_cap_silent(); + diag("released ", _name(type), " cap (", _cap_account, ")"); + } + public: /** * Constructor * - * \param receiver_ep entrypoint holding signal-receiver component - * objects - * \param context_ep global pool of all signal contexts - * \param md_alloc backing-store allocator for - * signal-context component objects - * - * To maintain proper synchronization, 'receiver_ep' must be - * the same entrypoint as used for the signal-session component. - * The 'signal_context_ep' is only used for associative array - * to map signal-context capabilities to 'Signal_context_component' - * objects and as capability allocator for such objects. + * \param ep entrypoint holding signal-receiver component + * objects, signal contexts, thread objects + * \param ram_alloc backing store for dynamically allocated + * session meta data */ - Pd_session_component(Rpc_entrypoint &thread_ep, - Rpc_entrypoint &receiver_ep, - Rpc_entrypoint &context_ep, - Allocator &md_alloc, + Pd_session_component(Rpc_entrypoint &ep, + Resources resources, + Label const &label, + Diag diag, + Ram_allocator &ram_alloc, + Region_map &local_rm, Pager_entrypoint &pager_ep, char const *args) : - _label(args), - _md_alloc(&md_alloc, _ram_quota(args)), - _pd(&_md_alloc, _label.string), - _thread_ep(thread_ep), _pager_ep(pager_ep), - _signal_broker(_md_alloc, receiver_ep, context_ep), - _rpc_cap_factory(_md_alloc), + Session_object(ep, resources, label, diag), + _constrained_md_ram_alloc(ram_alloc, *this, *this), + _sliced_heap(_constrained_md_ram_alloc, local_rm), + _ep(ep), _pager_ep(pager_ep), + _rpc_cap_factory(_sliced_heap), _native_pd(*this, args), - _address_space(thread_ep, _md_alloc, pager_ep, + _address_space(ep, _sliced_heap, pager_ep, platform()->vm_start(), platform()->vm_size()), - _stack_area(thread_ep, _md_alloc, pager_ep, 0, stack_area_virtual_size()), - _linker_area(thread_ep, _md_alloc, pager_ep, 0, LINKER_AREA_SIZE) + _stack_area (_ep, _sliced_heap, pager_ep, 0, stack_area_virtual_size()), + _linker_area(_ep, _sliced_heap, pager_ep, 0, LINKER_AREA_SIZE) { } /** - * Register quota donation at allocator guard + * Initialize cap account without providing a reference account + * + * This method is solely used to set up the initial PD session within + * core. The cap accounts of regular PD session are initialized via + * 'ref_account'. */ - void upgrade_ram_quota(size_t ram_quota); + void init_cap_account() { _cap_account.construct(*this, _label); } + + /** + * Session_object interface + */ + void session_quota_upgraded() override; /** * Associate thread with PD @@ -135,8 +160,6 @@ class Genode::Pd_session_component : public Rpc_object return _address_space; } - Session_label label() { return Session_label(_label.string); } - /************************** ** PD session interface ** @@ -147,42 +170,62 @@ class Genode::Pd_session_component : public Rpc_object Signal_source_capability alloc_signal_source() override { - try { - return _signal_broker.alloc_signal_source(); } + _consume_cap(SIG_SOURCE_CAP); + try { return _signal_broker.alloc_signal_source(); } catch (Genode::Allocator::Out_of_memory) { - throw Pd_session::Out_of_metadata(); } + _released_cap_silent(); + throw Out_of_ram(); + } } - void free_signal_source(Signal_source_capability sig_rec_cap) override { - _signal_broker.free_signal_source(sig_rec_cap); } + void free_signal_source(Signal_source_capability sig_rec_cap) override + { + _signal_broker.free_signal_source(sig_rec_cap); + _released_cap(SIG_SOURCE_CAP); + } Signal_context_capability alloc_context(Signal_source_capability sig_rec_cap, unsigned long imprint) override { + Cap_quota_guard::Reservation cap_costs(*this, Cap_quota{1}); try { - return _signal_broker.alloc_context(sig_rec_cap, imprint); } + Signal_context_capability cap = + _signal_broker.alloc_context(sig_rec_cap, imprint); + + cap_costs.acknowledge(); + diag("consumed signal-context cap (", _cap_account, ")"); + return cap; + } catch (Genode::Allocator::Out_of_memory) { - throw Pd_session::Out_of_metadata(); } + throw Out_of_ram(); } catch (Signal_broker::Invalid_signal_source) { throw Pd_session::Invalid_signal_source(); } } - void free_context(Signal_context_capability cap) override { - _signal_broker.free_context(cap); } + void free_context(Signal_context_capability cap) override + { + _signal_broker.free_context(cap); + _released_cap(SIG_CONTEXT_CAP); + } void submit(Signal_context_capability cap, unsigned n) override { _signal_broker.submit(cap, n); } + /* + * \throw Out_of_caps by '_consume_cap' + * \throw Out_of_ram by '_rpc_cap_factory.alloc' + */ Native_capability alloc_rpc_cap(Native_capability ep) override { - try { - return _rpc_cap_factory.alloc(ep); } - catch (Genode::Allocator::Out_of_memory) { - throw Pd_session::Out_of_metadata(); } + _consume_cap(RPC_CAP); + return _rpc_cap_factory.alloc(ep); } - void free_rpc_cap(Native_capability cap) override { - _rpc_cap_factory.free(cap); } + void free_rpc_cap(Native_capability cap) override + { + _rpc_cap_factory.free(cap); + _released_cap(RPC_CAP); + } Capability address_space() { return _address_space.cap(); } @@ -193,6 +236,65 @@ class Genode::Pd_session_component : public Rpc_object Capability linker_area() { return _linker_area.cap(); } + void ref_account(Capability pd_cap) override + { + /* the reference account can be defined only once */ + if (_cap_account.constructed()) + return; + + if (this->cap() == pd_cap) + return; + + _ep.apply(pd_cap, [&] (Pd_session_component *pd) { + + if (!pd || !pd->_cap_account.constructed()) { + error("invalid PD session specified as ref account"); + throw Invalid_session(); + } + + _cap_account.construct(*this, _label, *pd->_cap_account); + }); + } + + void transfer_quota(Capability pd_cap, Cap_quota amount) override + { + /* the reference account can be defined only once */ + if (!_cap_account.constructed()) + throw Undefined_ref_account(); + + if (this->cap() == pd_cap) + return; + + _ep.apply(pd_cap, [&] (Pd_session_component *pd) { + + if (!pd || !pd->_cap_account.constructed()) + throw Invalid_session(); + + try { + _cap_account->transfer_quota(*pd->_cap_account, amount); + diag("transferred ", amount, " caps " + "to '", pd->_cap_account->label(), "' (", _cap_account, ")"); + } + catch (Account::Unrelated_account) { + warning("attempt to transfer cap quota to unrelated PD session"); + throw Invalid_session(); } + catch (Account::Limit_exceeded) { + warning("cap limit (", *_cap_account, ") exceeded " + "during transfer_quota(", amount, ")"); + throw Out_of_caps(); } + }); + } + + Cap_quota cap_quota() const + { + return _cap_account.constructed() ? _cap_account->limit() : Cap_quota { 0 }; + } + + Cap_quota used_caps() const + { + return _cap_account.constructed() ? _cap_account->used() : Cap_quota { 0 }; + } + Capability native_pd() { return _native_pd.cap(); } }; diff --git a/repos/base/src/core/include/trace/subject_registry.h b/repos/base/src/core/include/trace/subject_registry.h index 4ea2f0f80..2b636e9b8 100644 --- a/repos/base/src/core/include/trace/subject_registry.h +++ b/repos/base/src/core/include/trace/subject_registry.h @@ -387,7 +387,7 @@ class Genode::Trace::Subject_registry } /** - * \throw Ram_session::Quota_exceeded + * \throw Out_of_ram */ void import_new_sources(Source_registry &sources) { diff --git a/repos/base/src/core/main.cc b/repos/base/src/core/main.cc index 7d01cba58..bf43cb637 100644 --- a/repos/base/src/core/main.cc +++ b/repos/base/src/core/main.cc @@ -125,12 +125,16 @@ class Core_child : public Child_policy Registry &_services; + Capability _core_pd_cap; + Pd_session &_core_pd; + Capability _core_ram_cap; Ram_session &_core_ram; Capability _core_cpu_cap; Cpu_session &_core_cpu; + Cap_quota const _cap_quota; Ram_quota const _ram_quota; Child _child; @@ -141,14 +145,17 @@ class Core_child : public Child_policy * Constructor */ Core_child(Registry &services, + Pd_session &core_pd, Capability core_pd_cap, Ram_session &core_ram, Capability core_ram_cap, Cpu_session &core_cpu, Capability core_cpu_cap, - Ram_quota ram_quota) + Cap_quota cap_quota, Ram_quota ram_quota) : _entrypoint(nullptr, STACK_SIZE, "init_child", false), _services(services), + _core_pd_cap (core_pd_cap), _core_pd (core_pd), _core_ram_cap(core_ram_cap), _core_ram(core_ram), _core_cpu_cap(core_cpu_cap), _core_cpu(core_cpu), + _cap_quota(Child::effective_quota(cap_quota)), _ram_quota(Child::effective_quota(ram_quota)), _child(*env_deprecated()->rm_session(), _entrypoint, *this) { @@ -176,6 +183,12 @@ class Core_child : public Child_policy return *service; } + void init(Pd_session &session, Capability cap) override + { + session.ref_account(_core_pd_cap); + _core_pd.transfer_quota(cap, _cap_quota); + } + void init(Ram_session &session, Capability cap) override { session.ref_account(_core_ram_cap); @@ -188,6 +201,9 @@ class Core_child : public Child_policy _core_cpu.transfer_quota(cap, Cpu_session::quota_lim_upscale(100, 100)); } + Pd_session &ref_pd() { return _core_pd; } + Pd_session_capability ref_pd_cap() const { return _core_pd_cap; } + Ram_session &ref_ram() { return _core_ram; } Ram_session_capability ref_ram_cap() const { return _core_ram_cap; } @@ -271,7 +287,7 @@ int main() static Rm_root rm_root (e, &sliced_heap, pager_ep); static Cpu_root cpu_root (e, e, &pager_ep, &sliced_heap, Trace::sources()); - static Pd_root pd_root (e, e, pager_ep, &sliced_heap); + static Pd_root pd_root (*e, pager_ep, core_ram_alloc, local_rm, sliced_heap); static Log_root log_root (e, &sliced_heap); static Io_mem_root io_mem_root (e, e, platform()->io_mem_alloc(), platform()->ram_alloc(), &sliced_heap); @@ -292,7 +308,27 @@ int main() /* make platform-specific services known to service pool */ platform_add_local_services(e, &sliced_heap, &services); - /* create CPU session representing core */ + /* calculate number of capabilities to be assigned to init */ + size_t const preservered_cap_quota = 1000; + + if (platform()->max_caps() < preservered_cap_quota) { + error("platform cap limit lower than preservation for core"); + return -1; + } + + size_t const avail_cap_quota = platform()->max_caps() - preservered_cap_quota; + + /* PD session representing core */ + static Pd_session_component + core_pd(*e, + Session::Resources { Ram_quota { 16*1024 }, + Cap_quota { avail_cap_quota } }, + Session::Label("core"), Session::Diag{false}, + core_ram_alloc, local_rm, pager_ep, ""); + + core_pd.init_cap_account(); + + /* CPU session representing core */ static Cpu_session_component core_cpu(e, e, &pager_ep, &sliced_heap, Trace::sources(), "label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT); @@ -309,14 +345,15 @@ int main() size_t const avail_ram_quota = platform_ram_limit - preserved_ram_quota; - log("", avail_ram_quota / (1024*1024), " MiB RAM " + log("", avail_ram_quota / (1024*1024), " MiB RAM and ", avail_cap_quota, " caps " "assigned to init"); static Reconstructible init(services, + core_pd, core_pd.cap(), *env_deprecated()->ram_session(), env_deprecated()->ram_session_cap(), core_cpu, core_cpu_cap, - Ram_quota{avail_ram_quota}); + core_pd.cap_quota(), Ram_quota{avail_ram_quota}); platform()->wait_for_exit(); diff --git a/repos/base/src/core/pd_upgrade_ram_quota.cc b/repos/base/src/core/pd_upgrade_ram_quota.cc index 681dfe6c0..456303f93 100644 --- a/repos/base/src/core/pd_upgrade_ram_quota.cc +++ b/repos/base/src/core/pd_upgrade_ram_quota.cc @@ -17,8 +17,5 @@ using namespace Genode; -void Pd_session_component::upgrade_ram_quota(size_t ram_quota) -{ - _md_alloc.upgrade(ram_quota); -} +void Pd_session_component::session_quota_upgraded() { } diff --git a/repos/base/src/core/ram_session_component.cc b/repos/base/src/core/ram_session_component.cc index ae8024773..7ab1191c8 100644 --- a/repos/base/src/core/ram_session_component.cc +++ b/repos/base/src/core/ram_session_component.cc @@ -60,8 +60,10 @@ void Ram_session_component::_free_ds(Dataspace_capability ds_cap) }); /* call dataspace destructors and free memory */ - if (ds) + if (ds) { destroy(*_ds_slab, ds); + Cap_quota_guard::replenish(Cap_quota{1}); + } } @@ -92,6 +94,11 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, Cache_attr Ram_quota_guard::Reservation sbs_ram_costs(*this, Ram_quota{SBS}); } + /* + * Each dataspace is an RPC object and thereby consumes a capability. + */ + Cap_quota_guard::Reservation dataspace_cap_costs(*this, Cap_quota{1}); + /* * Allocate physical backing store * @@ -191,6 +198,7 @@ Ram_dataspace_capability Ram_session_component::alloc(size_t ds_size, Cache_attr Dataspace_capability result = _ep.manage(ds); dataspace_ram_costs.acknowledge(); + dataspace_cap_costs.acknowledge(); phys_alloc_guard.ack = true; return static_cap_cast(result); diff --git a/repos/base/src/core/spec/x86/io_port_session_component.cc b/repos/base/src/core/spec/x86/io_port_session_component.cc index 22ad51131..c807d8993 100644 --- a/repos/base/src/core/spec/x86/io_port_session_component.cc +++ b/repos/base/src/core/spec/x86/io_port_session_component.cc @@ -43,11 +43,6 @@ Io_port_session_component::Io_port_session_component(Range_allocator *io_port_al case Range_allocator::Alloc_return::OUT_OF_METADATA: error("I/O port allocator ran out of meta data"); - - /* - * Do not throw 'Quota_exceeded' because the client cannot do - * anything about the meta data allocator of I/O ports. - */ throw Root::Invalid_args(); case Range_allocator::Alloc_return::OK: break; diff --git a/repos/base/src/include/base/internal/expanding_ram_session_client.h b/repos/base/src/include/base/internal/expanding_ram_session_client.h index 979d8216e..dbd39aa6e 100644 --- a/repos/base/src/include/base/internal/expanding_ram_session_client.h +++ b/repos/base/src/include/base/internal/expanding_ram_session_client.h @@ -31,8 +31,17 @@ struct Genode::Expanding_ram_session_client : Upgradeable_clientparent(); parent.resource_request(String<128>("ram_quota=", amount).string()); } + + void _request_caps_from_parent(size_t amount) + { + Parent &parent = *env_deprecated()->parent(); + parent.resource_request(String<128>("cap_quota=", amount).string()); + } + Expanding_ram_session_client(Ram_session_capability cap, Parent::Client::Id id) - : Upgradeable_client(cap, id) { } + : + Upgradeable_client(cap, id) + { } Ram_dataspace_capability alloc(size_t size, Cache_attribute cached = UNCACHED) override { @@ -41,8 +50,20 @@ struct Genode::Expanding_ram_session_client : Upgradeable_client( - [&] () { return Ram_session_client::alloc(size, cached); }, + [&] () { + return retry( + [&] () { return Ram_session_client::alloc(size, cached); }, + [&] () { + try { upgrade_caps(UPGRADE_CAPS); } + catch (Out_of_caps) { + warning("cap quota exhausted, issuing resource request to parent"); + _request_caps_from_parent(UPGRADE_CAPS); + } + }, + NUM_ATTEMPTS); + }, [&] () { /* * The RAM service withdraws the meta data for the allocator diff --git a/repos/base/src/include/base/internal/platform_env.h b/repos/base/src/include/base/internal/platform_env.h index f16023746..f8c30b2e2 100644 --- a/repos/base/src/include/base/internal/platform_env.h +++ b/repos/base/src/include/base/internal/platform_env.h @@ -115,8 +115,8 @@ class Genode::Platform_env : public Env_deprecated, ** Emergency_ram_reserve interface ** *************************************/ - void release() { - + void release() + { log("used before freeing emergency=", _resources.ram.used_ram()); _resources.ram.free(_emergency_ram_ds); log("used after freeing emergency=", _resources.ram.used_ram()); diff --git a/repos/base/src/include/base/internal/upgradeable_client.h b/repos/base/src/include/base/internal/upgradeable_client.h index 70d7f4ef9..3666d7ea9 100644 --- a/repos/base/src/include/base/internal/upgradeable_client.h +++ b/repos/base/src/include/base/internal/upgradeable_client.h @@ -35,10 +35,12 @@ struct Genode::Upgradeable_client : CLIENT void upgrade_ram(size_t quota) { - char buf[128]; - snprintf(buf, sizeof(buf), "ram_quota=%lu", quota); + env_deprecated()->parent()->upgrade(_id, String<64>("ram_quota=", quota).string()); + } - env_deprecated()->parent()->upgrade(_id, buf); + void upgrade_caps(size_t quota) + { + env_deprecated()->parent()->upgrade(_id, String<64>("cap_quota=", quota).string()); } }; diff --git a/repos/base/src/lib/base/child.cc b/repos/base/src/lib/base/child.cc index 2b2e69490..5db391e00 100644 --- a/repos/base/src/lib/base/child.cc +++ b/repos/base/src/lib/base/child.cc @@ -75,6 +75,7 @@ void Child::session_sigh(Signal_context_capability sigh) if (session.phase == Session_state::AVAILABLE || session.phase == Session_state::INSUFFICIENT_RAM_QUOTA || + session.phase == Session_state::INSUFFICIENT_CAP_QUOTA || session.phase == Session_state::INVALID_ARGS) { if (sigh.valid() && session.async_client_notify) @@ -88,6 +89,7 @@ void Child::session_sigh(Signal_context_capability sigh) * Create session-state object for a dynamically created session * * \throw Out_of_ram + * \throw Insufficient_cap_quota * \throw Insufficient_ram_quota * \throw Parent::Service_denied */ @@ -104,6 +106,11 @@ create_session(Child_policy::Name const &child_name, Service &service, catch (Insufficient_ram_quota) { error(child_name, " requested session with insufficient RAM quota"); throw; } + + catch (Insufficient_cap_quota) { + error(child_name, " requested session with insufficient cap quota"); + throw; } + catch (Allocator::Out_of_memory) { error(child_name, " session meta data could not be allocated"); throw Out_of_ram(); } @@ -170,6 +177,7 @@ Session_capability Child::session(Parent::Client::Id id, /* filter session affinity */ Affinity const filtered_affinity = _policy.filter_session_affinity(affinity); + Cap_quota const cap_quota = cap_quota_from_args(argbuf); Ram_quota const ram_quota = ram_quota_from_args(argbuf); /* portion of quota to keep for ourself to maintain the session meta data */ @@ -202,13 +210,18 @@ Session_capability Child::session(Parent::Client::Id id, try { Ram_transfer::Remote_account ref_ram_account { _policy.ref_ram(), _policy.ref_ram_cap() }; + Cap_transfer::Remote_account ref_cap_account { _policy.ref_pd(), _policy.ref_pd_cap() }; + Ram_transfer::Remote_account ram_account { ram(), ram_session_cap() }; + Cap_transfer::Remote_account cap_account { pd(), pd_session_cap() }; /* transfer the quota donation from the child's account to ourself */ Ram_transfer ram_donation_from_child(ram_quota, ram_account, ref_ram_account); + Cap_transfer cap_donation_from_child(cap_quota, cap_account, ref_cap_account); /* transfer session quota from ourself to the service provider */ Ram_transfer ram_donation_to_service(forward_ram_quota, ref_ram_account, service); + Cap_transfer cap_donation_to_service(cap_quota, ref_cap_account, service); /* try to dispatch session request synchronously */ service.initiate_request(session); @@ -223,9 +236,16 @@ Session_capability Child::session(Parent::Client::Id id, throw Insufficient_ram_quota(); } + if (session.phase == Session_state::INSUFFICIENT_CAP_QUOTA) { + _revert_quota_and_destroy(session); + throw Insufficient_cap_quota(); + } + /* finish transaction */ ram_donation_from_child.acknowledge(); + cap_donation_from_child.acknowledge(); ram_donation_to_service.acknowledge(); + cap_donation_to_service.acknowledge(); } /* * Release session meta data if one of the quota transfers went wrong. @@ -234,6 +254,10 @@ Session_capability Child::session(Parent::Client::Id id, session.destroy(); throw Out_of_ram(); } + catch (Cap_transfer::Quota_exceeded) { + session.destroy(); + throw Out_of_caps(); + } /* * Copy out the session cap before we are potentially kicking off the @@ -261,7 +285,8 @@ Session_capability Child::session_cap(Client::Id id) auto lamda = [&] (Session_state &session) { if (session.phase == Session_state::INVALID_ARGS - || session.phase == Session_state::INSUFFICIENT_RAM_QUOTA) { + || session.phase == Session_state::INSUFFICIENT_RAM_QUOTA + || session.phase == Session_state::INSUFFICIENT_CAP_QUOTA) { Session_state::Phase const phase = session.phase; @@ -275,6 +300,7 @@ Session_capability Child::session_cap(Client::Id id) switch (phase) { case Session_state::INVALID_ARGS: throw Parent::Service_denied(); case Session_state::INSUFFICIENT_RAM_QUOTA: throw Insufficient_ram_quota(); + case Session_state::INSUFFICIENT_CAP_QUOTA: throw Insufficient_cap_quota(); default: break; } } @@ -318,29 +344,43 @@ Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const Ram_quota const ram_quota { Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0) }; + Cap_quota const cap_quota { + Arg_string::find_arg(args.string(), "cap_quota").ulong_value(0) }; + try { Ram_transfer::Remote_account ref_ram_account { _policy.ref_ram(), _policy.ref_ram_cap() }; + Cap_transfer::Remote_account ref_cap_account { _policy.ref_pd(), _policy.ref_pd_cap() }; + Ram_transfer::Remote_account ram_account { ram(), ram_session_cap() }; + Cap_transfer::Remote_account cap_account { pd(), pd_session_cap() }; /* transfer quota from client to ourself */ Ram_transfer ram_donation_from_child(ram_quota, ram_account, ref_ram_account); + Cap_transfer cap_donation_from_child(cap_quota, cap_account, ref_cap_account); /* transfer session quota from ourself to the service provider */ Ram_transfer ram_donation_to_service(ram_quota, ref_ram_account, session.service()); + Cap_transfer cap_donation_to_service(cap_quota, ref_cap_account, session.service()); - session.increase_donated_quota(ram_quota); + session.increase_donated_quota(ram_quota, cap_quota); session.phase = Session_state::UPGRADE_REQUESTED; session.service().initiate_request(session); /* finish transaction */ ram_donation_from_child.acknowledge(); + cap_donation_from_child.acknowledge(); ram_donation_to_service.acknowledge(); + cap_donation_to_service.acknowledge(); } catch (Ram_transfer::Quota_exceeded) { warning(_policy.name(), ": RAM upgrade of ", session.service().name(), " failed"); throw Out_of_ram(); } + catch (Cap_transfer::Quota_exceeded) { + warning(_policy.name(), ": cap upgrade of ", session.service().name(), " failed"); + throw Out_of_caps(); + } if (session.phase == Session_state::CAP_HANDED_OUT) { result = UPGRADE_DONE; @@ -361,11 +401,18 @@ void Child::_revert_quota_and_destroy(Session_state &session) Ram_transfer::Account &service_ram_account = session.service(); Ram_transfer::Remote_account child_ram_account(ram(), ram_session_cap()); + Cap_transfer::Remote_account ref_cap_account(_policy.ref_pd(), _policy.ref_pd_cap()); + Cap_transfer::Account &service_cap_account = session.service(); + Cap_transfer::Remote_account child_cap_account(pd(), pd_session_cap()); + try { /* transfer session quota from the service to ourself */ Ram_transfer ram_donation_from_service(session.donated_ram_quota(), service_ram_account, ref_ram_account); + Cap_transfer cap_donation_from_service(session.donated_cap_quota(), + service_cap_account, ref_cap_account); + /* * Transfer session quota from ourself to the client (our child). In * addition to the quota returned from the server, we also return the @@ -377,9 +424,14 @@ void Child::_revert_quota_and_destroy(Session_state &session) Ram_transfer ram_donation_to_client(returned_ram, ref_ram_account, child_ram_account); + Cap_transfer cap_donation_to_client(session.donated_cap_quota(), + ref_cap_account, child_cap_account); + /* finish transaction */ ram_donation_from_service.acknowledge(); + cap_donation_from_service.acknowledge(); ram_donation_to_client.acknowledge(); + cap_donation_to_client.acknowledge(); } catch (Ram_transfer::Quota_exceeded) { warning(_policy.name(), ": could not revert session RAM quota (", session, ")"); } @@ -398,7 +450,8 @@ Child::Close_result Child::_close(Session_state &session) * without involving the server */ if (session.phase == Session_state::INVALID_ARGS - || session.phase == Session_state::INSUFFICIENT_RAM_QUOTA) { + || session.phase == Session_state::INSUFFICIENT_RAM_QUOTA + || session.phase == Session_state::INSUFFICIENT_CAP_QUOTA) { _revert_quota_and_destroy(session); return CLOSE_DONE; } @@ -503,6 +556,12 @@ void Child::session_response(Server::Id id, Session_response response) session.ready_callback->session_ready(session); break; + case Parent::INSUFFICIENT_CAP_QUOTA: + session.phase = Session_state::INSUFFICIENT_CAP_QUOTA; + if (session.ready_callback) + session.ready_callback->session_ready(session); + break; + case Parent::SESSION_OK: if (session.phase == Session_state::UPGRADE_REQUESTED) { session.phase = Session_state::CAP_HANDED_OUT; @@ -649,6 +708,7 @@ void Child::_try_construct_env_dependent_members() _parent_cap); } catch (Out_of_ram) { _error("out of RAM during ELF loading"); } + catch (Out_of_caps) { _error("out of caps 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"); } diff --git a/repos/base/src/lib/base/component.cc b/repos/base/src/lib/base/component.cc index ad27ca5c7..3c01aae62 100644 --- a/repos/base/src/lib/base/component.cc +++ b/repos/base/src/lib/base/component.cc @@ -123,24 +123,27 @@ namespace { * the route between client and server, the session quota provided * by the client may become successively diminished by intermediate * components, prompting the server to deny the session request. - * - * If the session creation failed due to insufficient session - * quota, we try to repeatedly increase the quota up to - * 'NUM_ATTEMPTS'. */ - enum { NUM_ATTEMPTS = 10 }; /* extract session quota as specified by the 'Connection' */ char argbuf[Parent::Session_args::MAX_SIZE]; strncpy(argbuf, args.string(), sizeof(argbuf)); - Ram_quota ram_quota = ram_quota_from_args(argbuf); - return retry( - [&] () { + Ram_quota ram_quota = ram_quota_from_args(argbuf); + Cap_quota cap_quota = cap_quota_from_args(argbuf); + + unsigned warn_after_attempts = 2; + + for (unsigned cnt = 0;; cnt++) { + + try { Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", String<32>(ram_quota).string()); + Arg_string::set_arg(argbuf, sizeof(argbuf), "cap_quota", + String<32>(cap_quota).string()); + Session_capability cap = _parent.session(id, name, Parent::Session_args(argbuf), affinity); @@ -149,30 +152,34 @@ namespace { _block_for_session(); return _parent.session_cap(id); - }, - [&] () { - /* - * If our RAM session has less quota available than the - * session quota, the session-quota transfer failed. In - * this case, we try to recover by issuing a resource - * request to the parent. - * - * Otherwise, the session-quota transfer succeeded but - * the request was denied by the server. - */ + } + + catch (Insufficient_ram_quota) { + ram_quota = Ram_quota { ram_quota.value + 4096 }; } + + catch (Insufficient_cap_quota) { + cap_quota = Cap_quota { cap_quota.value + 4 }; } + + catch (Out_of_ram) { if (ram_quota.value > ram().avail_ram().value) { Parent::Resource_args args(String<64>("ram_quota=", ram_quota)); _parent.resource_request(args); - } else { - ram_quota = Ram_quota { ram_quota.value + 4096 }; } - }, - NUM_ATTEMPTS); + } - warning("giving up to increase session quota for ", name.string(), " session " - "after ", (int)NUM_ATTEMPTS, " attempts"); + catch (Out_of_caps) { + if (cap_quota.value > pd().avail_caps().value) { + Parent::Resource_args args(String<64>("cap_quota=", cap_quota)); + _parent.resource_request(args); + } + } - throw Insufficient_ram_quota(); + if (cnt == warn_after_attempts) { + warning("re-attempted ", name.string(), " session request ", + cnt, " times (args: ", Cstring(argbuf), ")"); + warn_after_attempts *= 2; + } + } } void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override diff --git a/repos/base/src/lib/base/root_proxy.cc b/repos/base/src/lib/base/root_proxy.cc index da052e968..cba0c442a 100644 --- a/repos/base/src/lib/base/root_proxy.cc +++ b/repos/base/src/lib/base/root_proxy.cc @@ -187,6 +187,8 @@ void Root_proxy::_handle_session_request(Xml_node request) _env.parent().session_response(id, Parent::INVALID_ARGS); } catch (Insufficient_ram_quota) { _env.parent().session_response(id, Parent::INSUFFICIENT_RAM_QUOTA); } + catch (Insufficient_cap_quota) { + _env.parent().session_response(id, Parent::INSUFFICIENT_CAP_QUOTA); } catch (Root::Unavailable) { _env.parent().session_response(id, Parent::INVALID_ARGS); } } @@ -196,8 +198,9 @@ void Root_proxy::_handle_session_request(Xml_node request) _id_space.apply(id, [&] (Session &session) { Ram_quota const ram_quota { request.attribute_value("ram_quota", 0UL) }; + Cap_quota const cap_quota { request.attribute_value("cap_quota", 0UL) }; - String<80> const args("ram_quota=", ram_quota); + String<80> const args("ram_quota=", ram_quota, ", cap_quota=", cap_quota); Root_client(session.service.root).upgrade(session.cap, args.string()); diff --git a/repos/base/src/lib/base/rpc_cap_alloc.cc b/repos/base/src/lib/base/rpc_cap_alloc.cc index c63a6447e..e1cc305c5 100644 --- a/repos/base/src/lib/base/rpc_cap_alloc.cc +++ b/repos/base/src/lib/base/rpc_cap_alloc.cc @@ -23,13 +23,19 @@ using namespace Genode; Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd, Native_capability ep, addr_t) { - Untyped_capability new_obj_cap = - retry( - [&] () { return pd.alloc_rpc_cap(_cap); }, - [&] () { env_deprecated()->parent()->upgrade(Parent::Env::pd(), - "ram_quota=16K"); }); + for (;;) { - return new_obj_cap; + Ram_quota ram_upgrade { 0 }; + Cap_quota cap_upgrade { 0 }; + + try { return pd.alloc_rpc_cap(_cap); } + catch (Out_of_ram) { ram_upgrade = Ram_quota { 2*1024*sizeof(long) }; } + catch (Out_of_caps) { cap_upgrade = Cap_quota { 4 }; } + + env_deprecated()->parent()->upgrade(Parent::Env::pd(), + String<100>("ram_quota=", ram_upgrade, ", " + "cap_quota=", cap_upgrade).string()); + } } diff --git a/repos/base/src/lib/base/session_state.cc b/repos/base/src/lib/base/session_state.cc index f5da491fb..8f3c3e70c 100644 --- a/repos/base/src/lib/base/session_state.cc +++ b/repos/base/src/lib/base/session_state.cc @@ -33,6 +33,7 @@ struct Formatted_phase case State::CREATE_REQUESTED: print(output, "CREATE_REQUESTED"); break; case State::INVALID_ARGS: print(output, "INVALID_ARGS"); break; case State::INSUFFICIENT_RAM_QUOTA: print(output, "INSUFFICIENT_RAM_QUOTA"); break; + case State::INSUFFICIENT_CAP_QUOTA: print(output, "INSUFFICIENT_CAP_QUOTA"); break; case State::AVAILABLE: print(output, "AVAILABLE"); break; case State::CAP_HANDED_OUT: print(output, "CAP_HANDED_OUT"); break; case State::UPGRADE_REQUESTED: print(output, "UPGRADE_REQUESTED"); break; @@ -49,7 +50,7 @@ void Session_state::print(Output &out) const print(out, "service=", _service.name(), " cid=", _id_at_client, " " "args='", _args, "' state=", Formatted_phase(phase), " " - "ram_quota=", _donated_ram_quota); + "ram_quota=", _donated_ram_quota, ", cap_quota=", _donated_cap_quota); } @@ -77,6 +78,7 @@ void Session_state::generate_session_request(Xml_generator &xml) const xml.node("upgrade", [&] () { xml.attribute("id", id_at_server->id().value); xml.attribute("ram_quota", ram_upgrade.value); + xml.attribute("cap_quota", cap_upgrade.value); }); break; @@ -88,6 +90,7 @@ void Session_state::generate_session_request(Xml_generator &xml) const case INVALID_ARGS: case INSUFFICIENT_RAM_QUOTA: + case INSUFFICIENT_CAP_QUOTA: case AVAILABLE: case CAP_HANDED_OUT: case CLOSED: @@ -102,6 +105,7 @@ void Session_state::generate_client_side_info(Xml_generator &xml, Detail detail) xml.attribute("label", _label); xml.attribute("state", String<32>(Formatted_phase(phase))); xml.attribute("ram", String<32>(_donated_ram_quota)); + xml.attribute("caps", String<32>(_donated_cap_quota)); if (detail.args == Detail::ARGS) xml.node("args", [&] () { xml.append_sanitized(_args.string()); }); @@ -161,6 +165,7 @@ Session_state::Session_state(Service &service, : _service(service), _donated_ram_quota(ram_quota_from_args(args.string())), + _donated_cap_quota(cap_quota_from_args(args.string())), _id_at_client(*this, client_id_space, client_id), _label(label), _args(args), _affinity(affinity) { } diff --git a/repos/base/src/lib/base/signal.cc b/repos/base/src/lib/base/signal.cc index ab7303b85..a6b554fc7 100644 --- a/repos/base/src/lib/base/signal.cc +++ b/repos/base/src/lib/base/signal.cc @@ -229,21 +229,26 @@ Signal_context_capability Signal_receiver::manage(Signal_context *context) /* register context at process-wide registry */ signal_context_registry()->insert(&context->_registry_le); - retry( - [&] () { + for (;;) { + + Ram_quota ram_upgrade { 0 }; + Cap_quota cap_upgrade { 0 }; + + try { /* use signal context as imprint */ context->_cap = env_deprecated()->pd_session()->alloc_context(_cap, (long)context); - }, - [&] () { - size_t const quota = 1024*sizeof(long); - char buf[64]; - snprintf(buf, sizeof(buf), "ram_quota=%ld", quota); - - log("upgrading quota donation for PD session (", quota, " bytes)"); - - env_deprecated()->parent()->upgrade(Parent::Env::pd(), buf); + break; } - ); + catch (Out_of_ram) { ram_upgrade = Ram_quota { 1024*sizeof(long) }; } + catch (Out_of_caps) { cap_upgrade = Cap_quota { 4 }; } + + log("upgrading quota donation for PD session " + "(", ram_upgrade, " bytes, ", cap_upgrade, " caps)"); + + env_deprecated()->parent()->upgrade(Parent::Env::pd(), + String<100>("ram_quota=", ram_upgrade, ", " + "cap_quota=", cap_upgrade).string()); + } return context->_cap; } diff --git a/repos/base/src/lib/base/slab.cc b/repos/base/src/lib/base/slab.cc index 39fa829a8..cbfea202a 100644 --- a/repos/base/src/lib/base/slab.cc +++ b/repos/base/src/lib/base/slab.cc @@ -319,7 +319,6 @@ void Slab::insert_sb(void *ptr) bool Slab::alloc(size_t size, void **out_addr) { - /* too large for us ? */ if (size > _slab_size) { error("requested size ", size, " is larger then slab size ", diff --git a/repos/base/src/test/rm_fault/main.cc b/repos/base/src/test/rm_fault/main.cc index 6b5a693d2..0d3df446e 100644 --- a/repos/base/src/test/rm_fault/main.cc +++ b/repos/base/src/test/rm_fault/main.cc @@ -98,8 +98,10 @@ class Test_child_policy : public Child_policy Binary_name binary_name() const override { return "test-rm_fault"; } - Ram_session &ref_ram() override { return _env.ram(); } + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + Ram_session &ref_ram() override { return _env.ram(); } Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); } void init(Ram_session &session, Ram_session_capability cap) override @@ -111,6 +113,9 @@ class Test_child_policy : public Child_policy void init(Pd_session &session, Pd_session_capability cap) override { + session.ref_account(_env.pd_session_cap()); + _env.pd().transfer_quota(cap, Cap_quota{20}); + Region_map_client address_space(session.address_space()); address_space.fault_handler(_fault_handler_sigh); } diff --git a/repos/demo/include/launchpad/launchpad.h b/repos/demo/include/launchpad/launchpad.h index 311ffbdb2..e540b3fdc 100644 --- a/repos/demo/include/launchpad/launchpad.h +++ b/repos/demo/include/launchpad/launchpad.h @@ -59,6 +59,7 @@ class Launchpad_child : public Genode::Child_policy, Genode::Ram_session_capability _ref_ram_cap; Genode::Ram_session_client _ref_ram { _ref_ram_cap }; + Genode::Cap_quota const _cap_quota; Genode::Ram_quota const _ram_quota; Parent_services &_parent_services; @@ -97,6 +98,7 @@ class Launchpad_child : public Genode::Child_policy, Genode::Allocator &alloc, Genode::Session_label const &label, Binary_name const &elf_name, + Genode::Cap_quota cap_quota, Genode::Ram_quota ram_quota, Parent_services &parent_services, Child_services &child_services, @@ -105,6 +107,7 @@ class Launchpad_child : public Genode::Child_policy, _name(label), _elf_name(elf_name), _env(env), _alloc(alloc), _ref_ram_cap(env.ram_session_cap()), + _cap_quota(Genode::Child::effective_quota(cap_quota)), _ram_quota(Genode::Child::effective_quota(ram_quota)), _parent_services(parent_services), _child_services(child_services), @@ -133,9 +136,19 @@ class Launchpad_child : public Genode::Child_policy, Binary_name binary_name() const override { return _elf_name; } + Genode::Pd_session &ref_pd() override { return _env.pd(); } + Genode::Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + Genode::Ram_session &ref_ram() override { return _ref_ram; } Genode::Ram_session_capability ref_ram_cap() const override { return _ref_ram_cap; } + void init(Genode::Pd_session &session, + Genode::Pd_session_capability cap) override + { + session.ref_account(_env.pd_session_cap()); + _env.pd().transfer_quota(cap, _cap_quota); + } + void init(Genode::Ram_session &session, Genode::Ram_session_capability cap) override { @@ -200,7 +213,8 @@ class Launchpad_child : public Genode::Child_policy, Child_service(_child_services, service_name, _session_requester.id_space(), _child.session_factory(), *this, - _child.ram_session_cap()); + _child.ram_session_cap(), + _child.pd_session_cap()); } }; @@ -233,6 +247,7 @@ class Launchpad public: + typedef Genode::Cap_quota Cap_quota; typedef Genode::Ram_quota Ram_quota; Launchpad(Genode::Env &env, unsigned long initial_quota); @@ -254,7 +269,7 @@ class Launchpad virtual void quota(unsigned long quota) { } virtual void add_launcher(Launchpad_child::Name const &binary_name, - unsigned long default_quota, + Cap_quota caps, unsigned long default_quota, Genode::Dataspace_capability config_ds) { } virtual void add_child(Launchpad_child::Name const &, @@ -266,7 +281,7 @@ class Launchpad Genode::Allocator &) { } Launchpad_child *start_child(Launchpad_child::Name const &binary_name, - Ram_quota quota, + Cap_quota cap_quota, Ram_quota ram_quota, Genode::Dataspace_capability config_ds); /** diff --git a/repos/demo/src/app/launchpad/launch_entry.h b/repos/demo/src/app/launchpad/launch_entry.h index 81c50ec29..c71e42b9f 100644 --- a/repos/demo/src/app/launchpad/launch_entry.h +++ b/repos/demo/src/app/launchpad/launch_entry.h @@ -38,14 +38,15 @@ class Launch_entry : public Scout::Parent_element, public Loadbar_listener /** * Constructor */ - Launch_entry(Scout::Launcher::Name const &prg_name, unsigned long initial_quota, + Launch_entry(Scout::Launcher::Name const &prg_name, + unsigned long caps, unsigned long initial_quota, unsigned long max_quota, Launchpad *launchpad, Genode::Dataspace_capability config_ds) : _prg_name(prg_name), _block(Scout::Block::RIGHT), _loadbar(this, &Scout::label_font), _config(config_ds), - _launcher(prg_name, launchpad, initial_quota * 1024UL, &_config) + _launcher(prg_name, launchpad, caps, initial_quota * 1024UL, &_config) { _block.append_launchertext(_prg_name.string(), &Scout::link_style, &_launcher); diff --git a/repos/demo/src/app/launchpad/launcher.cc b/repos/demo/src/app/launchpad/launcher.cc index 63fb5a312..786f247bd 100644 --- a/repos/demo/src/app/launchpad/launcher.cc +++ b/repos/demo/src/app/launchpad/launcher.cc @@ -21,6 +21,7 @@ using namespace Scout; void Launcher::launch() { _launchpad->start_child(prg_name(), + Launchpad::Cap_quota{caps()}, Launchpad::Ram_quota{quota()}, _config ? _config->config_ds() : Genode::Dataspace_capability()); diff --git a/repos/demo/src/app/launchpad/launchpad_window.h b/repos/demo/src/app/launchpad/launchpad_window.h index 41cad81dd..ca5e02924 100644 --- a/repos/demo/src/app/launchpad/launchpad_window.h +++ b/repos/demo/src/app/launchpad/launchpad_window.h @@ -127,12 +127,12 @@ class Launchpad_window : public Scout::Scrollbar_listener, _status_entry.refresh(); } - void add_launcher(Launchpad_child::Name const &name, + void add_launcher(Launchpad_child::Name const &name, Cap_quota caps, unsigned long default_quota, Genode::Dataspace_capability config_ds = Genode::Dataspace_capability()) override { Launch_entry *le; - le = new Launch_entry(name, default_quota / 1024, + le = new Launch_entry(name, caps.value, default_quota / 1024, initial_quota() / 1024, this, config_ds); _launch_section.append(le); diff --git a/repos/demo/src/app/scout/doc.cc b/repos/demo/src/app/scout/doc.cc index 27843242e..7d5c481eb 100644 --- a/repos/demo/src/app/scout/doc.cc +++ b/repos/demo/src/app/scout/doc.cc @@ -149,7 +149,7 @@ Document *create_document() b0->append_plaintext("that can be started by clicking on the application's name. Before starting an", &plain_style); b0->append_plaintext("application, the user can define the amount of memory quota to donate to the", &plain_style); b0->append_plaintext("new application by adjusting the red bar using the mouse.", &plain_style); - Launcher *l0 = new Launcher("launchpad", 1, 22*1024*1024); + Launcher *l0 = new Launcher("launchpad", 1, 100000, 22*1024*1024); b0->append_launchertext("Start the launchpad by clicking on this link...", &link_style, l0); chapter->append(b0); diff --git a/repos/demo/src/app/scout/elements.h b/repos/demo/src/app/scout/elements.h index 438cdb6a2..b31698d75 100644 --- a/repos/demo/src/app/scout/elements.h +++ b/repos/demo/src/app/scout/elements.h @@ -227,12 +227,13 @@ class Scout::Launcher : public Anchor private: - Name _prg_name; - int _active; - int _exec_once; - Launchpad *_launchpad; - unsigned long _quota; - Launcher_config *_config; + Name _prg_name; + int _active; + int _exec_once; + Launchpad *_launchpad; + unsigned long const _caps; + unsigned long _quota; + Launcher_config *_config; public: @@ -242,22 +243,30 @@ class Scout::Launcher : public Anchor * Constructors */ Launcher(Name const &prg_name, int exec_once = 0, - unsigned long quota = 0, Launcher_config *config = 0) : + unsigned long caps = 0, unsigned long quota = 0, + Launcher_config *config = 0) + : _prg_name(prg_name), _active(1), - _exec_once(exec_once), _quota(quota), _config(config) { } + _exec_once(exec_once), _caps(caps), _quota(quota), _config(config) + { } Launcher(Name const &prg_name, Launchpad *launchpad, - unsigned long quota, Launcher_config *config = 0) : - _prg_name(prg_name), _launchpad(launchpad), _quota(quota), - _config(config) { } + unsigned long caps, unsigned long quota, + Launcher_config *config = 0) + : + _prg_name(prg_name), _launchpad(launchpad), + _caps(caps), _quota(quota), _config(config) + { } - int active() { return _active; } + int active() const { return _active; } - Name prg_name() { return _prg_name; } + Name prg_name() const { return _prg_name; } void quota(unsigned long quota) { _quota = quota; } - unsigned long quota() { return _quota; } + unsigned long quota() const { return _quota; } + + unsigned long caps() const { return _caps; } Launcher_config *config() { return _config; } diff --git a/repos/demo/src/app/scout/launcher.cc b/repos/demo/src/app/scout/launcher.cc index efc29bcea..9179c27b8 100644 --- a/repos/demo/src/app/scout/launcher.cc +++ b/repos/demo/src/app/scout/launcher.cc @@ -115,6 +115,7 @@ void Launcher::launch() } _launchpad_ptr->start_child(prg_name(), + Launchpad::Cap_quota{caps()}, Launchpad::Ram_quota{quota()}, config_registry.config(prg_name().string())); } diff --git a/repos/demo/src/lib/launchpad/launchpad.cc b/repos/demo/src/lib/launchpad/launchpad.cc index da126050a..491ee2a80 100644 --- a/repos/demo/src/lib/launchpad/launchpad.cc +++ b/repos/demo/src/lib/launchpad/launchpad.cc @@ -106,6 +106,8 @@ void Launchpad::process_config(Genode::Xml_node config_node) Number_of_bytes default_ram_quota = node.attribute_value("ram_quota", Number_of_bytes(0)); + Launchpad::Cap_quota const cap_quota { node.attribute_value("caps", 0UL) }; + /* * Obtain configuration for the child */ @@ -137,13 +139,13 @@ void Launchpad::process_config(Genode::Xml_node config_node) } /* add launchpad entry */ - add_launcher(*name, default_ram_quota, config_ds); + add_launcher(*name, cap_quota, default_ram_quota, config_ds); }); } Launchpad_child *Launchpad::start_child(Launchpad_child::Name const &binary_name, - Ram_quota ram_quota, + Cap_quota cap_quota, Ram_quota ram_quota, Dataspace_capability config_ds) { log("starting ", binary_name, " with quota ", ram_quota); @@ -165,6 +167,17 @@ Launchpad_child *Launchpad::start_child(Launchpad_child::Name const &binary_name ram_quota = Ram_quota { avail - preserved }; } + size_t const avail_caps = _env.pd().avail_caps().value; + + if (cap_quota.value > avail_caps) { + warning("child's cap quota (", cap_quota.value, ") exceeds the " + "number of available capabilities (", avail_caps, ")"); + + size_t const preserved_caps = min(avail_caps, 25UL); + + cap_quota = Cap_quota { avail_caps - preserved_caps }; + } + size_t metadata_size = 4096*16 + sizeof(Launchpad_child); if (metadata_size > ram_quota.value) { @@ -176,7 +189,8 @@ Launchpad_child *Launchpad::start_child(Launchpad_child::Name const &binary_name try { Launchpad_child *c = new (&_sliced_heap) - Launchpad_child(_env, _heap, unique_name, binary_name, ram_quota, + Launchpad_child(_env, _heap, unique_name, binary_name, + cap_quota, ram_quota, _parent_services, _child_services, config_ds); Lock::Guard lock_guard(_children_lock); diff --git a/repos/gems/include/gems/report_rom_slave.h b/repos/gems/include/gems/report_rom_slave.h index fb308e4b5..2b1331b32 100644 --- a/repos/gems/include/gems/report_rom_slave.h +++ b/repos/gems/include/gems/report_rom_slave.h @@ -45,16 +45,20 @@ class Report_rom_slave : public Genode::Noncopyable static Name _name() { return "report_rom"; } static Genode::Ram_quota _quota() { return { 1024*1024 }; } + static Genode::Cap_quota _caps() { return { 25 }; } public: Policy(Genode::Rpc_entrypoint &ep, Genode::Region_map &rm, + Genode::Pd_session &ref_pd, + Genode::Pd_session_capability ref_pd_cap, Genode::Ram_session &ref_ram, Genode::Ram_session_capability ref_ram_cap, const char *config) : Genode::Slave::Policy(_name(), _name(), *this, ep, rm, + ref_pd, ref_pd_cap, _caps(), ref_ram, ref_ram_cap, _quota()) { if (config) @@ -76,14 +80,15 @@ class Report_rom_slave : public Genode::Noncopyable * \param ram RAM session used to allocate the configuration * dataspace */ - Report_rom_slave(Genode::Pd_session &pd, - Genode::Region_map &rm, + Report_rom_slave(Genode::Region_map &rm, + Genode::Pd_session &pd, + Genode::Pd_session_capability pd_cap, Genode::Ram_session &ram, Genode::Ram_session_capability ram_cap, char const *config) : _ep(&pd, _ep_stack_size, "report_rom"), - _policy(_ep, rm, ram, ram_cap, config), + _policy(_ep, rm, pd, pd_cap, ram, ram_cap, config), _child(rm, _ep, _policy) { } diff --git a/repos/gems/src/app/launcher/fading_dialog.h b/repos/gems/src/app/launcher/fading_dialog.h index da0c424c7..cf4d6ec3c 100644 --- a/repos/gems/src/app/launcher/fading_dialog.h +++ b/repos/gems/src/app/launcher/fading_dialog.h @@ -196,11 +196,10 @@ class Launcher::Fading_dialog : private Input_event_handler _fader_slave_ep(&env.pd(), _fader_slave_ep_stack_size, "nit_fader"), _nitpicker_connection(env, "menu"), _nitpicker_session(env, _nitpicker_connection, env.ep(), _fader_slave_ep, *this), - _nit_fader_slave(_fader_slave_ep, env.rm(), - env.ram(), env.ram_session_cap(), - _nitpicker_service), + _nit_fader_slave(_fader_slave_ep, env.rm(), env.pd(), env.pd_session_cap(), + env.ram(), env.ram_session_cap(), _nitpicker_service), _nit_fader_connection(env.rm(), _nit_fader_slave.policy(), Slave::Args("label=menu")), - _menu_view_slave(env.pd(), env.rm(), + _menu_view_slave(env.rm(), env.pd(), env.pd_session_cap(), env.ram(), env.ram_session_cap(), _nit_fader_connection, _dialog_rom, _hover_report, initial_position) diff --git a/repos/gems/src/app/launcher/main.cc b/repos/gems/src/app/launcher/main.cc index dd69c638a..a83978013 100644 --- a/repos/gems/src/app/launcher/main.cc +++ b/repos/gems/src/app/launcher/main.cc @@ -43,7 +43,7 @@ struct Launcher::Main ""; Report_rom_slave _report_rom_slave { - _env.pd(), _env.rm(), _env.ram(), _env.ram_session_cap(), + _env.rm(), _env.pd(), _env.pd_session_cap(), _env.ram(), _env.ram_session_cap(), _report_rom_config }; /** diff --git a/repos/gems/src/app/launcher/menu_view_slave.h b/repos/gems/src/app/launcher/menu_view_slave.h index 09c249194..df826e1ef 100644 --- a/repos/gems/src/app/launcher/menu_view_slave.h +++ b/repos/gems/src/app/launcher/menu_view_slave.h @@ -75,11 +75,14 @@ class Launcher::Menu_view_slave static Name _name() { return "menu_view"; } static Genode::Ram_quota _quota() { return { 6*1024*1024 }; } + static Genode::Cap_quota _caps() { return { 25 }; } public: Policy(Genode::Rpc_entrypoint &ep, Genode::Region_map &rm, + Genode::Pd_session &ref_pd, + Genode::Pd_session_capability ref_pd_cap, Genode::Ram_session &ref_ram, Genode::Ram_session_capability ref_ram_cap, Capability nitpicker_session, @@ -88,6 +91,7 @@ class Launcher::Menu_view_slave Position position) : Genode::Slave::Policy(_name(), _name(), *this, ep, rm, + ref_pd, ref_pd_cap, _caps(), ref_ram, ref_ram_cap, _quota()), _nitpicker(rm, nitpicker_session), _dialog_rom(dialog_rom_session), @@ -127,8 +131,9 @@ class Launcher::Menu_view_slave /** * Constructor */ - Menu_view_slave(Genode::Pd_session &pd, - Genode::Region_map &rm, + Menu_view_slave(Genode::Region_map &rm, + Genode::Pd_session &ref_pd, + Genode::Pd_session_capability ref_pd_cap, Genode::Ram_session &ref_ram, Genode::Ram_session_capability ref_ram_cap, Capability nitpicker_session, @@ -136,8 +141,8 @@ class Launcher::Menu_view_slave Capability hover_report_session, Position initial_position) : - _ep(&pd, _ep_stack_size, "nit_fader"), - _policy(_ep, rm, ref_ram, ref_ram_cap, + _ep(&ref_pd, _ep_stack_size, "nit_fader"), + _policy(_ep, rm, ref_pd, ref_pd_cap, ref_ram, ref_ram_cap, nitpicker_session, dialog_rom_session, hover_report_session, initial_position), _child(rm, _ep, _policy) diff --git a/repos/gems/src/app/launcher/nit_fader_slave.h b/repos/gems/src/app/launcher/nit_fader_slave.h index 1d0b51530..d467617be 100644 --- a/repos/gems/src/app/launcher/nit_fader_slave.h +++ b/repos/gems/src/app/launcher/nit_fader_slave.h @@ -47,16 +47,20 @@ class Launcher::Nit_fader_slave static Name _name() { return "nit_fader"; } static Genode::Ram_quota _quota() { return { 2*1024*1024 }; } + static Genode::Cap_quota _caps() { return { 25 }; } public: Policy(Rpc_entrypoint &ep, Region_map &rm, + Pd_session &ref_pd, + Pd_session_capability ref_pd_cap, Ram_session &ref_ram, Ram_session_capability ref_ram_cap, Genode::Service &nitpicker_service) : Genode::Slave::Policy(_name(), _name(), *this, ep, rm, + ref_pd, ref_pd_cap, _caps(), ref_ram, ref_ram_cap, _quota()), _nitpicker_service(nitpicker_service) { @@ -95,11 +99,13 @@ class Launcher::Nit_fader_slave */ Nit_fader_slave(Rpc_entrypoint &ep, Genode::Region_map &rm, + Pd_session &ref_pd, + Pd_session_capability ref_pd_cap, Ram_session &ref_ram, Ram_session_capability ref_ram_cap, Genode::Service &nitpicker_service) : - _policy(ep, rm, ref_ram, ref_ram_cap, nitpicker_service), + _policy(ep, rm, ref_pd, ref_pd_cap, ref_ram, ref_ram_cap, nitpicker_service), _child(rm, ep, _policy) { visible(false); diff --git a/repos/gems/src/app/launcher/subsystem_manager.h b/repos/gems/src/app/launcher/subsystem_manager.h index 818d78d9e..c82f91220 100644 --- a/repos/gems/src/app/launcher/subsystem_manager.h +++ b/repos/gems/src/app/launcher/subsystem_manager.h @@ -147,6 +147,18 @@ class Launcher::Subsystem_manager return Ram_config { quantum, limit }; } + static Cap_quota _caps_config(Xml_node subsystem) + { + Cap_quota const caps { subsystem.attribute_value("caps", 0UL) }; + + if (caps.value) + return caps; + + Genode::error("missing 'caps' attribute for ", + subsystem.attribute_value("name", Label())); + throw Invalid_config(); + } + public: Subsystem_manager(Genode::Env & env, @@ -166,8 +178,7 @@ class Launcher::Subsystem_manager { Child::Binary_name const binary_name = _binary_name(subsystem); - Label const label = string_attribute(subsystem, "name", - Label("")); + Label const label = string_attribute(subsystem, "name", Label("")); Ram_config const ram_config = _ram_config(subsystem); @@ -177,9 +188,11 @@ class Launcher::Subsystem_manager Child *child = new (_heap) Child(_ram, _heap, label, binary_name.string(), _env.pd(), + _env.pd_session_cap(), _env.ram(), _env.ram_session_cap(), _env.rm(), + _caps_config(subsystem), ram_config.quantum, ram_config.limit, _yield_broadcast_handler, _exited_child_sig_cap); diff --git a/repos/hello_tutorial/doc/hello_tutorial.txt b/repos/hello_tutorial/doc/hello_tutorial.txt index 8a76bc4fb..926421a30 100644 --- a/repos/hello_tutorial/doc/hello_tutorial.txt +++ b/repos/hello_tutorial/doc/hello_tutorial.txt @@ -76,6 +76,8 @@ C++ class in 'include/hello_session/hello_session.h' !{ ! static const char *service_name() { return "Hello"; } ! +! enum { CAP_QUOTA = 2 }; +! ! virtual void say_hello() = 0; ! virtual int add(int a, int b) = 0; ! @@ -91,7 +93,12 @@ across component boundaries. Furthermore, we use the interface to specify the name of the service by providing the 'service_name' method. This method will later be used by both the server for announcing the service at its parent and the client for -requesting the creation of a "Hello" session. +requesting the creation of a "Hello" session. The 'CAP_QUOTA' definition +specifies the amount of capabilities required to establish the session. +The specified amount is transferred from the client to the server at session +creation time. For the "Hello" session, two capabilities are required, namely +a dataspace capability for the server-side memory occupied by the session +object and the actual session capability that refers to the RPC interface. The 'GENODE_RPC' macro is used to declare an RPC function. Its first argument is a type name that is used to refer to the RPC function. The type name can @@ -251,6 +258,7 @@ entry to init's 'config' file, which is located at 'build/bin/config'. ! ! ! +! ! ! ! @@ -345,7 +353,7 @@ of the session interface. For our case, the file ! : ! /* create session */ ! Genode::Connection(env, session(env.parent(), -! "ram_quota=4K")), +! "ram_quota=4K, cap_quota=4")), ! /* initialize RPC interface */ ! Session_client(cap()) { } !}; @@ -413,6 +421,7 @@ at the _run/hello.run_ and look as follows: ! ! ! +! ! ! ! diff --git a/repos/libports/run/ldso.run b/repos/libports/run/ldso.run index 84867bbac..c1a526f0c 100644 --- a/repos/libports/run/ldso.run +++ b/repos/libports/run/ldso.run @@ -43,6 +43,7 @@ run_genode_until {child ".*" exited with exit value 123.*\n} 10 grep_output {^\[init } unify_output {\[init \-\> test\-ldso\] upgrading quota donation for .* \([0-9]+ bytes\)} "" unify_output {ram_quota=[0-9]+} "ram_quota=UNIFIED" +unify_output {cap_quota=[0-9]+} "cap_quota=UNIFIED" trim_lines compare_output_to { @@ -83,7 +84,7 @@ compare_output_to { [init -> test-ldso] Catch exceptions in program [init -> test-ldso] --------------------------- [init -> test-ldso] exception in remote procedure call: -[init -> test-ldso] Error: ROM-session creation failed (ram_quota=UNIFIED, label="unknown_file") +[init -> test-ldso] Error: ROM-session creation failed (ram_quota=UNIFIED, cap_quota=UNIFIED, label="unknown_file") [init -> test-ldso] Error: Could not open ROM session for "unknown_file" [init -> test-ldso] caught [init -> test-ldso] exception in program: caught diff --git a/repos/os/include/cli_monitor/child.h b/repos/os/include/cli_monitor/child.h index 030776843..0b4c458ce 100644 --- a/repos/os/include/cli_monitor/child.h +++ b/repos/os/include/cli_monitor/child.h @@ -43,6 +43,7 @@ class Cli_monitor::Child_base : public Genode::Child_policy class Quota_exceeded : public Genode::Exception { }; typedef Genode::size_t size_t; + typedef Genode::Cap_quota Cap_quota; typedef Genode::Registered Parent_service; @@ -55,9 +56,14 @@ class Cli_monitor::Child_base : public Genode::Child_policy Genode::Session_label const _label; Binary_name const _binary_name; + Genode::Pd_session_capability _ref_pd_cap; + Genode::Pd_session &_ref_pd; + Genode::Ram_session_capability _ref_ram_cap; Genode::Ram_session &_ref_ram; + Cap_quota _cap_quota; + size_t _ram_quota; size_t _ram_limit; @@ -110,10 +116,12 @@ class Cli_monitor::Child_base : public Genode::Child_policy Genode::Allocator &alloc, Name const &label, Binary_name const &binary_name, - Genode::Pd_session &pd_session, + Genode::Pd_session &ref_pd, + Genode::Pd_session_capability ref_pd_cap, Genode::Ram_session &ref_ram, Genode::Ram_session_capability ref_ram_cap, Genode::Region_map &local_rm, + Cap_quota cap_quota, Genode::size_t ram_quota, Genode::size_t ram_limit, Genode::Signal_context_capability yield_response_sig_cap, @@ -121,9 +129,10 @@ class Cli_monitor::Child_base : public Genode::Child_policy : _ram(ram), _alloc(alloc), _label(label), _binary_name(binary_name), + _ref_pd_cap (ref_pd_cap), _ref_pd (ref_pd), _ref_ram_cap(ref_ram_cap), _ref_ram(ref_ram), - _ram_quota(ram_quota), _ram_limit(ram_limit), - _entrypoint(&pd_session, ENTRYPOINT_STACK_SIZE, _label.string(), false), + _cap_quota(cap_quota), _ram_quota(ram_quota), _ram_limit(ram_limit), + _entrypoint(&ref_pd, ENTRYPOINT_STACK_SIZE, _label.string(), false), _config_policy(local_rm, "config", _entrypoint, &ref_ram), _yield_response_sigh_cap(yield_response_sig_cap), _exit_sig_cap(exit_sig_cap), @@ -275,9 +284,18 @@ class Cli_monitor::Child_base : public Genode::Child_policy Name name() const override { return _label; } Binary_name binary_name() const override { return _binary_name; } + Genode::Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; } + Genode::Pd_session &ref_pd() override { return _ref_pd; } + Genode::Ram_session_capability ref_ram_cap() const override { return _ref_ram_cap; } Genode::Ram_session &ref_ram() override { return _ref_ram; } + void init(Genode::Pd_session &session, Genode::Pd_session_capability cap) override + { + session.ref_account(_ref_pd_cap); + _ref_pd.transfer_quota(cap, _cap_quota); + } + void init(Genode::Ram_session &session, Genode::Ram_session_capability cap) override { session.ref_account(_ref_ram_cap); diff --git a/repos/os/include/loader_session/client.h b/repos/os/include/loader_session/client.h index 11f0497c9..0b79691bd 100644 --- a/repos/os/include/loader_session/client.h +++ b/repos/os/include/loader_session/client.h @@ -34,6 +34,9 @@ struct Loader::Session_client : Genode::Rpc_client void commit_rom_module(Name const &name) override { call(name); } + void cap_quota(Cap_quota limit) override { + call(limit); } + void ram_quota(Ram_quota quantum) override { call(quantum); } diff --git a/repos/os/include/loader_session/connection.h b/repos/os/include/loader_session/connection.h index 85ae33b8b..f2da35ca2 100644 --- a/repos/os/include/loader_session/connection.h +++ b/repos/os/include/loader_session/connection.h @@ -26,11 +26,12 @@ struct Loader::Connection : Genode::Connection, Session_client /** * Constructor */ - Connection(Genode::Env &env, Ram_quota ram_quota) + Connection(Genode::Env &env, Ram_quota ram_quota, Cap_quota cap_quota) : Genode::Connection(env, session(env.parent(), - "ram_quota=%ld", - ram_quota.value)), + "ram_quota=%ld, cap_quota=%ld", + ram_quota.value, + CAP_QUOTA + cap_quota.value)), Session_client(cap()) { } diff --git a/repos/os/include/loader_session/loader_session.h b/repos/os/include/loader_session/loader_session.h index 5ce86b728..5881d0eac 100644 --- a/repos/os/include/loader_session/loader_session.h +++ b/repos/os/include/loader_session/loader_session.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ namespace Loader { using Genode::Dataspace_capability; using Genode::Signal_context_capability; using Genode::Ram_quota; + using Genode::Cap_quota; struct Session; } @@ -90,6 +92,11 @@ struct Loader::Session : Genode::Session */ virtual void commit_rom_module(Name const &name) = 0; + /** + * Define capability quota assigned to the subsystem + */ + virtual void cap_quota(Cap_quota) = 0; + /** * Define RAM quota assigned to the subsystem * @@ -167,6 +174,7 @@ struct Loader::Session : Genode::Session GENODE_RPC_THROW(Rpc_commit_rom_module, void, commit_rom_module, GENODE_TYPE_LIST(Rom_module_does_not_exist), Name const &); + GENODE_RPC(Rpc_cap_quota, void, cap_quota, Cap_quota); GENODE_RPC(Rpc_ram_quota, void, ram_quota, Ram_quota); GENODE_RPC(Rpc_constrain_geometry, void, constrain_geometry, Area); GENODE_RPC(Rpc_parent_view, void, parent_view, Nitpicker::View_capability); @@ -182,7 +190,7 @@ struct Loader::Session : Genode::Session GENODE_TYPE_LIST(View_does_not_exist)); GENODE_RPC_INTERFACE(Rpc_alloc_rom_module, Rpc_commit_rom_module, - Rpc_ram_quota, Rpc_constrain_geometry, + Rpc_cap_quota, Rpc_ram_quota, Rpc_constrain_geometry, Rpc_parent_view, Rpc_view_ready_sigh, Rpc_fault_sigh, Rpc_start, Rpc_view_geometry, Rpc_view_size); }; diff --git a/repos/os/include/os/attached_mmio.h b/repos/os/include/os/attached_mmio.h index 438d9dd81..ccf6fbbe9 100644 --- a/repos/os/include/os/attached_mmio.h +++ b/repos/os/include/os/attached_mmio.h @@ -44,7 +44,9 @@ class Genode::Attached_mmio : public Attached_io_mem_dataspace, * * \throw Parent::Service_denied * \throw Insufficient_ram_quota - * \throw Parent::Unavailable + * \throw Insufficient_cap_quota + * \throw Out_of_ram + * \throw Out_of_caps * \throw Rm_session::Attach_failed */ Attached_mmio(Env &env, addr_t base, size_t size, diff --git a/repos/os/include/os/child_policy_dynamic_rom.h b/repos/os/include/os/child_policy_dynamic_rom.h index c69cc7439..a53ae5138 100644 --- a/repos/os/include/os/child_policy_dynamic_rom.h +++ b/repos/os/include/os/child_policy_dynamic_rom.h @@ -71,6 +71,9 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object, * \param ram RAM session used to allocate the backing store * for buffering ROM module data * + * \throw Out_of_ram + * \throw Out_of_caps + * * If 'ram' is 0, the child policy is ineffective. */ Child_policy_dynamic_rom_file(Region_map &rm, @@ -195,6 +198,7 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object, case Session_state::INVALID_ARGS: case Session_state::INSUFFICIENT_RAM_QUOTA: + case Session_state::INSUFFICIENT_CAP_QUOTA: case Session_state::AVAILABLE: case Session_state::CAP_HANDED_OUT: case Session_state::CLOSED: diff --git a/repos/os/include/os/dynamic_rom_session.h b/repos/os/include/os/dynamic_rom_session.h index 510e84060..f77ad1d77 100644 --- a/repos/os/include/os/dynamic_rom_session.h +++ b/repos/os/include/os/dynamic_rom_session.h @@ -98,6 +98,21 @@ class Genode::Dynamic_rom_session : public Rpc_object */ return true; } + catch (Out_of_caps) { + + error("ouf of child cap quota while delivering dynamic ROM"); + + /* + * XXX We may try to generate a resource request on + * behalf of the child. + */ + + /* + * Don't let the child try again to obtain a dataspace + * by pretending that the ROM module is up-to-date. + */ + return true; + } try { _content_producer.produce_content(_ds->local_addr(), @@ -166,6 +181,9 @@ class Genode::Dynamic_rom_session : public Rpc_object if (!_ds.constructed()) _unsynchronized_update(); + if (!_ds.constructed()) + return Rom_dataspace_capability(); + Dataspace_capability ds_cap = _ds->cap(); return static_cap_cast(ds_cap); diff --git a/repos/os/include/os/slave.h b/repos/os/include/os/slave.h index afc3bd09c..90d49e442 100644 --- a/repos/os/include/os/slave.h +++ b/repos/os/include/os/slave.h @@ -52,16 +52,19 @@ class Genode::Slave::Policy : public Child_policy private: - Label const _label; - Binary_name const _binary_name; + Label const _label; + Binary_name const _binary_name; + Pd_session &_ref_pd; + Pd_session_capability _ref_pd_cap; Ram_session &_ref_ram; Ram_session_capability _ref_ram_cap; - Genode::Parent_service _binary_service; - Ram_quota const _ram_quota; - Parent_services &_parent_services; - Rpc_entrypoint &_ep; - Child_policy_dynamic_rom_file _config_policy; - Session_requester _session_requester; + Genode::Parent_service _binary_service; + Cap_quota const _cap_quota; + Ram_quota const _ram_quota; + Parent_services &_parent_services; + Rpc_entrypoint &_ep; + Child_policy_dynamic_rom_file _config_policy; + Session_requester _session_requester; public: @@ -83,14 +86,18 @@ class Genode::Slave::Policy : public Child_policy Parent_services &parent_services, Rpc_entrypoint &ep, Region_map &rm, + Pd_session &ref_pd, + Pd_session_capability ref_pd_cap, + Cap_quota cap_quota, Ram_session &ref_ram, Ram_session_capability ref_ram_cap, Ram_quota ram_quota) : _label(label), _binary_name(binary_name), + _ref_pd(ref_pd), _ref_pd_cap(ref_pd_cap), _ref_ram(ref_ram), _ref_ram_cap(ref_ram_cap), _binary_service(Rom_session::service_name()), - _ram_quota(ram_quota), + _cap_quota(cap_quota), _ram_quota(ram_quota), _parent_services(parent_services), _ep(ep), _config_policy(rm, "config", _ep, &_ref_ram), _session_requester(ep, _ref_ram, rm) @@ -127,9 +134,18 @@ class Genode::Slave::Policy : public Child_policy Binary_name binary_name() const override { return _binary_name; } + Pd_session &ref_pd() override { return _ref_pd; } + Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; } + Ram_session &ref_ram() override { return _ref_ram; } Ram_session_capability ref_ram_cap() const override { return _ref_ram_cap; } + void init(Pd_session &session, Pd_session_capability cap) override + { + session.ref_account(_ref_pd_cap); + _ref_pd.transfer_quota(cap, _cap_quota); + } + void init(Ram_session &session, Ram_session_capability cap) override { session.ref_account(_ref_ram_cap); @@ -217,6 +233,7 @@ class Genode::Slave::Connection_base case Session_state::INVALID_ARGS: case Session_state::INSUFFICIENT_RAM_QUOTA: + case Session_state::INSUFFICIENT_CAP_QUOTA: case Session_state::AVAILABLE: case Session_state::CAP_HANDED_OUT: case Session_state::CLOSED: @@ -256,6 +273,22 @@ class Genode::Slave::Connection_base return _policy.ref_ram_cap(); } + /** + * Service ('Cap_transfer::Account') interface + */ + void transfer(Pd_session_capability to, Cap_quota amount) override + { + if (to.valid()) _policy.ref_pd().transfer_quota(to, amount); + } + + /** + * Service ('Cap_transfer::Account') interface + */ + Pd_session_capability cap(Cap_quota) const override + { + return _policy.ref_pd_cap(); + } + } _service; Local_connection _connection; diff --git a/repos/os/run/report_rom.run b/repos/os/run/report_rom.run index 6e3b0e5f6..5c057b13d 100644 --- a/repos/os/run/report_rom.run +++ b/repos/os/run/report_rom.run @@ -71,7 +71,7 @@ compare_output_to { [init -> test-report_rom] Reporter: start reporting (while the ROM client still listens) [init -> test-report_rom] ROM client: wait for update notification [init -> test-report_rom] ROM client: try to open the same report again - [init -> test-report_rom] Error: Report-session creation failed (label="brightness", ram_quota=14336, buffer_size=4096) + [init -> test-report_rom] Error: Report-session creation failed (label="brightness", ram_quota=14336, cap_quota=3, buffer_size=4096) [init -> test-report_rom] ROM client: catched Parent::Service_denied - OK [init -> test-report_rom] --- test-report_rom finished --- } diff --git a/repos/os/src/app/cli_monitor/child.h b/repos/os/src/app/cli_monitor/child.h index 2e768c617..f881d8693 100644 --- a/repos/os/src/app/cli_monitor/child.h +++ b/repos/os/src/app/cli_monitor/child.h @@ -31,10 +31,12 @@ struct Cli_monitor::Child : Child_base, List::Element Genode::Allocator &alloc, Name const &label, Binary_name const &binary, - Genode::Pd_session &pd_session, + Genode::Pd_session &ref_pd, + Genode::Pd_session_capability ref_pd_cap, Genode::Ram_session &ref_ram, Genode::Ram_session_capability ref_ram_cap, Genode::Region_map &local_rm, + Cap_quota cap_quota, Genode::size_t ram_quota, Genode::size_t ram_limit, Genode::Signal_context_capability yield_response_sig_cap, @@ -44,10 +46,12 @@ struct Cli_monitor::Child : Child_base, List::Element alloc, label, binary, - pd_session, + ref_pd, + ref_pd_cap, ref_ram, ref_ram_cap, local_rm, + cap_quota, ram_quota, ram_limit, yield_response_sig_cap, diff --git a/repos/os/src/app/cli_monitor/main.cc b/repos/os/src/app/cli_monitor/main.cc index 184ba3acb..0346928f1 100644 --- a/repos/os/src/app/cli_monitor/main.cc +++ b/repos/os/src/app/cli_monitor/main.cc @@ -172,7 +172,8 @@ struct Cli_monitor::Main /* initialize generic commands */ Registered _help_command { _commands }; Registered _kill_command { _commands, _children, _heap }; - Registered _start_command { _commands, _ram, _heap, _env.pd(), + Registered _start_command { _commands, _ram, _heap, + _env.pd(), _env.pd_session_cap(), _env.ram(), _env.ram_session_cap(), _env.rm(), _children, _subsystem_config_registry, diff --git a/repos/os/src/app/cli_monitor/start_command.h b/repos/os/src/app/cli_monitor/start_command.h index 97a9501c6..aaf750e37 100644 --- a/repos/os/src/app/cli_monitor/start_command.h +++ b/repos/os/src/app/cli_monitor/start_command.h @@ -34,7 +34,8 @@ class Cli_monitor::Start_command : public Command Ram &_ram; Genode::Allocator &_alloc; Child_registry &_children; - Genode::Pd_session &_pd; + Genode::Pd_session &_ref_pd; + Genode::Pd_session_capability _ref_pd_cap; Genode::Ram_session &_ref_ram; Genode::Ram_session_capability _ref_ram_cap; Genode::Region_map &_local_rm; @@ -50,6 +51,7 @@ class Cli_monitor::Start_command : public Command size_t count = 1; Genode::Number_of_bytes ram = 0; Genode::Number_of_bytes ram_limit = 0; + size_t caps = subsystem_node.attribute_value("caps", 0UL); /* read default RAM quota from config */ try { @@ -113,8 +115,10 @@ class Cli_monitor::Start_command : public Command Child *child = 0; try { child = new (_alloc) - Child(_ram, _alloc, label, binary_name, _pd, _ref_ram, - _ref_ram_cap, _local_rm, ram, ram_limit, + Child(_ram, _alloc, label, binary_name, + _ref_pd, _ref_pd_cap, _ref_ram, + _ref_ram_cap, _local_rm, + Genode::Cap_quota{caps}, ram, ram_limit, _yield_response_sigh_cap, _exit_sig_cap); } catch (Genode::Parent::Service_denied) { @@ -160,7 +164,8 @@ class Cli_monitor::Start_command : public Command Start_command(Ram &ram, Genode::Allocator &alloc, - Genode::Pd_session &pd, + Genode::Pd_session &ref_pd, + Genode::Pd_session_capability ref_pd_cap, Genode::Ram_session &ref_ram, Genode::Ram_session_capability ref_ram_cap, Genode::Region_map &local_rm, @@ -170,8 +175,10 @@ class Cli_monitor::Start_command : public Command Signal_context_capability exit_sig_cap) : Command("start", "create new subsystem"), - _ram(ram), _alloc(alloc), _children(children), _pd(pd), - _ref_ram(ref_ram), _ref_ram_cap(ref_ram_cap), _local_rm(local_rm), + _ram(ram), _alloc(alloc), _children(children), + _ref_pd(ref_pd), _ref_pd_cap(ref_pd_cap), + _ref_ram(ref_ram), _ref_ram_cap(ref_ram_cap), + _local_rm(local_rm), _subsystem_configs(subsustem_configs), _yield_response_sigh_cap(yield_response_sigh_cap), _exit_sig_cap(exit_sig_cap) diff --git a/repos/os/src/drivers/platform/spec/x86/device_pd.h b/repos/os/src/drivers/platform/spec/x86/device_pd.h index 406a93433..a28af2672 100644 --- a/repos/os/src/drivers/platform/spec/x86/device_pd.h +++ b/repos/os/src/drivers/platform/spec/x86/device_pd.h @@ -33,12 +33,16 @@ class Platform::Device_pd_policy Device_pd_policy(Genode::Rpc_entrypoint &slave_ep, Genode::Region_map &local_rm, + Genode::Pd_session &pd_ref, + Genode::Pd_session_capability pd_ref_cap, + Genode::Cap_quota cap_quota, Genode::Ram_session &ram_ref, Genode::Ram_session_capability ram_ref_cap, Genode::Ram_quota ram_quota, Genode::Session_label const &label) : Genode::Slave::Policy(label, "device_pd", *this, slave_ep, local_rm, + pd_ref, pd_ref_cap, cap_quota, ram_ref, ram_ref_cap, ram_quota) { } }; diff --git a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h index 1b508d1f8..c5a725bd3 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h @@ -202,6 +202,8 @@ class Platform::Session_component : public Genode::Rpc_object Genode::Attached_rom_dataspace &_config; Genode::Ram_session_guard _env_ram; Genode::Ram_session_capability _env_ram_cap; + Genode::Pd_session &_env_pd; + Genode::Pd_session_capability _env_pd_cap; Genode::Region_map &_local_rm; Genode::Heap _md_alloc; Genode::Session_label const _label; @@ -254,6 +256,13 @@ class Platform::Session_component : public Genode::Rpc_object enum { OVERHEAD = 4096 }; try { _env_ram.transfer_quota(_ram, Genode::Ram_quota{OVERHEAD}); } catch (...) { throw Genode::Insufficient_ram_quota(); } + + /* + * XXX instead of eagerly upgrading the RAM session, upgrade it + * on demand, paid by the client's cap session quota. + */ + using namespace Genode; + _ram.upgrade(Session::Resources { Ram_quota{0}, Cap_quota{10} }); } bool const _ram_initialized = (_init_ram(), true); @@ -291,7 +300,7 @@ class Platform::Session_component : public Genode::Rpc_object private: - enum { RAM_QUOTA = 190 * 4096 }; + enum { CAP_QUOTA = 60, RAM_QUOTA = 190 * 4096 }; Quota_reservation const _reservation; Device_pd_policy _policy; @@ -319,11 +328,15 @@ class Platform::Session_component : public Genode::Rpc_object Genode::Rpc_entrypoint &ep, Genode::Ram_session_guard &ref_ram, Genode::Ram_session_capability ref_ram_cap, + Genode::Pd_session &ref_pd, + Genode::Pd_session_capability ref_pd_cap, Genode::Session_label const &label) try : _reservation(ref_ram, RAM_QUOTA), _policy(ep, local_rm, - ref_ram, ref_ram_cap, Genode::Ram_quota{RAM_QUOTA}, label), + ref_pd, ref_pd_cap, Genode::Cap_quota{CAP_QUOTA}, + ref_ram, ref_ram_cap, Genode::Ram_quota{RAM_QUOTA}, + label), _child(local_rm, ep, _policy), _connection(_policy, Genode::Slave::Args()) { } @@ -331,8 +344,18 @@ class Platform::Session_component : public Genode::Rpc_object catch (Out_of_metadata) { throw; } /* thrown by 'Device_pd_policy' or 'Child' */ catch (Genode::Out_of_ram) { throw Out_of_metadata(); } - /* throw by 'Slave::Connection' */ + catch (Genode::Out_of_caps) { + /* XXX reflect 'Out_of_caps' exception to client */ + Genode::error("Out_of_caps during device-pd creation"); + throw Out_of_metadata(); + } + /* thrown by 'Slave::Connection' */ catch (Genode::Insufficient_ram_quota) { throw Out_of_metadata(); } + /* thrown by 'Slave::Connection' */ + catch (Genode::Insufficient_cap_quota) { + Genode::error("Insufficient_cap_quota during device-pd creation"); + throw Out_of_metadata(); + } Device_pd_client &session() { return _connection; } @@ -368,13 +391,19 @@ class Platform::Session_component : public Genode::Rpc_object try { _device_pd = new (_md_alloc) - Device_pd(_local_rm, _device_pd_ep, _env_ram, - _env_ram_cap, _label); + Device_pd(_local_rm, _device_pd_ep, _env_ram, _env_ram_cap, + _env_pd, _env_pd_cap, _label); } /* thrown by '_md_alloc' */ catch (Genode::Out_of_ram) { throw Out_of_metadata(); } + catch (Genode::Out_of_caps) { + /* XXX reflect exception to client */ + Genode::error("Out_of_caps during Device_pd construction"); + throw Out_of_metadata(); + } + /* thrown by 'Device_pd' */ catch (Out_of_metadata) { throw; } @@ -609,6 +638,8 @@ class Platform::Session_component : public Genode::Rpc_object _env_ram(env.ram(), env.ram_session_cap(), Genode::Arg_string::find_arg(args, "ram_quota").long_value(0)), _env_ram_cap(env.ram_session_cap()), + _env_pd(env.pd()), + _env_pd_cap(env.pd_session_cap()), _local_rm(env.rm()), _md_alloc(_env_ram, env.rm()), _label(Genode::label_from_args(args)), @@ -846,6 +877,10 @@ class Platform::Session_component : public Genode::Rpc_object return _env.ep().rpc_ep().manage(dev); } catch (Genode::Allocator::Out_of_memory) { throw Out_of_metadata(); + } catch (Genode::Out_of_caps) { + /* XXX reflect exception to client */ + Genode::error("Out_of_caps during Device_component construction"); + throw Out_of_metadata(); } }; return _env.ep().rpc_ep().apply(prev_device, lambda); @@ -945,24 +980,35 @@ class Platform::Session_component : public Genode::Rpc_object /* 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(); } + catch (Genode::Out_of_caps) { + Genode::error("Out_of_caps during alloc_dma_buffer (transfer_quota)"); + throw Fatal(); + } + catch (...) { } enum { UPGRADE_QUOTA = 4096 }; /* allocate dataspace from session specific ram session */ Ram_capability ram_cap = Genode::retry( [&] () { - try { - return _ram.alloc(size, Genode::UNCACHED); - } - catch (Genode::Out_of_ram) { + Ram_capability ram = Genode::retry( + [&] () { + try { return _ram.alloc(size, Genode::UNCACHED); } + catch (Genode::Out_of_caps) { + Genode::error("Out_of_caps during alloc_dma_buffer (alloc)"); + throw Fatal(); + } + }, + [&] () { + if (!_env_ram.withdraw(UPGRADE_QUOTA)) { + _rollback(size); + } - if (!_env_ram.withdraw(UPGRADE_QUOTA)) - _rollback(size); - - throw; - } + /* upgrade meta-data quota */ + _ram.upgrade_ram(UPGRADE_QUOTA); + }); + return ram; }, [&] () { /* @@ -973,7 +1019,7 @@ class Platform::Session_component : public Genode::Rpc_object * UPGRADE_QUOTA steps. */ try { _env_ram.transfer_quota(_ram, Genode::Ram_quota{UPGRADE_QUOTA}); } - catch (...) { throw Genode::Out_of_ram(); } + catch (Genode::Out_of_ram) { throw Out_of_metadata(); } }); if (!ram_cap.valid()) diff --git a/repos/os/src/init/child.cc b/repos/os/src/init/child.cc index b0abce20d..bb00983be 100644 --- a/repos/os/src/init/child.cc +++ b/repos/os/src/init/child.cc @@ -289,9 +289,20 @@ void Init::Child::report_state(Xml_generator &xml, Report_detail const &detail) generate_ram_info(xml, _child.ram()); - if (_requested_resources.constructed()) - xml.attribute("requested", String<32> { - Number_of_bytes(_requested_resources->ram.value) }); + if (_requested_resources.constructed() && _requested_resources->ram.value) + xml.attribute("requested", String<32>(_requested_resources->ram)); + }); + } + + if (detail.child_caps() && _child.pd_session_cap().valid()) { + xml.node("caps", [&] () { + + xml.attribute("assigned", String<32>(_resources.assigned_cap_quota)); + + generate_caps_info(xml, _child.pd()); + + if (_requested_resources.constructed() && _requested_resources->caps.value) + xml.attribute("requested", String<32>(_requested_resources->caps)); }); } @@ -320,6 +331,18 @@ void Init::Child::report_state(Xml_generator &xml, Report_detail const &detail) } +void Init::Child::init(Pd_session &session, Pd_session_capability cap) +{ + session.ref_account(_env.pd_session_cap()); + + Cap_quota const quota { _resources.effective_cap_quota().value }; + + try { _env.pd().transfer_quota(cap, quota); } + catch (Out_of_caps) { + error(name(), ": unable to initialize cap quota of PD"); } +} + + void Init::Child::init(Ram_session &session, Ram_session_capability cap) { session.ref_account(_env.ram_session_cap()); @@ -613,8 +636,10 @@ Init::Child::Child(Env &env, Report_update_trigger &report_update_trigger, Xml_node start_node, Default_route_accessor &default_route_accessor, + Default_caps_accessor &default_caps_accessor, Name_registry &name_registry, Ram_quota ram_limit, + Cap_quota cap_limit, Ram_limit_accessor &ram_limit_accessor, Prio_levels prio_levels, Affinity::Space const &affinity_space, @@ -628,8 +653,10 @@ Init::Child::Child(Env &env, _default_route_accessor(default_route_accessor), _ram_limit_accessor(ram_limit_accessor), _name_registry(name_registry), - _resources(_resources_from_start_node(start_node, prio_levels, affinity_space)), - _resources_checked((_check_ram_constraints(ram_limit), true)), + _resources(_resources_from_start_node(start_node, prio_levels, affinity_space, + default_caps_accessor.default_caps(), cap_limit)), + _resources_checked((_check_ram_constraints(ram_limit), + _check_cap_constraints(cap_limit), true)), _parent_services(parent_services), _child_services(child_services), _session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()) @@ -637,6 +664,7 @@ Init::Child::Child(Env &env, if (_verbose.enabled()) { log("child \"", _unique_name, "\""); log(" RAM quota: ", _resources.effective_ram_quota()); + log(" cap quota: ", _resources.effective_cap_quota()); log(" ELF binary: ", _binary_name); log(" priority: ", _resources.priority); } diff --git a/repos/os/src/init/child.h b/repos/os/src/init/child.h index 8ee582c9f..ae2096597 100644 --- a/repos/os/src/init/child.h +++ b/repos/os/src/init/child.h @@ -48,6 +48,8 @@ class Init::Child : Child_policy, Routed_service::Wakeup struct Default_route_accessor { virtual Xml_node default_route() = 0; }; + struct Default_caps_accessor { virtual Cap_quota default_caps() = 0; }; + struct Ram_limit_accessor { virtual Ram_quota ram_limit() = 0; }; private: @@ -124,6 +126,7 @@ class Init::Child : Child_policy, Routed_service::Wakeup long priority; Affinity affinity; Ram_quota assigned_ram_quota; + Cap_quota assigned_cap_quota; size_t cpu_quota_pc; bool constrain_phys; @@ -131,15 +134,39 @@ class Init::Child : Child_policy, Routed_service::Wakeup { return Genode::Child::effective_quota(assigned_ram_quota); } + + Cap_quota effective_cap_quota() const + { + /* capabilities consumed by 'Genode::Child' */ + Cap_quota const effective = + Genode::Child::effective_quota(assigned_cap_quota); + + /* capabilities additionally consumed by init */ + enum { + STATIC_COSTS = 1 /* possible heap backing-store + allocation for session object */ + + 1 /* buffered XML start node */ + + 2 /* dynamic ROM for config */ + + 2 /* dynamic ROM for session requester */ + }; + + if (effective.value < STATIC_COSTS) + return Cap_quota{0}; + + return Cap_quota{effective.value - STATIC_COSTS}; + } }; Resources _resources_from_start_node(Xml_node start_node, Prio_levels prio_levels, - Affinity::Space const &affinity_space) + Affinity::Space const &affinity_space, + Cap_quota default_cap_quota, Cap_quota cap_limit) { size_t cpu_quota_pc = 0; bool constrain_phys = false; Number_of_bytes ram_bytes = 0; + size_t caps = start_node.attribute_value("caps", default_cap_quota.value); + start_node.for_each_sub_node("resource", [&] (Xml_node rsc) { typedef String<8> Name; @@ -153,6 +180,10 @@ class Init::Child : Child_policy, Routed_service::Wakeup if (name == "CPU") { cpu_quota_pc = rsc.attribute_value("quantum", 0UL); } + + if (name == "CAP") { + caps = rsc.attribute_value("quantum", 0UL); + } }); return Resources { log2(prio_levels.value), @@ -160,6 +191,7 @@ class Init::Child : Child_policy, Routed_service::Wakeup Affinity(affinity_space, affinity_location_from_xml(affinity_space, start_node)), Ram_quota { ram_bytes }, + Cap_quota { caps }, cpu_quota_pc, constrain_phys }; } @@ -171,6 +203,9 @@ class Init::Child : Child_policy, Routed_service::Wakeup if (_resources.effective_ram_quota().value == 0) warning(name(), ": no valid RAM quota defined"); + if (_resources.effective_cap_quota().value == 0) + warning(name(), ": no valid cap quota defined"); + /* * If the configured RAM quota exceeds our own quota, we donate * all remaining quota to the child. @@ -183,6 +218,20 @@ class Init::Child : Child_policy, Routed_service::Wakeup } } + void _check_cap_constraints(Cap_quota cap_limit) + { + if (_resources.assigned_cap_quota.value == 0) + warning(name(), ": no valid cap quota defined"); + + if (_resources.assigned_cap_quota.value > cap_limit.value) { + + warning(name(), ": assigned caps (", _resources.assigned_cap_quota.value, ") " + "exceed available caps (", cap_limit.value, ")"); + + _resources.assigned_cap_quota.value = cap_limit.value; + } + } + bool const _resources_checked; Registry &_parent_services; @@ -255,10 +304,12 @@ class Init::Child : Child_policy, Routed_service::Wakeup struct Requested_resources { Ram_quota const ram; + Cap_quota const caps; Requested_resources(Parent::Resource_args const &args) : - ram (ram_quota_from_args(args.string())) + ram (ram_quota_from_args(args.string())), + caps(cap_quota_from_args(args.string())) { } }; @@ -266,16 +317,20 @@ class Init::Child : Child_policy, Routed_service::Wakeup Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), *this }; - struct Ram_accessor : Routed_service::Ram_accessor + struct Ram_pd_accessor : Routed_service::Ram_accessor, + Routed_service::Pd_accessor { Genode::Child &_child; - Ram_accessor(Genode::Child &child) : _child(child) { } + Ram_pd_accessor(Genode::Child &child) : _child(child) { } Ram_session &ram() override { return _child.ram(); } Ram_session_capability ram_cap() const override { return _child.ram_session_cap(); } - } _ram_accessor { _child }; + Pd_session &pd() override { return _child.pd(); } + Pd_session_capability pd_cap() const override { return _child.pd_session_cap(); } + + } _ram_pd_accessor { _child }; /** * Async_service::Wakeup callback @@ -343,7 +398,7 @@ class Init::Child : Child_policy, Routed_service::Wakeup new (_alloc) Routed_service(_child_services, this->name(), - _ram_accessor, + _ram_pd_accessor, _ram_pd_accessor, _session_requester.id_space(), _child.session_factory(), name, *this); @@ -375,8 +430,10 @@ class Init::Child : Child_policy, Routed_service::Wakeup Report_update_trigger &report_update_trigger, Xml_node start_node, Default_route_accessor &default_route_accessor, + Default_caps_accessor &default_caps_accessor, Name_registry &name_registry, Ram_quota ram_limit, + Cap_quota cap_limit, Ram_limit_accessor &ram_limit_accessor, Prio_levels prio_levels, Affinity::Space const &affinity_space, @@ -391,6 +448,7 @@ class Init::Child : Child_policy, Routed_service::Wakeup bool has_name(Child_policy::Name const &str) const { return str == name(); } Ram_quota ram_quota() const { return _resources.assigned_ram_quota; } + Cap_quota cap_quota() const { return _resources.assigned_cap_quota; } void initiate_env_ram_session() { @@ -451,9 +509,13 @@ class Init::Child : Child_policy, Routed_service::Wakeup Child_policy::Name name() const override { return _unique_name; } + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + Ram_session &ref_ram() override { return _env.ram(); } Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); } + void init(Pd_session &, Pd_session_capability) override; void init(Ram_session &, Ram_session_capability) override; void init(Cpu_session &, Cpu_session_capability) override; diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc index 5d8816e33..b193d10a7 100644 --- a/repos/os/src/init/main.cc +++ b/repos/os/src/init/main.cc @@ -26,7 +26,7 @@ namespace Init { struct Main; } struct Init::Main : State_reporter::Producer, Child::Default_route_accessor, - Child::Ram_limit_accessor + Child::Default_caps_accessor, Child::Ram_limit_accessor { Env &_env; @@ -42,6 +42,8 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor, Constructible _default_route; + Cap_quota _default_caps { 0 }; + unsigned _child_cnt = 0; static Ram_quota _preserved_ram_from_config(Xml_node config) @@ -70,6 +72,32 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor, return Ram_quota { avail_ram.value - preserved_ram.value }; } + static Cap_quota _preserved_caps_from_config(Xml_node config) + { + size_t preserve = 20; + + config.for_each_sub_node("resource", [&] (Xml_node node) { + if (node.attribute_value("name", String<16>()) == "CAP") + preserve = node.attribute_value("preserve", preserve); }); + + return Cap_quota { preserve }; + } + + Cap_quota _avail_caps() + { + Cap_quota const preserved_caps = _preserved_caps_from_config(_config.xml()); + + Cap_quota avail_caps { _env.pd().avail_caps().value }; + + if (preserved_caps.value > avail_caps.value) { + error("Capability preservation exceeds available capabilities"); + return Cap_quota { 0 }; + } + + /* deduce preserved quota from available quota */ + return Cap_quota { avail_caps.value - preserved_caps.value }; + } + /** * Child::Ram_limit_accessor interface */ @@ -80,7 +108,10 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor, void produce_state_report(Xml_generator &xml, Report_detail const &detail) const { if (detail.init_ram()) - xml.node("ram", [&] () { generate_ram_info(xml, _env.ram()); }); + xml.node("ram", [&] () { generate_ram_info (xml, _env.ram()); }); + + if (detail.init_caps()) + xml.node("caps", [&] () { generate_caps_info(xml, _env.pd()); }); if (detail.children()) _children.report_state(xml, detail); @@ -95,6 +126,11 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor, : Xml_node(""); } + /** + * Default_caps_accessor interface + */ + Cap_quota default_caps() override { return _default_caps; } + State_reporter _state_reporter { _env, *this }; Signal_handler
_resource_avail_handler { @@ -258,6 +294,12 @@ void Init::Main::_handle_config() _default_route.construct(_heap, _config.xml().sub_node("default-route")); } catch (...) { } + _default_caps = Cap_quota { 0 }; + try { + _default_caps = Cap_quota { _config.xml().sub_node("default") + .attribute_value("caps", 0UL) }; } + catch (...) { } + Prio_levels const prio_levels = prio_levels_from_xml(_config.xml()); Affinity::Space const affinity_space = affinity_space_from_xml(_config.xml()); @@ -278,9 +320,11 @@ void Init::Main::_handle_config() /* initial RAM and caps limit before starting new children */ Ram_quota const avail_ram = _avail_ram(); + Cap_quota const avail_caps = _avail_caps(); /* variable used to track the RAM and caps taken by new started children */ Ram_quota used_ram { 0 }; + Cap_quota used_caps { 0 }; /* create new children */ try { @@ -300,12 +344,18 @@ void Init::Main::_handle_config() throw Out_of_ram(); } + if (used_caps.value > avail_caps.value) { + error("capabilities exhausted while starting childen"); + throw Out_of_caps(); + } + try { Init::Child &child = *new (_heap) Init::Child(_env, _heap, *_verbose, Init::Child::Id { ++_child_cnt }, _state_reporter, - start_node, *this, _children, - Ram_quota { avail_ram.value - used_ram.value }, + start_node, *this, *this, _children, + Ram_quota { avail_ram.value - used_ram.value }, + Cap_quota { avail_caps.value - used_caps.value }, *this, prio_levels, affinity_space, _parent_services, _child_services); _children.insert(&child); @@ -317,6 +367,9 @@ void Init::Main::_handle_config() used_ram = Ram_quota { used_ram.value + child.ram_quota().value + metadata_overhead }; + + used_caps = Cap_quota { used_caps.value + + child.cap_quota().value }; } catch (Rom_connection::Rom_connection_failed) { /* @@ -326,6 +379,8 @@ void Init::Main::_handle_config() } catch (Out_of_ram) { warning("memory exhausted during child creation"); } + catch (Out_of_caps) { + warning("local capabilities exhausted during child creation"); } catch (Child::Missing_name_attribute) { warning("skipped startup of nameless child"); } catch (Region_map::Attach_failed) { diff --git a/repos/os/src/init/report.h b/repos/os/src/init/report.h index fcb6b0857..cfb81104f 100644 --- a/repos/os/src/init/report.h +++ b/repos/os/src/init/report.h @@ -33,7 +33,9 @@ class Init::Report_detail : Genode::Noncopyable bool _provided = false; bool _session_args = false; bool _child_ram = false; + bool _child_caps = false; bool _init_ram = false; + bool _init_caps = false; public: @@ -47,7 +49,9 @@ class Init::Report_detail : Genode::Noncopyable _provided = report.attribute_value("provided", false); _session_args = report.attribute_value("session_args", false); _child_ram = report.attribute_value("child_ram", false); + _child_caps = report.attribute_value("child_caps", false); _init_ram = report.attribute_value("init_ram", false); + _init_caps = report.attribute_value("init_caps", false); } bool children() const { return _children; } @@ -56,7 +60,9 @@ class Init::Report_detail : Genode::Noncopyable bool provided() const { return _provided; } bool session_args() const { return _session_args; } bool child_ram() const { return _child_ram; } + bool child_caps() const { return _child_caps; } bool init_ram() const { return _init_ram; } + bool init_caps() const { return _init_caps; } }; diff --git a/repos/os/src/init/server.cc b/repos/os/src/init/server.cc index 9f5738666..b6e8664be 100644 --- a/repos/os/src/init/server.cc +++ b/repos/os/src/init/server.cc @@ -12,6 +12,7 @@ */ /* Genode includes */ +#include #include /* local includes */ @@ -149,10 +150,14 @@ void Init::Server::session_closed(Session_state &session) _report_update_trigger.trigger_report_update(); Ram_transfer::Account &service_ram_account = session.service(); + Cap_transfer::Account &service_cap_account = session.service(); service_ram_account.try_transfer(_env.ram_session_cap(), session.donated_ram_quota()); + service_cap_account.try_transfer(_env.pd_session_cap(), + session.donated_cap_quota()); + Parent::Server::Id id { session.id_at_client().value }; session.destroy(); @@ -183,7 +188,9 @@ void Init::Server::_handle_create_session_request(Xml_node request, char argbuf[Parent::Session_args::MAX_SIZE]; strncpy(argbuf, args.string(), sizeof(argbuf)); + Cap_quota const cap_quota = cap_quota_from_args(argbuf); Ram_quota const ram_quota = ram_quota_from_args(argbuf); + size_t const keep_quota = route.service.factory().session_costs(); if (ram_quota.value < keep_quota) @@ -201,10 +208,13 @@ void Init::Server::_handle_create_session_request(Xml_node request, /* transfer session quota */ try { Ram_transfer::Remote_account env_ram_account(_env.ram(), _env.ram_session_cap()); + Cap_transfer::Remote_account env_cap_account(_env.pd(), _env.pd_session_cap()); Ram_transfer ram_transfer(forward_ram_quota, env_ram_account, route.service); + Cap_transfer cap_transfer(cap_quota, env_cap_account, route.service); ram_transfer.acknowledge(); + cap_transfer.acknowledge(); } catch (...) { /* @@ -212,7 +222,8 @@ void Init::Server::_handle_create_session_request(Xml_node request, * transfor the session quota to us prior issuing the session * request. */ - warning("unable to transfer session quota (", ram_quota, " bytes) " + warning("unable to transfer session quota " + "(", ram_quota, " bytes, ", cap_quota, " caps) " "of forwarded ", name, " session"); session.destroy(); throw Parent::Service_denied(); @@ -233,6 +244,9 @@ void Init::Server::_handle_create_session_request(Xml_node request, if (session.phase == Session_state::INSUFFICIENT_RAM_QUOTA) throw Genode::Insufficient_ram_quota(); + + if (session.phase == Session_state::INSUFFICIENT_CAP_QUOTA) + throw Genode::Insufficient_cap_quota(); } catch (Parent::Service_denied) { _env.parent().session_response(Parent::Server::Id { id.value }, @@ -240,6 +254,9 @@ void Init::Server::_handle_create_session_request(Xml_node request, catch (Genode::Insufficient_ram_quota) { _env.parent().session_response(Parent::Server::Id { id.value }, Parent::INSUFFICIENT_RAM_QUOTA); } + catch (Genode::Insufficient_cap_quota) { + _env.parent().session_response(Parent::Server::Id { id.value }, + Parent::INSUFFICIENT_CAP_QUOTA); } } @@ -249,23 +266,28 @@ void Init::Server::_handle_upgrade_session_request(Xml_node request, _client_id_space.apply(id, [&] (Session_state &session) { Ram_quota const ram_quota { request.attribute_value("ram_quota", 0UL) }; + Cap_quota const cap_quota { request.attribute_value("cap_quota", 0UL) }; session.phase = Session_state::UPGRADE_REQUESTED; try { Ram_transfer::Remote_account env_ram_account(_env.ram(), _env.ram_session_cap()); + Cap_transfer::Remote_account env_cap_account(_env.pd(), _env.pd_session_cap()); Ram_transfer ram_transfer(ram_quota, env_ram_account, session.service()); + Cap_transfer cap_transfer(cap_quota, env_cap_account, session.service()); ram_transfer.acknowledge(); + cap_transfer.acknowledge(); } catch (...) { - warning("unable to upgrade session quota (", ram_quota, " bytes) " + warning("unable to upgrade session quota " + "(", ram_quota, " bytes, ", cap_quota, " caps) " "of forwarded ", session.service().name(), " session"); return; } - session.increase_donated_quota(ram_quota); + session.increase_donated_quota(ram_quota, cap_quota); session.service().initiate_request(session); session.service().wakeup(); }); diff --git a/repos/os/src/init/service.h b/repos/os/src/init/service.h index 8f4da4370..6559f52cd 100644 --- a/repos/os/src/init/service.h +++ b/repos/os/src/init/service.h @@ -71,11 +71,18 @@ class Init::Routed_service : public Async_service, public Abandonable virtual Ram_session_capability ram_cap() const = 0; }; + struct Pd_accessor + { + virtual Pd_session &pd() = 0; + virtual Pd_session_capability pd_cap() const = 0; + }; + private: Child_name _child_name; Ram_accessor &_ram_accessor; + Pd_accessor &_pd_accessor; Session_state::Factory &_factory; @@ -94,6 +101,7 @@ class Init::Routed_service : public Async_service, public Abandonable Routed_service(Registry &services, Child_name const &child_name, Ram_accessor &ram_accessor, + Pd_accessor &pd_accessor, Id_space &server_id_space, Session_state::Factory &factory, Service::Name const &name, @@ -101,7 +109,7 @@ class Init::Routed_service : public Async_service, public Abandonable : Async_service(name, server_id_space, factory, wakeup), _child_name(child_name), - _ram_accessor(ram_accessor), + _ram_accessor(ram_accessor), _pd_accessor(pd_accessor), _factory(factory), _registry_element(services, *this) { } @@ -124,6 +132,22 @@ class Init::Routed_service : public Async_service, public Abandonable { return _ram_accessor.ram_cap(); } + + /** + * Cap_transfer::Account interface + */ + void transfer(Pd_session_capability to, Cap_quota amount) override + { + if (to.valid()) _pd_accessor.pd().transfer_quota(to, amount); + } + + /** + * Cap_transfer::Account interface + */ + Pd_session_capability cap(Cap_quota) const override + { + return _pd_accessor.pd_cap(); + } }; #endif /* _SRC__INIT__SERVICE_H_ */ diff --git a/repos/os/src/init/types.h b/repos/os/src/init/types.h index b39915ae9..27c0d19c3 100644 --- a/repos/os/src/init/types.h +++ b/repos/os/src/init/types.h @@ -16,6 +16,7 @@ #include #include +#include namespace Init { diff --git a/repos/os/src/init/utils.h b/repos/os/src/init/utils.h index 7e3a8e98f..7468b9803 100644 --- a/repos/os/src/init/utils.h +++ b/repos/os/src/init/utils.h @@ -136,13 +136,20 @@ namespace Init { inline void generate_ram_info(Xml_generator &xml, Ram_session const &ram) { - typedef String<32> Value; xml.attribute("quota", Value(ram.ram_quota())); xml.attribute("used", Value(ram.used_ram())); xml.attribute("avail", Value(ram.avail_ram())); } + inline void generate_caps_info(Xml_generator &xml, Pd_session const &pd) + { + typedef String<32> Value; + xml.attribute("quota", Value(pd.cap_quota())); + xml.attribute("used", Value(pd.used_caps())); + xml.attribute("avail", Value(pd.avail_caps())); + } + /** * Read priority-levels declaration from config */ diff --git a/repos/os/src/server/loader/child.h b/repos/os/src/server/loader/child.h index 4e9e573dd..a4a00c03f 100644 --- a/repos/os/src/server/loader/child.h +++ b/repos/os/src/server/loader/child.h @@ -45,6 +45,7 @@ class Loader::Child : public Child_policy Session_label const _label; Name const _binary_name; + Cap_quota const _cap_quota; Ram_quota const _ram_quota; Parent_services &_parent_services; @@ -62,6 +63,7 @@ class Loader::Child : public Child_policy Allocator &alloc, Name const &binary_name, Session_label const &label, + Cap_quota cap_quota, Ram_quota ram_quota, Parent_services &parent_services, Service &local_rom_service, @@ -74,6 +76,7 @@ class Loader::Child : public Child_policy _alloc(alloc), _label(label), _binary_name(binary_name), + _cap_quota(Genode::Child::effective_quota(cap_quota)), _ram_quota(Genode::Child::effective_quota(ram_quota)), _parent_services(parent_services), _local_nitpicker_service(local_nitpicker_service), @@ -94,9 +97,18 @@ class Loader::Child : public Child_policy Binary_name binary_name() const override { return _binary_name; } + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + Ram_session &ref_ram() override { return _env.ram(); } Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); } + void init(Pd_session &pd, Pd_session_capability pd_cap) override + { + pd.ref_account(ref_pd_cap()); + ref_pd().transfer_quota(pd_cap, _cap_quota); + } + void init(Ram_session &ram, Ram_session_capability ram_cap) override { ram.ref_account(ref_ram_cap()); diff --git a/repos/os/src/server/loader/main.cc b/repos/os/src/server/loader/main.cc index bbbcd0e8f..1fe5df3e8 100644 --- a/repos/os/src/server/loader/main.cc +++ b/repos/os/src/server/loader/main.cc @@ -14,7 +14,6 @@ /* Genode includes */ #include #include -#include #include #include #include @@ -204,9 +203,11 @@ class Loader::Session_component : public Rpc_object Env &_env; Session_label const _label; Xml_node const _config; + Cap_quota const _cap_quota; Ram_quota const _ram_quota; Ram_session_client_guard _local_ram { _env.ram_session_cap(), _ram_quota }; Heap _md_alloc { _local_ram, _env.rm() }; + size_t _subsystem_cap_quota_limit = 0; size_t _subsystem_ram_quota_limit = 0; Parent_services _parent_services; Rom_module_registry _rom_modules { _env, _config, _local_ram, _md_alloc }; @@ -243,10 +244,11 @@ class Loader::Session_component : public Rpc_object /** * Constructor */ - Session_component(Env &env, Session_label const &label, - Xml_node config, Ram_quota quota) + Session_component(Env &env, Session_label const &label, Xml_node config, + Cap_quota cap_quota, Ram_quota ram_quota) : - _env(env), _label(label), _config(config), _ram_quota(quota) + _env(env), _label(label), _config(config), + _cap_quota(cap_quota), _ram_quota(ram_quota) { /* fetch all parent-provided ROMs according to the config */ config.for_each_sub_node("parent-rom", [&] (Xml_node rom) @@ -287,6 +289,11 @@ class Loader::Session_component : public Rpc_object throw Rom_module_does_not_exist(); } } + void cap_quota(Cap_quota caps) override + { + _subsystem_cap_quota_limit = caps.value; + } + void ram_quota(Ram_quota quantum) override { _subsystem_ram_quota_limit = quantum.value; @@ -334,6 +341,10 @@ class Loader::Session_component : public Rpc_object return; } + size_t const cap_quota = (_subsystem_cap_quota_limit > 0) + ? min(_subsystem_cap_quota_limit, _cap_quota.value) + : _cap_quota.value; + size_t const ram_quota = (_subsystem_ram_quota_limit > 0) ? min(_subsystem_ram_quota_limit, _ram_quota.value) : _ram_quota.value; @@ -341,7 +352,7 @@ class Loader::Session_component : public Rpc_object try { _child.construct(_env, _md_alloc, binary_name.string(), prefixed_label(_label, Session_label(label.string())), - Ram_quota{ram_quota}, + Cap_quota{cap_quota}, Ram_quota{ram_quota}, _parent_services, _rom_service, _cpu_service, _pd_service, _nitpicker_service, _fault_sigh); @@ -381,6 +392,7 @@ class Loader::Root : public Root_component catch (...) { } return new (md_alloc()) Session_component(_env, label, session_config, + cap_quota_from_args(args), ram_quota_from_args(args)); } diff --git a/repos/os/src/test/bomb/main.cc b/repos/os/src/test/bomb/main.cc index 960733e10..f600da3b2 100644 --- a/repos/os/src/test/bomb/main.cc +++ b/repos/os/src/test/bomb/main.cc @@ -32,6 +32,7 @@ class Bomb_child : public Child_policy Env &_env; Binary_name const _binary_name; Name const _label; + Cap_quota const _cap_quota; Ram_quota const _ram_quota; Registry > &_parent_services; @@ -45,11 +46,13 @@ class Bomb_child : public Child_policy Bomb_child(Env &env, Name const &binary_name, Name const &label, + Cap_quota const cap_quota, Ram_quota const ram_quota, Registry > &parent_services, unsigned generation) : _env(env), _binary_name(binary_name), _label(label), + _cap_quota(Child::effective_quota(cap_quota)), _ram_quota(Child::effective_quota(ram_quota)), _parent_services(parent_services) { @@ -68,12 +71,21 @@ class Bomb_child : public Child_policy Binary_name binary_name() const override { return _binary_name; } + void init(Pd_session &pd, Pd_session_capability pd_cap) override + { + pd.ref_account(_env.pd_session_cap()); + _env.pd().transfer_quota(pd_cap, _cap_quota); + } + void init(Ram_session &ram, Ram_session_capability ram_cap) override { ram.ref_account(_env.ram_session_cap()); _env.ram().transfer_quota(ram_cap, _ram_quota); } + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + Ram_session &ref_ram() override { return _env.ram(); } Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); } @@ -154,10 +166,10 @@ struct Bomb Attached_rom_dataspace config { env, "config" }; unsigned round = 0; - unsigned const rounds = config.xml().attribute_value("rounds", 1U); - unsigned const generation = config.xml().attribute_value("generations", 1U); - unsigned const children = config.xml().attribute_value("children", 2U); - unsigned const sleeptime = config.xml().attribute_value("sleep", 2000U); + unsigned const rounds = config.xml().attribute_value("rounds", 1U); + unsigned const generation = config.xml().attribute_value("generations", 1U); + unsigned const children = config.xml().attribute_value("children", 2U); + unsigned const sleeptime = config.xml().attribute_value("sleep", 2000U); size_t const ram_demand = config.xml().attribute_value("demand", 1024UL * 1024); Heap heap { env.ram(), env.rm() }; @@ -177,6 +189,17 @@ struct Bomb " - not enough memory."); return; } + + size_t const avail_caps = env.pd().avail_caps().value; + size_t const preserved_caps = children*10; + + if (avail_caps < preserved_caps) { + log("I ran out of capabilities."); + return; + } + + Cap_quota const cap_quota { (avail_caps - preserved_caps) / children }; + if (generation == 0) { log("I'm a leaf node - generation 0"); return; @@ -192,7 +215,7 @@ struct Bomb unique_child_name(child_registry, binary_name, generation - 1), - ram_amount, + cap_quota, ram_amount, parent_services, generation - 1); } diff --git a/repos/os/src/test/dynamic_config/loader/main.cc b/repos/os/src/test/dynamic_config/loader/main.cc index f2706ae0b..c39cdec09 100644 --- a/repos/os/src/test/dynamic_config/loader/main.cc +++ b/repos/os/src/test/dynamic_config/loader/main.cc @@ -24,7 +24,7 @@ struct Main Env &env; int counter { -1 }; - Loader::Connection loader { env, Ram_quota{8*1024*1024} }; + Loader::Connection loader { env, Ram_quota{8*1024*1024}, Cap_quota{100} }; Timer::Connection timer { env }; Signal_handler
timer_handler { env.ep(), *this, &Main::handle_timer }; diff --git a/repos/os/src/test/dynamic_config/master/main.cc b/repos/os/src/test/dynamic_config/master/main.cc index cf351cb7d..a3d8face2 100644 --- a/repos/os/src/test/dynamic_config/master/main.cc +++ b/repos/os/src/test/dynamic_config/master/main.cc @@ -36,6 +36,7 @@ struct Test::Policy Policy(Env &env, Name const &name) : Slave::Policy(name, name, *this, env.ep().rpc_ep(), env.rm(), + env.pd(), env.pd_session_cap(), Cap_quota{100}, env.ram(), env.ram_session_cap(), Ram_quota{1024*1024}) { } }; diff --git a/repos/os/src/test/fault_detection/main.cc b/repos/os/src/test/fault_detection/main.cc index f5960fd08..12df2ae81 100644 --- a/repos/os/src/test/fault_detection/main.cc +++ b/repos/os/src/test/fault_detection/main.cc @@ -70,6 +70,7 @@ class Test_child : public Genode::Child_policy private: Env &_env; + Cap_quota const _cap_quota { 30 }; Ram_quota const _ram_quota { 1024*1024 }; Binary_name const _binary_name; Signal_context_capability _sigh; @@ -101,6 +102,9 @@ class Test_child : public Genode::Child_policy Binary_name binary_name() const override { return _binary_name; } + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + Ram_session &ref_ram() override { return _env.ram(); } Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); } @@ -116,8 +120,11 @@ class Test_child : public Genode::Child_policy cpu.exception_sigh(_sigh); } - void init(Pd_session &pd, Pd_session_capability) override + void init(Pd_session &pd, Pd_session_capability pd_cap) override { + pd.ref_account(ref_pd_cap()); + ref_pd().transfer_quota(pd_cap, _cap_quota); + /* register handler for unresolvable page faults */ Region_map_client address_space(pd.address_space()); address_space.fault_handler(_sigh); @@ -162,7 +169,7 @@ struct Faulting_loader_child_test void start_iteration(Env &env, Signal_context_capability fault_sigh) { - loader.construct(env, Ram_quota{1024*1024}); + loader.construct(env, Ram_quota{1024*1024}, Cap_quota{100}); /* register fault handler at loader session */ loader->fault_sigh(fault_sigh); @@ -207,7 +214,7 @@ struct Faulting_loader_grand_child_test void start_iteration(Env &env, Signal_context_capability fault_sigh) { - loader.construct(env, Ram_quota{2*1024*1024}); + loader.construct(env, Ram_quota{2*1024*1024}, Cap_quota{100}); /* import config into loader session */ { diff --git a/repos/os/src/test/loader/main.cc b/repos/os/src/test/loader/main.cc index 1de7a9007..f0c5e3bc0 100644 --- a/repos/os/src/test/loader/main.cc +++ b/repos/os/src/test/loader/main.cc @@ -25,7 +25,7 @@ struct Test::Main { Env &_env; - Loader::Connection _loader { _env, Ram_quota{8*1024*1024} }; + Loader::Connection _loader { _env, Ram_quota{8*1024*1024}, Cap_quota{100} }; Timer::Connection _timer { _env }; Loader::Area _size; diff --git a/repos/os/src/test/resource_request/main.cc b/repos/os/src/test/resource_request/main.cc index 7ebf56d35..14f2be577 100644 --- a/repos/os/src/test/resource_request/main.cc +++ b/repos/os/src/test/resource_request/main.cc @@ -70,6 +70,7 @@ struct Test::Monitor xml.node("start", [&] () { xml.attribute("name", "test-resource_request"); + xml.attribute("caps", 3000); xml.node("resource", [&] () { xml.attribute("name", "RAM"); xml.attribute("quantum", _ram_quota); diff --git a/repos/os/src/test/resource_yield/main.cc b/repos/os/src/test/resource_yield/main.cc index 9f6b6ab2f..bf9e99243 100644 --- a/repos/os/src/test/resource_yield/main.cc +++ b/repos/os/src/test/resource_yield/main.cc @@ -285,7 +285,7 @@ class Test::Parent { Parent &_parent; - enum { SLAVE_QUOTA = 10*1024*1024 }; + enum { SLAVE_CAPS = 50, SLAVE_RAM = 10*1024*1024 }; void yield_response() override { @@ -296,7 +296,8 @@ class Test::Parent : Slave::Policy(Label("child"), "test-resource_yield", *this, env.ep().rpc_ep(), env.rm(), - env.ram(), env.ram_session_cap(), Ram_quota{SLAVE_QUOTA}), + env.pd(), env.pd_session_cap(), Cap_quota{SLAVE_CAPS}, + env.ram(), env.ram_session_cap(), Ram_quota{SLAVE_RAM}), _parent(parent) { configure(""); diff --git a/repos/ports/src/app/gdb_monitor/app_child.h b/repos/ports/src/app/gdb_monitor/app_child.h index cd97f2c93..865ad9605 100644 --- a/repos/ports/src/app/gdb_monitor/app_child.h +++ b/repos/ports/src/app/gdb_monitor/app_child.h @@ -50,8 +50,11 @@ class Gdb_monitor::App_child : public Child_policy Allocator &_alloc; + Pd_session_capability _ref_pd_cap { _env.pd_session_cap() }; + Pd_session &_ref_pd { _env.pd() }; + Ram_session_capability _ref_ram_cap { _env.ram_session_cap() }; - Ram_session_client _ref_ram { _ref_ram_cap }; + Ram_session &_ref_ram { _env.ram() }; const char *_unique_name; @@ -60,6 +63,7 @@ class Gdb_monitor::App_child : public Child_policy Region_map &_rm; Ram_quota _ram_quota; + Cap_quota _cap_quota; Rpc_entrypoint _entrypoint; @@ -112,6 +116,7 @@ class Gdb_monitor::App_child : public Child_policy Allocator &alloc, char const *unique_name, Ram_quota ram_quota, + Cap_quota cap_quota, Signal_receiver &signal_receiver, Xml_node target_node) : @@ -119,7 +124,7 @@ class Gdb_monitor::App_child : public Child_policy _alloc(alloc), _unique_name(unique_name), _rm(_env.rm()), - _ram_quota(ram_quota), + _ram_quota(ram_quota), _cap_quota(cap_quota), _entrypoint(&_env.pd(), STACK_SIZE, "GDB monitor entrypoint"), _child_config(env.ram(), _rm, target_node), _config_policy("config", _child_config.dataspace(), &_entrypoint), @@ -155,6 +160,10 @@ class Gdb_monitor::App_child : public Child_policy Name name() const override { return _unique_name; } + Pd_session &ref_pd() override { return _ref_pd; } + + Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; } + Ram_session &ref_ram() override { return _ref_ram; } Ram_session_capability ref_ram_cap() const override { return _ref_ram_cap; } @@ -166,6 +175,13 @@ class Gdb_monitor::App_child : public Child_policy _ref_ram.transfer_quota(cap, _ram_quota); } + void init(Pd_session &session, + Pd_session_capability cap) override + { + session.ref_account(_ref_pd_cap); + _ref_pd.transfer_quota(cap, _cap_quota); + } + Service &resolve_session_request(Service::Name const &service_name, Session_state::Args const &args) override { diff --git a/repos/ports/src/app/gdb_monitor/gdbserver/genode-low.cc b/repos/ports/src/app/gdb_monitor/gdbserver/genode-low.cc index a1154a5a7..79ac2e357 100644 --- a/repos/ports/src/app/gdb_monitor/gdbserver/genode-low.cc +++ b/repos/ports/src/app/gdb_monitor/gdbserver/genode-low.cc @@ -469,6 +469,17 @@ extern "C" int fork() Number_of_bytes ram_quota = genode_env->ram().avail_ram().value - preserved_ram_quota; + Cap_quota const avail_cap_quota = genode_env->pd().avail_caps(); + + Genode::size_t const preserved_caps = 100; + + if (avail_cap_quota.value < preserved_caps) { + error("not enough available caps for preservation of ", preserved_caps); + return -1; + } + + Cap_quota const cap_quota { avail_cap_quota.value - preserved_caps }; + /* start the application */ static Heap alloc(genode_env->ram(), genode_env->rm()); @@ -483,6 +494,7 @@ extern "C" int fork() alloc, filename, Ram_quota{ram_quota}, + cap_quota, signal_receiver, target_node); 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 9d9ab86f4..9b0130547 100644 --- a/repos/ports/src/app/gdb_monitor/pd_session_component.h +++ b/repos/ports/src/app/gdb_monitor/pd_session_component.h @@ -116,6 +116,15 @@ class Gdb_monitor::Pd_session_component : public Rpc_object Capability linker_area() override { return _linker_area.Rpc_object::cap(); } + void ref_account(Capability pd) override { + warning("Pd_session::ref_account not implemented"); } + + void transfer_quota(Capability pd, Cap_quota amount) override { + warning("Pd_session::transfer_quota not implemented"); } + + Cap_quota cap_quota() const { return _pd.cap_quota(); } + Cap_quota used_caps() const { return _pd.used_caps(); } + Capability native_pd() override { return _pd.native_pd(); } }; diff --git a/repos/ports/src/noux/child.h b/repos/ports/src/noux/child.h index e63b02092..f54beb522 100644 --- a/repos/ports/src/noux/child.h +++ b/repos/ports/src/noux/child.h @@ -143,6 +143,9 @@ class Noux::Child : public Rpc_object, enum { STACK_SIZE = 8*1024*sizeof(long) }; Rpc_entrypoint _ep { &_env.pd(), STACK_SIZE, "noux_process", false }; + Pd_session &_ref_pd; + Pd_session_capability const _ref_pd_cap; + Ram_session &_ref_ram; Ram_session_capability const _ref_ram_cap; @@ -334,6 +337,8 @@ class Noux::Child : public Rpc_object, Args const &args, Sysio::Env const &sysio_env, Allocator &heap, + Pd_session &ref_pd, + Pd_session_capability ref_pd_cap, Ram_session &ref_ram, Ram_session_capability ref_ram_cap, Parent_services &parent_services, @@ -354,6 +359,7 @@ class Noux::Child : public Rpc_object, _root_dir(root_dir), _destruct_queue(destruct_queue), _heap(heap), + _ref_pd (ref_pd), _ref_pd_cap (ref_pd_cap), _ref_ram(ref_ram), _ref_ram_cap(ref_ram_cap), _args(ref_ram, _env.rm(), ARGS_DS_SIZE, args), _sysio_env(_ref_ram, _env.rm(), sysio_env), @@ -368,7 +374,8 @@ class Noux::Child : public Rpc_object, _noux_service, _empty_rom_service, _rom_service, _parent_services, *this, parent_exit, *this, _destruct_handler, - ref_ram, ref_ram_cap, _verbose.enabled()), + ref_pd, ref_pd_cap, ref_ram, ref_ram_cap, + _verbose.enabled()), _child(_env.rm(), _ep, _child_policy) { if (_verbose.enabled()) @@ -534,6 +541,7 @@ class Noux::Child : public Rpc_object, args, env, _heap, + _ref_pd, _ref_pd_cap, _ref_ram, _ref_ram_cap, _parent_services, false, diff --git a/repos/ports/src/noux/child_policy.h b/repos/ports/src/noux/child_policy.h index a87f79dec..7615d2812 100644 --- a/repos/ports/src/noux/child_policy.h +++ b/repos/ports/src/noux/child_policy.h @@ -58,6 +58,8 @@ class Noux::Child_policy : public Genode::Child_policy Parent_exit *_parent_exit; File_descriptor_registry &_file_descriptor_registry; Signal_context_capability _destruct_context_cap; + Pd_session &_ref_pd; + Pd_session_capability _ref_pd_cap; Ram_session &_ref_ram; Ram_session_capability _ref_ram_cap; int _exit_value; @@ -93,6 +95,8 @@ class Noux::Child_policy : public Genode::Child_policy Parent_exit *parent_exit, File_descriptor_registry &file_descriptor_registry, Signal_context_capability destruct_context_cap, + Pd_session &ref_pd, + Pd_session_capability ref_pd_cap, Ram_session &ref_ram, Ram_session_capability ref_ram_cap, bool verbose) @@ -109,6 +113,7 @@ class Noux::Child_policy : public Genode::Child_policy _parent_exit(parent_exit), _file_descriptor_registry(file_descriptor_registry), _destruct_context_cap(destruct_context_cap), + _ref_pd (ref_pd), _ref_pd_cap (ref_pd_cap), _ref_ram(ref_ram), _ref_ram_cap(ref_ram_cap), _exit_value(~0), _verbose(verbose) @@ -122,8 +127,10 @@ class Noux::Child_policy : public Genode::Child_policy Name name() const override { return _name; } - Ram_session &ref_ram() override { return _ref_ram; } + Pd_session &ref_pd() override { return _ref_pd; } + Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; } + Ram_session &ref_ram() override { return _ref_ram; } Ram_session_capability ref_ram_cap() const override { return _ref_ram_cap; } void init(Ram_session &session, Ram_session_capability cap) override diff --git a/repos/ports/src/noux/main.cc b/repos/ports/src/noux/main.cc index db9042cb5..0e24098c3 100644 --- a/repos/ports/src/noux/main.cc +++ b/repos/ports/src/noux/main.cc @@ -272,6 +272,8 @@ struct Noux::Main _args_of_init_process(), env_string_of_init_process(_config.xml()), _heap, + _env.pd(), + _env.pd_session_cap(), _ref_ram, Ram_session_capability(), _parent_services, diff --git a/repos/ports/src/noux/pd_session_component.h b/repos/ports/src/noux/pd_session_component.h index 2f850e0b4..43631201a 100644 --- a/repos/ports/src/noux/pd_session_component.h +++ b/repos/ports/src/noux/pd_session_component.h @@ -33,10 +33,23 @@ class Noux::Pd_session_component : public Rpc_object Pd_connection _pd; + Pd_session &_ref_pd; + Region_map_component _address_space; Region_map_component _stack_area; Region_map_component _linker_area; + template + auto _with_automatic_cap_upgrade(FUNC func) -> decltype(func()) + { + Cap_quota upgrade { 10 }; + enum { NUM_ATTEMPTS = 3 }; + return retry( + [&] () { return func(); }, + [&] () { _ref_pd.transfer_quota(_pd, upgrade); }, + NUM_ATTEMPTS); + } + public: /** @@ -46,12 +59,20 @@ class Noux::Pd_session_component : public Rpc_object Child_policy::Name const &name, Dataspace_registry &ds_registry) : - _ep(ep), _pd(env, name.string()), + _ep(ep), _pd(env, name.string()), _ref_pd(env.pd()), _address_space(alloc, _ep, ds_registry, _pd, _pd.address_space()), _stack_area (alloc, _ep, ds_registry, _pd, _pd.stack_area()), _linker_area (alloc, _ep, ds_registry, _pd, _pd.linker_area()) { _ep.manage(this); + + /* + * Equip the PD with an initial cap quota that suffices in the + * common case. Further capabilities are provisioned on demand + * via '_with_automatic_cap_upgrade'. + */ + _pd.ref_account(env.pd_session_cap()); + _ref_pd.transfer_quota(_pd, Cap_quota{10}); } ~Pd_session_component() @@ -115,15 +136,21 @@ class Noux::Pd_session_component : public Rpc_object bool assign_pci(addr_t addr, uint16_t bdf) override { return _pd.assign_pci(addr, bdf); } - Signal_source_capability alloc_signal_source() override { - return _pd.alloc_signal_source(); } + Signal_source_capability alloc_signal_source() override + { + return _with_automatic_cap_upgrade([&] () { + return _pd.alloc_signal_source(); }); + } void free_signal_source(Signal_source_capability cap) override { _pd.free_signal_source(cap); } Capability alloc_context(Signal_source_capability source, - unsigned long imprint) override { - return _pd.alloc_context(source, imprint); } + unsigned long imprint) override + { + return _with_automatic_cap_upgrade([&] () { + return _pd.alloc_context(source, imprint); }); + } void free_context(Capability cap) override { _pd.free_context(cap); } @@ -131,8 +158,11 @@ class Noux::Pd_session_component : public Rpc_object void submit(Capability context, unsigned cnt) override { _pd.submit(context, cnt); } - Native_capability alloc_rpc_cap(Native_capability ep) override { - return _pd.alloc_rpc_cap(ep); } + Native_capability alloc_rpc_cap(Native_capability ep) override + { + return _with_automatic_cap_upgrade([&] () { + return _pd.alloc_rpc_cap(ep); }); + } void free_rpc_cap(Native_capability cap) override { _pd.free_rpc_cap(cap); } @@ -146,6 +176,13 @@ class Noux::Pd_session_component : public Rpc_object Capability linker_area() override { return _linker_area.Rpc_object::cap(); } + void ref_account(Capability pd) override { } + + void transfer_quota(Capability pd, Cap_quota amount) override { } + + Cap_quota cap_quota() const { return _pd.cap_quota(); } + Cap_quota used_caps() const { return _pd.used_caps(); } + Capability native_pd() override { return _pd.native_pd(); } }; diff --git a/repos/ports/src/noux/syscall.cc b/repos/ports/src/noux/syscall.cc index 8812eb55e..83467a801 100644 --- a/repos/ports/src/noux/syscall.cc +++ b/repos/ports/src/noux/syscall.cc @@ -498,6 +498,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _args, _sysio_env.env(), _heap, + _ref_pd, _ref_pd_cap, _ref_ram, _ref_ram_cap, _parent_services, true,