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,