From 6f1d3862cda1d146eddef7db5f5ab066533f0d3b Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 8 Oct 2021 15:15:41 +0200 Subject: [PATCH] base: introduce Env::try_session The new 'Env::try_session' method mirrors the existing 'Env::session' without implicitly handling exceptions of the types 'Out_of_ram', 'Out_of_caps', 'Insufficient_ram_quota', and 'Insufficient_cap_quota'. It enables runtime environments like init to reflect those exceptions to their children instead of paying the costs of implicit session-quota upgrades out of the own pocket. By changing the 'Parent_service' to use 'try_session', this patch fixes a resource-exhaustion problem of init in Sculpt OS that occurred when the GPU multiplexer created a large batch of IO_MEM sessions, with each session requiring a second attempt with the session quota upgraded by 4 KiB. Issue #3767 --- repos/base/include/base/env.h | 28 +++++- repos/base/include/base/service.h | 92 +++++++++++++++---- repos/base/src/lib/base/component.cc | 31 ++++--- .../src/app/qt5/qt_launchpad/main.cpp | 17 +++- repos/libports/src/lib/libc/internal/env.h | 6 ++ repos/os/src/lib/sandbox/service.h | 4 +- repos/ports/src/app/gdb_monitor/app_child.h | 16 +++- 7 files changed, 153 insertions(+), 41 deletions(-) diff --git a/repos/base/include/base/env.h b/repos/base/include/base/env.h index d07f05d9ce..db431a763f 100644 --- a/repos/base/include/base/env.h +++ b/repos/base/include/base/env.h @@ -68,6 +68,16 @@ struct Genode::Env : Interface */ virtual Id_space &id_space() = 0; + /** + * Create session with quota upgrades as needed + * + * \throw Service_denied + * + * In contrast to 'try_session', this method implicitly handles + * 'Insufficient_ram_quota' and 'Insufficient_cap_quota' by successively + * increasing the session quota. On the occurrence of an 'Out_of_ram' + * or 'Out_of_caps' exception, a resource request is issued to the parent. + */ virtual Session_capability session(Parent::Service_name const &, Parent::Client::Id, Parent::Session_args const &, @@ -82,10 +92,6 @@ struct Genode::Env : Interface * \param affinity preferred CPU affinity for the session * * \throw Service_denied - * \throw Insufficient_cap_quota - * \throw Insufficient_ram_quota - * \throw Out_of_caps - * \throw Out_of_ram * * See the documentation of 'Parent::session'. * @@ -161,6 +167,20 @@ struct Genode::Env : Interface * \noapi */ virtual void reinit_main_thread(Capability &stack_area_rm) = 0; + + /** + * Attempt the creation of a session + * + * \throw Service_denied + * \throw Insufficient_cap_quota + * \throw Insufficient_ram_quota + * \throw Out_of_caps + * \throw Out_of_ram + */ + virtual Session_capability try_session(Parent::Service_name const &, + Parent::Client::Id, + Parent::Session_args const &, + Affinity const &) = 0; }; #endif /* _INCLUDE__BASE__ENV_H_ */ diff --git a/repos/base/include/base/service.h b/repos/base/include/base/service.h index 74d0f1f854..2eaf528c25 100644 --- a/repos/base/include/base/service.h +++ b/repos/base/include/base/service.h @@ -27,6 +27,7 @@ namespace Genode { class Service; template class Session_factory; template class Local_service; + class Try_parent_service; class Parent_service; class Async_service; class Child_service; @@ -230,9 +231,14 @@ class Genode::Local_service : public Service /** - * Representation of a service provided by our parent + * Representation of a strictly accounted service provided by our parent + * + * The 'Try_parent_service' reflects the local depletion of RAM or cap quotas + * during 'initiate_request' via 'Out_of_ram' or 'Out_of_caps' exceptions. + * This is appropriate in situations that demand strict accounting of resource + * use per child, e.g., child components hosted by the init component. */ -class Genode::Parent_service : public Service +class Genode::Try_parent_service : public Service { private: @@ -240,12 +246,13 @@ class Genode::Parent_service : public Service public: - /** - * Constructor - */ - Parent_service(Env &env, Service::Name const &name) + Try_parent_service(Env &env, Service::Name const &name) : Service(name), _env(env) { } + /* + * \throw Out_of_ram + * \throw Out_of_caps + */ void initiate_request(Session_state &session) override { switch (session.phase) { @@ -256,32 +263,35 @@ class Genode::Parent_service : public Service _env.id_space()); try { - session.cap = _env.session(name().string(), - session.id_at_parent->id(), - Session_state::Server_args(session).string(), - session.affinity()); + session.cap = _env.try_session(name().string(), + session.id_at_parent->id(), + Session_state::Server_args(session).string(), + session.affinity()); session.phase = Session_state::AVAILABLE; } catch (Out_of_ram) { session.id_at_parent.destruct(); - session.phase = Session_state::SERVICE_DENIED; } - + session.phase = Session_state::CLOSED; + throw; + } catch (Out_of_caps) { session.id_at_parent.destruct(); - session.phase = Session_state::SERVICE_DENIED; } - + session.phase = Session_state::CLOSED; + throw; + } catch (Insufficient_ram_quota) { session.id_at_parent.destruct(); - session.phase = Session_state::INSUFFICIENT_RAM_QUOTA; } - + session.phase = Session_state::INSUFFICIENT_RAM_QUOTA; + } catch (Insufficient_cap_quota) { session.id_at_parent.destruct(); - session.phase = Session_state::INSUFFICIENT_CAP_QUOTA; } - + session.phase = Session_state::INSUFFICIENT_CAP_QUOTA; + } catch (Service_denied) { session.id_at_parent.destruct(); - session.phase = Session_state::SERVICE_DENIED; } + session.phase = Session_state::SERVICE_DENIED; + } break; @@ -326,6 +336,50 @@ class Genode::Parent_service : public Service }; +/** + * Representation of a service provided by our parent + * + * In contrast to 'Try_parent_service', the 'Parent_service' handles the + * exhaution of the local RAM or cap quotas by issuing resource requests. + * This is useful in situations where the parent is unconditionally willing + * to satisfy the resource needs of its children. + */ +class Genode::Parent_service : public Try_parent_service +{ + private: + + Env &_env; + + public: + + Parent_service(Env &env, Service::Name const &name) + : Try_parent_service(env, name), _env(env) { } + + void initiate_request(Session_state &session) override + { + for (unsigned i = 0; i < 10; i++) { + + try { + Try_parent_service::initiate_request(session); + return; + } + catch (Out_of_ram) { + Ram_quota ram_quota { ram_quota_from_args(session.args().string()) }; + Parent::Resource_args args(String<64>("ram_quota=", ram_quota)); + _env.parent().resource_request(args); + } + catch (Out_of_caps) { + Cap_quota cap_quota { cap_quota_from_args(session.args().string()) }; + Parent::Resource_args args(String<64>("cap_quota=", cap_quota)); + _env.parent().resource_request(args); + } + } + + error("parent-session request repeatedly failed"); + } +}; + + /** * Representation of a service that asynchronously responds to session request */ diff --git a/repos/base/src/lib/base/component.cc b/repos/base/src/lib/base/component.cc index 8973e3b6b5..59390c48a2 100644 --- a/repos/base/src/lib/base/component.cc +++ b/repos/base/src/lib/base/component.cc @@ -116,10 +116,10 @@ namespace { _session_blockade->block(); } - Session_capability session(Parent::Service_name const &name, - Parent::Client::Id id, - Parent::Session_args const &args, - Affinity const &affinity) override + Session_capability try_session(Parent::Service_name const &name, + Parent::Client::Id id, + Parent::Session_args const &args, + Affinity const &affinity) override { if (!args.valid_string()) { warning(name.string(), " session denied because of truncated arguments"); @@ -128,6 +128,20 @@ namespace { Mutex::Guard guard(_mutex); + Session_capability cap = _parent.session(id, name, args, affinity); + + if (cap.valid()) + return cap; + + _block_for_session(); + return _parent.session_cap(id); + } + + Session_capability session(Parent::Service_name const &name, + Parent::Client::Id id, + Parent::Session_args const &args, + Affinity const &affinity) override + { /* * Since we account for the backing store for session meta data on * the route between client and server, the session quota provided @@ -154,14 +168,7 @@ namespace { 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); - - if (cap.valid()) - return cap; - - _block_for_session(); - return _parent.session_cap(id); + return try_session(name, id, Parent::Session_args(argbuf), affinity); } catch (Insufficient_ram_quota) { diff --git a/repos/libports/src/app/qt5/qt_launchpad/main.cpp b/repos/libports/src/app/qt5/qt_launchpad/main.cpp index b812fb792c..208e3cee88 100644 --- a/repos/libports/src/app/qt5/qt_launchpad/main.cpp +++ b/repos/libports/src/app/qt5/qt_launchpad/main.cpp @@ -54,10 +54,22 @@ struct Qt_launchpad_namespace::Local_env : Genode::Env Parent::Client::Id id, Parent::Session_args const &session_args, Affinity const &affinity) override - { return genode_env.session(service_name, id, session_args, affinity); } + { + return genode_env.session(service_name, id, session_args, affinity); + } + + Session_capability try_session(Parent::Service_name const &service_name, + Parent::Client::Id id, + Parent::Session_args const &session_args, + Affinity const &affinity) override + { + return genode_env.try_session(service_name, id, session_args, affinity); + } void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override - { return genode_env.upgrade(id, args); } + { + return genode_env.upgrade(id, args); + } void close(Parent::Client::Id id) override { return genode_env.close(id); } @@ -72,6 +84,7 @@ struct Qt_launchpad_namespace::Local_env : Genode::Env } }; + void Libc::Component::construct(Libc::Env &env) { Libc::with_libc([&] { diff --git a/repos/libports/src/lib/libc/internal/env.h b/repos/libports/src/lib/libc/internal/env.h index 982775b235..e94515c10d 100644 --- a/repos/libports/src/lib/libc/internal/env.h +++ b/repos/libports/src/lib/libc/internal/env.h @@ -114,6 +114,12 @@ class Libc::Env_implementation : public Libc::Env, public Config_accessor Affinity const &aff) override { return _env.session(name, id, args, aff); } + Session_capability try_session(Parent::Service_name const &name, + Parent::Client::Id id, + Parent::Session_args const &args, + Affinity const &aff) override { + return _env.try_session(name, id, args, aff); } + void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override { return _env.upgrade(id, args); } diff --git a/repos/os/src/lib/sandbox/service.h b/repos/os/src/lib/sandbox/service.h index eb629a4c20..9589de91b5 100644 --- a/repos/os/src/lib/sandbox/service.h +++ b/repos/os/src/lib/sandbox/service.h @@ -40,7 +40,7 @@ class Sandbox::Abandonable : Interface }; -class Sandbox::Parent_service : public Genode::Parent_service, public Abandonable +class Sandbox::Parent_service : public Genode::Try_parent_service, public Abandonable { private: @@ -51,7 +51,7 @@ class Sandbox::Parent_service : public Genode::Parent_service, public Abandonabl Parent_service(Registry ®istry, Env &env, Service::Name const &name) : - Genode::Parent_service(env, name), _reg_elem(registry, *this) + Genode::Try_parent_service(env, name), _reg_elem(registry, *this) { } }; diff --git a/repos/ports/src/app/gdb_monitor/app_child.h b/repos/ports/src/app/gdb_monitor/app_child.h index fcdca656d7..de48a3efb3 100644 --- a/repos/ports/src/app/gdb_monitor/app_child.h +++ b/repos/ports/src/app/gdb_monitor/app_child.h @@ -78,10 +78,22 @@ class Gdb_monitor::App_child : public Child_policy, Parent::Client::Id id, Parent::Session_args const &session_args, Affinity const &affinity) override - { return genode_env.session(service_name, id, session_args, affinity); } + { + return genode_env.session(service_name, id, session_args, affinity); + } + + Session_capability try_session(Parent::Service_name const &service_name, + Parent::Client::Id id, + Parent::Session_args const &session_args, + Affinity const &affinity) override + { + return genode_env.session(service_name, id, session_args, affinity); + } void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override - { return genode_env.upgrade(id, args); } + { + return genode_env.upgrade(id, args); + } void close(Parent::Client::Id id) override { return genode_env.close(id); }