diff --git a/repos/base-hw/src/core/env.cc b/repos/base-hw/src/core/env.cc index cfb2d1517..88d6dfb72 100644 --- a/repos/base-hw/src/core/env.cc +++ b/repos/base-hw/src/core/env.cc @@ -18,4 +18,4 @@ #include -void Genode::upgrade_pd_session_quota(Genode::size_t quota) { assert(false); } +void Genode::upgrade_pd_quota_non_blocking(Genode::size_t quota) { assert(false); } diff --git a/repos/base-hw/src/core/spec/arndale/platform_services.cc b/repos/base-hw/src/core/spec/arndale/platform_services.cc index 32d358a94..94a9959a5 100644 --- a/repos/base-hw/src/core/spec/arndale/platform_services.cc +++ b/repos/base-hw/src/core/spec/arndale/platform_services.cc @@ -15,21 +15,21 @@ #include #include -/* Core includes */ +/* core includes */ #include +#include /* for 'Core_service' type */ #include /* * Add ARM virtualization specific vm service */ -void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep, - Genode::Sliced_heap *sh, - Genode::Service_registry *ls) +void Genode::platform_add_local_services(Rpc_entrypoint *ep, + Sliced_heap *sh, + Registry *services) { using namespace Genode; static Vm_root vm_root(ep, sh); - static Local_service vm_ls(Vm_session::service_name(), &vm_root); - ls->insert(&vm_ls); + static Core_service vm_service(*services, vm_root); } diff --git a/repos/base-hw/src/core/spec/imx53/trustzone/platform_services.cc b/repos/base-hw/src/core/spec/imx53/trustzone/platform_services.cc index 9ce1fc6c8..ccb9ce07b 100644 --- a/repos/base-hw/src/core/spec/imx53/trustzone/platform_services.cc +++ b/repos/base-hw/src/core/spec/imx53/trustzone/platform_services.cc @@ -18,19 +18,17 @@ /* Core includes */ #include #include +#include #include /* * Add TrustZone specific vm service */ -void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep, - Genode::Sliced_heap *sh, - Genode::Service_registry *ls) +void Genode::platform_add_local_services(Rpc_entrypoint *ep, + Sliced_heap *sliced_heap, + Registry *local_services) { - using namespace Genode; - - static Vm_root vm_root(ep, sh); - static Local_service vm_ls(Vm_session::service_name(), &vm_root); - ls->insert(&vm_ls); + static Vm_root vm_root(ep, sliced_heap); + static Core_service vm_service(*local_services, vm_root); } diff --git a/repos/base-hw/src/core/spec/x86_64/muen/platform_services.cc b/repos/base-hw/src/core/spec/x86_64/muen/platform_services.cc index c16100267..3b436ba93 100644 --- a/repos/base-hw/src/core/spec/x86_64/muen/platform_services.cc +++ b/repos/base-hw/src/core/spec/x86_64/muen/platform_services.cc @@ -14,7 +14,7 @@ /* Genode includes */ #include -/* Core includes */ +/* core includes */ #include #include #include @@ -24,18 +24,15 @@ /* * Add I/O port service and virtualization specific vm service */ -void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep, - Genode::Sliced_heap *sh, - Genode::Service_registry *ls) +void Genode::platform_add_local_services(Rpc_entrypoint *ep, + Sliced_heap *sh, + Registry *services) { - using namespace Genode; - static Vm_root vm_root(ep, sh); - static Local_service vm_ls(Vm_session::service_name(), &vm_root); + static Core_service vm_ls(*services, vm_root); + static Io_port_root io_port_root(core_env()->pd_session(), platform()->io_port_alloc(), sh); - static Local_service io_port_ls(Io_port_session::service_name(), - &io_port_root); - ls->insert(&vm_ls); - ls->insert(&io_port_ls); + static Core_service io_port_ls(*services, + io_port_root); } 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 9966948ff..40d307949 100644 --- a/repos/base-hw/src/include/base/internal/native_env.h +++ b/repos/base-hw/src/include/base/internal/native_env.h @@ -20,9 +20,13 @@ namespace Genode { /** - * Upgrade quota of the PD session within my Genode environment + * Upgrade quota of the PD session within my Genode environment non-blocking + * + * This function doesn't lock the environment when upgrading. This is + * needed when doing upgrades in situations where the environment is + * already locked due to the operation that triggered the upgrade. */ - void upgrade_pd_session_quota(Genode::size_t); + void upgrade_pd_quota_non_blocking(size_t); }; #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 ddfdd45e5..c293471c5 100644 --- a/repos/base-hw/src/lib/base/env.cc +++ b/repos/base-hw/src/lib/base/env.cc @@ -12,18 +12,14 @@ */ /* Genode includes */ -#include #include /* base-internal includes */ +#include #include - -void Genode::upgrade_pd_session_quota(Genode::size_t quota) +void Genode::upgrade_pd_quota_non_blocking(size_t quota) { - char buf[128]; - snprintf(buf, sizeof(buf), "ram_quota=%lu", quota); - Pd_session_capability cap = - *static_cast(env()->pd_session()); - env()->parent()->upgrade(cap, buf); + internal_env().parent().upgrade(Parent::Env::pd(), + String<64>("ram_quota=", quota).string()); } diff --git a/repos/base-hw/src/lib/base/ipc.cc b/repos/base-hw/src/lib/base/ipc.cc index 8603d6469..8787f2542 100644 --- a/repos/base-hw/src/lib/base/ipc.cc +++ b/repos/base-hw/src/lib/base/ipc.cc @@ -109,7 +109,7 @@ Rpc_exception_code Genode::ipc_call(Native_capability dst, } }, - [&] () { upgrade_pd_session_quota(3*4096); }); + [&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); }); return Rpc_exception_code(utcb.exception_code()); } @@ -154,7 +154,7 @@ Genode::Rpc_request Genode::ipc_reply_wait(Reply_capability const &, default: break; } }, - [&] () { upgrade_pd_session_quota(3*4096); }); + [&] () { upgrade_pd_quota_non_blocking(3 * 1024 * sizeof(addr_t)); }); 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 fc0d5c78d..4e81d365f 100644 --- a/repos/base-hw/src/lib/base/signal.cc +++ b/repos/base-hw/src/lib/base/signal.cc @@ -20,7 +20,9 @@ /* base-internal includes */ #include +#include #include +#include using namespace Genode; @@ -63,12 +65,10 @@ void Signal_transmitter::submit(unsigned cnt) Signal_receiver::Signal_receiver() { retry( - [&] () { - _cap = env()->pd_session()->alloc_signal_source(); - }, + [&] () { _cap = internal_env().pd().alloc_signal_source(); }, [&] () { log("upgrading quota donation for PD session"); - env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K"); + internal_env().upgrade(Parent::Env::pd(), "ram_quota=8K"); } ); } @@ -104,11 +104,7 @@ Signal_context_capability Signal_receiver::manage(Signal_context * const c) _contexts.insert(&c->_receiver_le); return c->_cap; }, - [&] () { - log("upgrading quota donation for PD session"); - env()->parent()->upgrade(env()->pd_session_cap(), "ram_quota=8K"); - } - ); + [&] () { upgrade_pd_quota_non_blocking(1024 * sizeof(addr_t)); }); return c->_cap; } diff --git a/repos/base-linux/src/core/include/core_env.h b/repos/base-linux/src/core/include/core_env.h index b6073b12c..a30dd4f19 100644 --- a/repos/base-linux/src/core/include/core_env.h +++ b/repos/base-linux/src/core/include/core_env.h @@ -24,6 +24,7 @@ #include #include #include +#include /* base-internal includes */ #include @@ -138,8 +139,6 @@ namespace Genode { typedef Synchronized_ram_session Core_ram_session; - Core_parent _core_parent; - /* * Initialize the stack area before creating the first thread, * which happens to be the '_entrypoint'. @@ -163,6 +162,10 @@ namespace Genode { Heap _heap; Ram_session_capability const _ram_session_cap; + Registry _services; + + Core_parent _core_parent { _heap, _services }; + public: /** @@ -210,6 +213,8 @@ namespace Genode { warning(__FILE__, ":", __LINE__, " not implemented"); return Cpu_session_capability(); } + + Registry &services() { return _services; } }; diff --git a/repos/base-linux/src/include/base/internal/local_parent.h b/repos/base-linux/src/include/base/internal/local_parent.h index dcef6b77c..b35d88e45 100644 --- a/repos/base-linux/src/include/base/internal/local_parent.h +++ b/repos/base-linux/src/include/base/internal/local_parent.h @@ -23,7 +23,11 @@ /* base-internal includes */ #include -namespace Genode { class Local_parent; } +namespace Genode { + + class Local_session; + class Local_parent; +} /** @@ -42,6 +46,7 @@ class Genode::Local_parent : public Expanding_parent_client private: Allocator &_alloc; + Id_space _local_sessions_id_space; public: @@ -49,10 +54,9 @@ class Genode::Local_parent : public Expanding_parent_client ** Parent interface ** **********************/ - Session_capability session(Service_name const &, - Session_args const &, - Affinity const & = Affinity()); - void close(Session_capability); + Session_capability session(Client::Id, Service_name const &, Session_args const &, + Affinity const & = Affinity()) override; + Close_result close(Client::Id) override; /** * Constructor diff --git a/repos/base-linux/src/include/base/internal/local_rm_session.h b/repos/base-linux/src/include/base/internal/local_rm_session.h index f81ba41c8..b03f08ea1 100644 --- a/repos/base-linux/src/include/base/internal/local_rm_session.h +++ b/repos/base-linux/src/include/base/internal/local_rm_session.h @@ -19,17 +19,22 @@ #include /* base-internal includes */ +#include #include #include namespace Genode { struct Local_rm_session; } -struct Genode::Local_rm_session : Rm_session +struct Genode::Local_rm_session : Rm_session, Local_session { Allocator &md_alloc; - Local_rm_session(Allocator &md_alloc) : md_alloc(md_alloc) { } + Local_rm_session(Allocator &md_alloc, Id_space &id_space, + Parent::Client::Id id) + : + Local_session(id_space, id, *this), md_alloc(md_alloc) + { } Capability create(size_t size) { diff --git a/repos/base-linux/src/include/base/internal/local_session.h b/repos/base-linux/src/include/base/internal/local_session.h new file mode 100644 index 000000000..0c660967c --- /dev/null +++ b/repos/base-linux/src/include/base/internal/local_session.h @@ -0,0 +1,44 @@ +/* + * \brief Meta data for component-local sessions + * \author Norman Feske + * \date 2016-10-13 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__INTERNAL__LOCAL_SESSION_H_ +#define _INCLUDE__BASE__INTERNAL__LOCAL_SESSION_H_ + +/* Genode includes */ +#include + +namespace Genode { struct Local_session; } + + +struct Genode::Local_session : Parent::Client +{ + private: + + Id_space::Element _id_space_element; + + Session_capability _cap; + + public: + + Local_session(Id_space &id_space, Parent::Client::Id id, + Session &session) + : + _id_space_element(*this, id_space, id), + _cap(Local_capability::local_cap(&session)) + { } + + Capability local_session_cap() { return _cap; } +}; + + +#endif /* _INCLUDE__BASE__INTERNAL__LOCAL_SESSION_H_ */ diff --git a/repos/base-linux/src/include/base/internal/platform_env.h b/repos/base-linux/src/include/base/internal/platform_env.h index c6b10dd09..ebd8ae5a6 100644 --- a/repos/base-linux/src/include/base/internal/platform_env.h +++ b/repos/base-linux/src/include/base/internal/platform_env.h @@ -69,9 +69,9 @@ class Genode::Platform_env_base : public Env_deprecated Pd_session_capability pd_cap) : _ram_session_cap(ram_cap), - _ram_session_client(_ram_session_cap), + _ram_session_client(_ram_session_cap, Parent::Env::ram()), _cpu_session_cap(cpu_cap), - _cpu_session_client(cpu_cap), + _cpu_session_client(cpu_cap, Parent::Env::cpu()), _region_map_mmap(false), _pd_session_cap(pd_cap), _local_pd_session(_pd_session_cap) diff --git a/repos/base-linux/src/lib/base/platform_env.cc b/repos/base-linux/src/lib/base/platform_env.cc index 77fd27abe..167a5b48d 100644 --- a/repos/base-linux/src/lib/base/platform_env.cc +++ b/repos/base-linux/src/lib/base/platform_env.cc @@ -59,37 +59,44 @@ bool Region_map_mmap::_dataspace_writable(Dataspace_capability ds) ** Local_parent ** ******************/ -Session_capability Local_parent::session(Service_name const &service_name, +Session_capability Local_parent::session(Parent::Client::Id id, + Service_name const &service_name, Session_args const &args, Affinity const &affinity) { - if (strcmp(service_name.string(), Rm_session::service_name()) == 0) - { - Local_rm_session *session = new (_alloc) Local_rm_session(_alloc); + if (strcmp(service_name.string(), Rm_session::service_name()) == 0) { - return Local_capability::local_cap(session); + Local_rm_session *local_rm_session = new (_alloc) + Local_rm_session(_alloc, _local_sessions_id_space, id); + + return local_rm_session->local_session_cap(); } - return Expanding_parent_client::session(service_name, args, affinity); + return Expanding_parent_client::session(id, service_name, args, affinity); } -void Local_parent::close(Session_capability session) +Parent::Close_result Local_parent::close(Client::Id id) { + auto close_local_fn = [&] (Local_session &local_session) + { + Capability rm = + static_cap_cast(local_session.local_session_cap()); + destroy(_alloc, Local_capability::deref(rm)); + }; + /* - * Handle non-local capabilities + * Local RM sessions are present in the '_local_sessions_id_space'. If the + * apply succeeds, 'id' referred to the local session. Otherwise, we + * forward the request to the parent. */ - if (session.valid()) { - Parent_client::close(session); - return; + try { + _local_sessions_id_space.apply(id, close_local_fn); + return CLOSE_DONE; } + catch (Id_space::Unknown_id) { } - /* - * Detect capability to local RM session - */ - Capability rm = static_cap_cast(session); - - destroy(_alloc, Local_capability::deref(rm)); + return Parent_client::close(id); } @@ -148,9 +155,9 @@ Local_parent &Platform_env::_parent() Platform_env::Platform_env() : - Platform_env_base(static_cap_cast(_parent().session("Env::ram_session", "")), - static_cap_cast(_parent().session("Env::cpu_session", "")), - static_cap_cast (_parent().session("Env::pd_session", ""))), + Platform_env_base(static_cap_cast(_parent().session_cap(Parent::Env::ram())), + static_cap_cast(_parent().session_cap(Parent::Env::cpu())), + static_cap_cast (_parent().session_cap(Parent::Env::pd()))), _heap(Platform_env_base::ram_session(), Platform_env_base::rm_session()), _emergency_ram_ds(ram_session()->alloc(_emergency_ram_size())) { 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 b5ec4816b..fa3e6fd56 100644 --- a/repos/base-nova/src/lib/base/rpc_cap_alloc.cc +++ b/repos/base-nova/src/lib/base/rpc_cap_alloc.cc @@ -17,6 +17,9 @@ #include #include +/* base-internal includes */ +#include + /* NOVA-specific part of the PD session interface */ #include @@ -37,11 +40,7 @@ Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd, Native_capabili return native_pd.alloc_rpc_cap(ep, entry, 0); }, [&] () { - Pd_session_client *client = - dynamic_cast(&pd); - - if (client) - env()->parent()->upgrade(*client, "ram_quota=16K"); + internal_env().upgrade(Parent::Env::pd(), "ram_quota=16K"); }); native_pd.imprint_rpc_cap(new_obj_cap, new_obj_cap.local_name()); diff --git a/repos/base/include/base/child.h b/repos/base/include/base/child.h index ba1399a05..b3a4ba5ab 100644 --- a/repos/base/include/base/child.h +++ b/repos/base/include/base/child.h @@ -18,11 +18,14 @@ #include #include #include +#include #include -#include +#include #include -#include -#include +#include +#include +#include +#include #include namespace Genode { @@ -41,44 +44,47 @@ namespace Genode { */ struct Genode::Child_policy { + typedef String<64> Name; + typedef String<64> Binary_name; + typedef String<64> Linker_name; + virtual ~Child_policy() { } /** - * Return process name of the child + * Name of the child used as the child's label prefix */ - virtual const char *name() const = 0; + virtual Name name() const = 0; + + /** + * ROM module name of the binary to start + */ + virtual Binary_name binary_name() const { return name(); } + + /** + * ROM module name of the dynamic linker + */ + virtual Linker_name linker_name() const { return "ld.lib.so"; } /** * Determine service to provide a session request * - * \return Service to be contacted for the new session, or - * 0 if session request could not be resolved + * \return service to be contacted for the new session + * + * \throw Parent::Service_denied */ - virtual Service *resolve_session_request(const char * /*service_name*/, - const char * /*args*/) - { return 0; } + virtual Service &resolve_session_request(Service::Name const &, + Session_state::Args const &) = 0; /** * Apply transformations to session arguments */ - virtual void filter_session_args(const char * /*service*/, + virtual void filter_session_args(Service::Name const &, char * /*args*/, size_t /*args_len*/) { } /** * Register a service provided by the child - * - * \param name service name - * \param root interface for creating sessions for the service - * \param alloc allocator to be used for child-specific - * meta-data allocations - * \return true if announcement succeeded, or false if - * child is not permitted to announce service */ - virtual bool announce_service(const char * /*name*/, - Root_capability /*root*/, - Allocator * /*alloc*/, - Server * /*server*/) - { return false; } + virtual void announce_service(Service::Name const &) { } /** * Apply session affinity policy @@ -91,11 +97,6 @@ struct Genode::Child_policy return affinity; } - /** - * Unregister services that had been provided by the child - */ - virtual void unregister_services() { } - /** * Exit child */ @@ -110,8 +111,8 @@ struct Genode::Child_policy * The RAM session returned by this method is used for session-quota * transfers. */ - virtual Ram_session *ref_ram_session() { return env()->ram_session(); } - virtual Ram_session_capability ref_ram_cap() const { return env()->ram_session_cap(); } + virtual Ram_session &ref_ram() = 0; + virtual Ram_session_capability ref_ram_cap() const = 0; /** * Respond to the release of resources by the child @@ -125,6 +126,56 @@ struct Genode::Child_policy * Take action on additional resource needs by the child */ virtual void resource_request(Parent::Resource_args const &) { } + + /** + * Initialize the child's RAM session + * + * The function must define the child's reference account and transfer + * the child's initial RAM quota. + */ + virtual void init(Ram_session &, Capability) = 0; + + /** + * Initialize the child's CPU session + * + * The function may install an exception signal handler or assign CPU quota + * to the child. + */ + virtual void init(Cpu_session &, Capability) { } + + /** + * Initialize the child's PD session + * + * The function may install a region-map fault handler for the child's + * address space ('Pd_session::address_space');. + */ + virtual void init(Pd_session &, Capability) { } + + class Nonexistent_id_space : Exception { }; + + /** + * ID space for sessions provided by the child + * + * \throw Nonexistent_id_space + */ + virtual Id_space &server_id_space() { throw Nonexistent_id_space(); } + + /** + * Return region map for the child's address space + * + * \param pd the child's PD session capability + * + * By default, the function returns a 'nullptr'. In this case, the 'Child' + * interacts with the address space of the child's PD session via RPC calls + * to the 'Pd_session::address_space'. + * + * By overriding the default, those RPC calls can be omitted, which is + * useful if the child's PD session (including the PD's address space) is + * virtualized by the parent. If the virtual PD session is served by the + * same entrypoint as the child's parent interface, an RPC call to 'pd' + * would otherwise produce a deadlock. + */ + virtual Region_map *address_space(Pd_session &) { return nullptr; } }; @@ -150,9 +201,11 @@ struct Genode::Child_policy * to our account, and subsequently transfer the same amount from our * account to the client. */ -class Genode::Child : protected Rpc_object +class Genode::Child : protected Rpc_object, + Session_state::Ready_callback, + Session_state::Closed_callback { - public: + private: struct Initial_thread_base { @@ -192,52 +245,114 @@ class Genode::Child : protected Rpc_object Capability cap() { return _cap; } }; - private: - - class Session; - - /* PD session representing the protection domain of the child */ - Pd_session_capability _pd; - - /* RAM session that contains the quota of the child */ - Ram_session_capability _ram; - - /* CPU session that contains the quota of the child */ - Cpu_session_capability _cpu; - - /* services where the PD, RAM, and CPU resources come from */ - Service &_pd_service; - Service &_ram_service; - Service &_cpu_service; - - /* heap for child-specific allocations using the child's quota */ - Heap _heap; - - Rpc_entrypoint &_entrypoint; - Parent_capability _parent_cap; - /* child policy */ - Child_policy &_policy; + Child_policy &_policy; /* sessions opened by the child */ - Lock _lock; /* protect list manipulation */ - Object_pool _session_pool; - List _session_list; + Id_space _id_space; - /* server role */ - Server _server; + typedef Session_state::Args Args; - /* session-argument buffer */ - char _args[Parent::Session_args::MAX_SIZE]; + template + struct Env_connection + { + typedef String<64> Label; + + Args const _args; + Service &_service; + Local_connection _connection; + + /** + * Construct session arguments with the child policy applied + */ + Args _construct_args(Child_policy &policy, Label const &label) + { + /* copy original arguments into modifiable buffer */ + char buf[Session_state::Args::capacity()]; + buf[0] = 0; + + /* supply label as session argument */ + if (label.valid()) + Arg_string::set_arg_string(buf, sizeof(buf), "label", label.string()); + + /* apply policy to argument buffer */ + policy.filter_session_args(CONNECTION::service_name(), buf, sizeof(buf)); + + return Session_state::Args(Cstring(buf)); + } + + Env_connection(Child_policy &policy, Id_space &id_space, + Id_space::Id id, Label const &label = Label()) + : + _args(_construct_args(policy, label)), + _service(policy.resolve_session_request(CONNECTION::service_name(), _args)), + _connection(_service, id_space, id, _args, + policy.filter_session_affinity(Affinity())) + { } + + typedef typename CONNECTION::Session_type SESSION; + + SESSION &session() { return _connection.session(); } + Capability cap() const { return _connection.cap(); } + }; + + Env_connection _ram { _policy, + _id_space, Parent::Env::ram() }; + + Env_connection _pd { _policy, + _id_space, Parent::Env::pd() }; + + Env_connection _cpu { _policy, + _id_space, Parent::Env::cpu() }; + + Env_connection _log { _policy, + _id_space, Parent::Env::log() }; + + Env_connection _binary { _policy, + _id_space, Parent::Env::binary(), _policy.binary_name() }; + + Lazy_volatile_object > _linker { _policy, + _id_space, Parent::Env::linker(), _policy.linker_name() }; + + /* call 'Child_policy::init' methods for the environment sessions */ + void _init_env_sessions() + { + _policy.init(_ram.session(), _ram.cap()); + _policy.init(_cpu.session(), _cpu.cap()); + _policy.init(_pd.session(), _pd.cap()); + } + bool const _env_sessions_initialized = ( _init_env_sessions(), true ); + + Dataspace_capability _linker_dataspace() + { + try { + _linker.construct(_policy, _id_space, + Parent::Env::linker(), _policy.linker_name()); + return _linker->session().dataspace(); + } + catch (Parent::Service_denied) { return Rom_dataspace_capability(); } + } + + /* heap for child-specific allocations using the child's quota */ + Heap _heap; + + /* factory for dynamically created session-state objects */ + Session_state::Factory _session_factory { _heap }; + + Rpc_entrypoint &_entrypoint; + Parent_capability _parent_cap; /* signal handlers registered by the child */ Signal_context_capability _resource_avail_sigh; Signal_context_capability _yield_sigh; + Signal_context_capability _session_sigh; /* arguments fetched by the child in response to a yield signal */ Lock _yield_request_lock; Resource_args _yield_request_args; + Initial_thread _initial_thread { _cpu.session(), _pd.cap(), "initial" }; + struct Process { class Missing_dynamic_linker : Exception { }; @@ -313,30 +428,19 @@ class Genode::Child : protected Rpc_object Process _process; - /** - * Attach session information to a child - * - * \throw Ram_session::Quota_exceeded the child's heap partition cannot - * hold the session meta data - */ - void _add_session(const Session &s); + void _revert_quota_and_destroy(Session_state &); + + Close_result _close(Session_state &); /** - * Close session and revert quota donation associated with it + * Session_state::Ready_callback */ - void _remove_session(Session *s); - - void _close(Session *s); + void session_ready(Session_state &session) override; /** - * Return service interface targetting the parent - * - * The service returned by this method is used as default - * provider for the RAM, CPU, and RM resources of the child. It is - * solely used for targeting resource donations during - * 'Parent::upgrade_quota()' calls. + * Session_state::Closed_callback */ - static Service &_parent_service(); + void session_closed(Session_state &) override; public: @@ -357,60 +461,18 @@ class Genode::Child : protected Rpc_object /** * Constructor * - * \param elf_ds dataspace that contains the ELF binary - * \param ldso_ds dataspace that contains the dynamic linker, - * started if 'elf_ds' is a dynamically linked - * executable - * \param pd_cap capability of the new protection domain, - * used as argument for creating the initial - * thread, and handed out to the child as its - * environment - * \param pd PD session used for assigning the parent - * capability of the new process - * \param ram_cap RAM session capability handed out to the - * child as its environment - * \param ram RAM session used to allocate the BSS and - * DATA segments and as backing store for the - * local heap partition to keep child-specific - * meta data - * \param cpu_cap CPU session capability handed out to the - * child as its environment - * \param initial_thread initial thread of the new protection domain - * \param local_rm local address space - * \param remote_rm address space of new protection domain - * \param pd_service provider of the 'pd' session - * \param ram_service provider of the 'ram' session - * \param cpu_service provider of the 'cpu' session + * \param rm local address space, usually 'env.rm()' + * \param entrypoint entrypoint used to serve the parent interface of + * the child + * \param policy policy for the child * + * \throw Parent::Service_denied if the initial sessions for the + * child's environment could not be + * opened * \throw Ram_session::Alloc_failed * \throw Process_startup_failed - * - * Usually, the pairs of 'pd' and 'pd_cap', 'initial_thread' and - * 'cpu_cap', 'ram' and 'ram_cap' belong to each other. References to - * the session interfaces are passed as separate arguments in addition - * to the capabilities to allow the creator of a child to operate on - * locally implemented interfaces during the child initialization. - * - * The 'ram_service', 'cpu_service', and 'pd_service' arguments are - * needed to direct quota upgrades referring to the resources of - * the child environment. By default, we expect that these - * resources are provided by the parent. */ - Child(Dataspace_capability elf_ds, - Dataspace_capability ldso_ds, - Pd_session_capability pd_cap, - Pd_session &pd, - Ram_session_capability ram_cap, - Ram_session &ram, - Cpu_session_capability cpu_cap, - Initial_thread_base &initial_thread, - Region_map &local_rm, - Region_map &remote_rm, - Rpc_entrypoint &entrypoint, - Child_policy &policy, - Service &pd_service = _parent_service(), - Service &ram_service = _parent_service(), - Service &cpu_service = _parent_service()); + Child(Region_map &rm, Rpc_entrypoint &entrypoint, Child_policy &policy); /** * Destructor @@ -421,25 +483,40 @@ class Genode::Child : protected Rpc_object virtual ~Child(); /** - * Return heap that uses the child's quota + * RAM quota unconditionally consumed by the child's environment */ - Allocator *heap() { return &_heap; } - - Pd_session_capability pd_session_cap() const { return _pd; } - Ram_session_capability ram_session_cap() const { return _ram; } - Cpu_session_capability cpu_session_cap() const { return _cpu; } - Parent_capability parent_cap() const { return cap(); } + static size_t env_ram_quota() + { + return Cpu_connection::RAM_QUOTA + Ram_connection::RAM_QUOTA + + Pd_connection::RAM_QUOTA + Log_connection::RAM_QUOTA + + 2*Rom_connection::RAM_QUOTA; + } /** - * Discard all sessions to specified service - * - * When this method is called, we assume the server protection - * domain to be dead and all that all server quota was already - * transferred back to our own 'env()->ram_session()' account. Note - * that the specified server object may not exist anymore. We do - * not de-reference the server argument in here! + * Deduce session costs from usable ram quota */ - void revoke_server(const Server *server); + static size_t effective_ram_quota(size_t const ram_quota) + { + if (ram_quota < env_ram_quota()) + return 0; + + return ram_quota - env_ram_quota(); + } + + /** + * Return heap that uses the child's quota + */ + Allocator &heap() { return _heap; } + + Ram_session_capability ram_session_cap() const { return _ram.cap(); } + + Parent_capability parent_cap() const { return cap(); } + + Ram_session &ram() { return _ram.session(); } + Cpu_session &cpu() { return _cpu.session(); } + Pd_session &pd() { return _pd .session(); } + + Session_state::Factory &session_factory() { return _session_factory; } /** * Instruct the child to yield resources @@ -461,12 +538,16 @@ class Genode::Child : protected Rpc_object ** Parent interface ** **********************/ - void announce(Service_name const &, Root_capability) override; - Session_capability session(Service_name const &, Session_args const &, - Affinity const &) override; - void upgrade(Session_capability, Upgrade_args const &) override; - void close(Session_capability) override; + void announce(Service_name const &) override; + void session_sigh(Signal_context_capability) override; + Session_capability session(Client::Id, Service_name const &, + Session_args const &, Affinity const &) override; + Session_capability session_cap(Client::Id) override; + Upgrade_result upgrade(Client::Id, Upgrade_args const &) override; + Close_result close(Client::Id) override; void exit(int) override; + void session_response(Server::Id, Session_response) override; + void deliver_session_cap(Server::Id, Session_capability) override; Thread_capability main_thread_cap() const override; void resource_avail_sigh(Signal_context_capability) override; void resource_request(Resource_args const &) override; diff --git a/repos/base/include/base/connection.h b/repos/base/include/base/connection.h index fbf51ccc0..d09213a23 100644 --- a/repos/base/include/base/connection.h +++ b/repos/base/include/base/connection.h @@ -16,20 +16,56 @@ #include #include +#include -namespace Genode { template class Connection; } +namespace Genode { + + class Connection_base; + template class Connection; +} + + +class Genode::Connection_base : public Noncopyable +{ + protected: + + Env &_env; + + Parent::Client _parent_client; + + Id_space::Element const _id_space_element; + + void _block_for_session_response(); + + public: + + Connection_base(Env &env) + : + _env(env), + _id_space_element(_parent_client, _env.id_space()) + { } + + /** + * Legacy constructor + * + * \noapi + */ + Connection_base(); + + void upgrade_ram(size_t bytes) + { + String<64> const args("ram_quota=", bytes); + _env.upgrade(_id_space_element.id(), args.string()); + } +}; /** * Representation of an open connection to a service */ template -class Genode::Connection : public Noncopyable +class Genode::Connection : public Connection_base { - public: - - enum On_destruction { CLOSE = false, KEEP_OPEN = true }; - private: /* @@ -37,15 +73,12 @@ class Genode::Connection : public Noncopyable * 'session' method that is called before the 'Connection' is * constructed. */ + enum { FORMAT_STRING_SIZE = Parent::Session_args::MAX_SIZE }; char _session_args[FORMAT_STRING_SIZE]; char _affinity_arg[sizeof(Affinity)]; - Parent &_parent; - - On_destruction _on_destruction; - void _session(Parent &parent, Affinity const &affinity, const char *format_args, va_list list) @@ -63,7 +96,8 @@ class Genode::Connection : public Noncopyable memcpy(&affinity, _affinity_arg, sizeof(Affinity)); try { - return env()->parent()->session(_session_args, affinity); } + return _env.session(_id_space_element.id(), + _session_args, affinity); } catch (...) { error(SESSION_TYPE::service_name(), "-session creation failed " "(", Cstring(_session_args), ")"); @@ -75,20 +109,15 @@ class Genode::Connection : public Noncopyable public: + typedef SESSION_TYPE Session_type; + /** * Constructor - * - * \param od session policy applied when destructing the connection - * - * The 'op' argument defines whether the session should automatically - * be closed by the destructor of the connection (CLOSE), or the - * session should stay open (KEEP_OPEN). The latter is useful in - * situations where the creator a connection merely passes the - * session capability of the connection to another party but never - * invokes any of the session's RPC functions. */ - Connection(Env &env, Capability, On_destruction od = CLOSE) - : _parent(env.parent()), _on_destruction(od) { } + Connection(Env &env, Capability) + : + Connection_base(env), _cap(_request_cap()) + { } /** * Constructor @@ -97,28 +126,18 @@ class Genode::Connection : public Noncopyable * \deprecated Use the constructor with 'Env &' as first * argument instead */ - Connection(Capability, On_destruction od = CLOSE) - : _parent(*env()->parent()), _on_destruction(od) { } + Connection(Capability) : _cap(_request_cap()) { } /** * Destructor */ - ~Connection() - { - if (_on_destruction == CLOSE) - _parent.close(_cap); - } + ~Connection() { _env.close(_id_space_element.id()); } /** * Return session capability */ Capability cap() const { return _cap; } - /** - * Define session policy - */ - void on_destruction(On_destruction od) { _on_destruction = od; } - /** * Issue session request to the parent */ diff --git a/repos/base/include/base/env.h b/repos/base/include/base/env.h index 421130489..202a1f411 100644 --- a/repos/base/include/base/env.h +++ b/repos/base/include/base/env.h @@ -73,6 +73,61 @@ struct Genode::Env * Once we add 'Env::upgrade', we can remove this accessor. */ virtual Pd_session_capability pd_session_cap() = 0; + + /** + * ID space of sessions obtained from the parent + */ + virtual Id_space &id_space() = 0; + + virtual Session_capability session(Parent::Service_name const &, + Parent::Client::Id, + Parent::Session_args const &, + Affinity const &) = 0; + + /** + * Create session to a service + * + * \param SESSION_TYPE session interface type + * \param id session ID of new session + * \param args session constructor arguments + * \param affinity preferred CPU affinity for the session + * + * \throw Service_denied parent denies session request + * \throw Quota_exceeded our own quota does not suffice for + * the creation of the new session + * \throw Unavailable + * + * This method blocks until the session is available or an error + * occurred. + */ + template + Capability session(Parent::Client::Id id, + Parent::Session_args const &args, + Affinity const &affinity) + { + Session_capability cap = session(SESSION_TYPE::service_name(), + id, args, affinity); + return static_cap_cast(cap); + } + + /** + * Upgrade session quota + * + * \param id ID of recipient session + * \param args description of the amount of quota to transfer + * + * \throw Quota_exceeded quota could not be transferred + * + * The 'args' argument has the same principle format as the 'args' + * argument of the 'session' operation. + */ + virtual void upgrade(Parent::Client::Id id, + Parent::Upgrade_args const &args) = 0; + + /** + * Close session and block until the session is gone + */ + virtual void close(Parent::Client::Id) = 0; }; #endif /* _INCLUDE__BASE__ENV_H_ */ diff --git a/repos/base/include/base/local_connection.h b/repos/base/include/base/local_connection.h new file mode 100644 index 000000000..bb064d2c1 --- /dev/null +++ b/repos/base/include/base/local_connection.h @@ -0,0 +1,135 @@ +/* + * \brief Connection to a local child + * \author Norman Feske + * \date 2016-11-10 + * + * The 'Local_connection' can be used to locally establish a connection + * to a 'Local_service' or a 'Parent_service'. + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__LOCAL_CONNECTION_H_ +#define _INCLUDE__BASE__LOCAL_CONNECTION_H_ + +#include +#include + +namespace Genode { + + class Local_connection_base; + template class Local_connection; +} + + +struct Genode::Local_connection_base : Noncopyable +{ + public: + + typedef Session_state::Args Args; + + protected: + + Session_state _session_state; + + private: + + static Args _init_args(Args const &args, size_t const &ram_quota) + { + /* copy original arguments into modifiable buffer */ + char buf[Args::capacity()]; + strncpy(buf, args.string(), sizeof(buf)); + + Arg_string::set_arg(buf, sizeof(buf), "ram_quota", + String<64>(ram_quota).string()); + + /* return result as a copy */ + return Args(Cstring(buf)); + } + + protected: + + Local_connection_base(Service &service, + Id_space &id_space, + Parent::Client::Id id, + Args const &args, Affinity const &affinity, + size_t ram_quota) + : + _session_state(service, id_space, id, _init_args(args, ram_quota), + affinity) + { + _session_state.service().initiate_request(_session_state); + } + + ~Local_connection_base() + { + if (_session_state.alive()) { + _session_state.phase = Session_state::CLOSE_REQUESTED; + _session_state.service().initiate_request(_session_state); + } + } +}; + + +template +class Genode::Local_connection : Local_connection_base +{ + private: + + typedef typename CONNECTION::Session_type SESSION; + + Lazy_volatile_object _client; + + public: + + Capability cap() const + { + return reinterpret_cap_cast(_session_state.cap); + } + + SESSION &session() + { + /* + * If session comes from a local service (e.g,. a virtualized + * RAM session, we return the reference to the corresponding + * component object, which can be called directly. + * + * Otherwise, if the session is provided + */ + if (_session_state.local_ptr) + return *static_cast(_session_state.local_ptr); + + /* + * The session is provided remotely. So return a client-stub + * for interacting with the session. + */ + if (_client.constructed()) + return *_client; + + /* + * This error is printed if the session could not be + * established or the session is provided by a child service. + */ + error(SESSION::service_name(), " session (", _session_state.args(), ") " + "unavailable"); + throw Parent::Service_denied(); + } + + Local_connection(Service &service, Id_space &id_space, + Parent::Client::Id id, Args const &args, + Affinity const &affinity) + : + Local_connection_base(service, id_space, id, args, + affinity, CONNECTION::RAM_QUOTA) + { + if (_session_state.cap.valid()) + _client.construct(cap()); + } +}; + +#endif /* _INCLUDE__BASE__LOCAL_CONNECTION_H_ */ diff --git a/repos/base/include/base/service.h b/repos/base/include/base/service.h index 0bc4ec2fc..3a932d7ee 100644 --- a/repos/base/include/base/service.h +++ b/repos/base/include/base/service.h @@ -14,100 +14,42 @@ #ifndef _INCLUDE__BASE__SERVICE_H_ #define _INCLUDE__BASE__SERVICE_H_ -#include -#include #include #include #include +#include +#include +#include namespace Genode { - class Client; - class Server; class Service; - class Local_service; + template class Session_factory; + template class Local_service; class Parent_service; class Child_service; - class Service_registry; } -/** - * Client role - * - * A client is someone who applies for a service. If the service is not - * available yet, we enqueue the client into a wait queue and wake him up - * as soon as the requested service gets available. - */ -class Genode::Client : public List::Element -{ - private: - - Cancelable_lock _service_apply_lock; - const char *_apply_for; - - public: - - /** - * Constructor - */ - Client(): _service_apply_lock(Lock::LOCKED), _apply_for(0) { } - - virtual ~Client() { } - - /** - * Set/Request service name that we are currently applying for - */ - void apply_for(const char *apply_for) { _apply_for = apply_for; } - const char *apply_for() { return _apply_for; } - - /** - * Service wait queue support - */ - void sleep() { _service_apply_lock.lock(); } - void wakeup() { _service_apply_lock.unlock(); } -}; - - -/** - * Server role - * - * A server is a process that provides one or multiple services. For the - * most part, this class is used as an opaque key to represent the server - * role. - */ -class Genode::Server -{ - private: - - Ram_session_capability _ram; - - public: - - /** - * Constructor - * - * \param ram RAM session capability of the server process used, - * for quota transfers from/to the server - */ - Server(Ram_session_capability ram): _ram(ram) { } - - /** - * Return RAM session capability of the server process - */ - Ram_session_capability ram_session_cap() const { return _ram; } -}; - - -class Genode::Service : public List::Element +class Genode::Service : Noncopyable { public: - enum { MAX_NAME_LEN = 32 }; + typedef Session_state::Name Name; private: - char _name[MAX_NAME_LEN]; + Name const _name; + Ram_session_capability const _ram; + + protected: + + typedef Session_state::Factory Factory; + + /** + * Return factory to use for creating 'Session_state' objects + */ + virtual Factory &_factory(Factory &client_factory) { return client_factory; } public: @@ -123,52 +65,52 @@ class Genode::Service : public List::Element * Constructor * * \param name service name + * \param ram RAM session to receive/withdraw session quota */ - Service(const char *name) { strncpy(_name, name, sizeof(_name)); } + Service(Name const &name, Ram_session_capability ram) + : _name(name), _ram(ram) { } virtual ~Service() { } /** * Return service name */ - const char *name() const { return _name; } + Name const &name() const { return _name; } /** - * Create session + * Create new session-state object * - * \param args session-construction arguments - * \param affinity preferred CPU affinity of session + * The 'service' argument for the 'Session_state' corresponds to this + * session state. All subsequent 'Session_state' arguments correspond + * to the forwarded 'args'. + */ + template + Session_state &create_session(Factory &client_factory, ARGS &&... args) + { + return _factory(client_factory).create(*this, args...); + } + + /** + * Attempt the immediate (synchronous) creation of a session * - * \throw Invalid_args - * \throw Unavailable - * \throw Quota_exceeded + * Sessions to local services and parent services are usually created + * immediately during the dispatching of the 'Parent::session' request. + * In these cases, it is not needed to wait for an asynchronous + * response. */ - virtual Session_capability session(char const *args, - Affinity const &affinity) = 0; + virtual void initiate_request(Session_state &session) = 0; /** - * Extend resource donation to an existing session + * Wake up service to query session requests */ - virtual void upgrade(Session_capability session, const char *args) = 0; - - /** - * Close session - */ - virtual void close(Session_capability /*session*/) { } - - /** - * Return server providing the service - */ - virtual Server *server() const { return 0; } + virtual void wakeup() { } /** * Return the RAM session to be used for trading resources */ - Ram_session_capability ram_session_cap() + Ram_session_capability ram() const { - if (server()) - return server()->ram_session_cap(); - return Ram_session_capability(); + return _ram; } }; @@ -176,36 +118,118 @@ class Genode::Service : public List::Element /** * Representation of a locally implemented service */ +template class Genode::Local_service : public Service { + public: + + struct Factory + { + typedef Session_state::Args Args; + + class Denied : Exception { }; + + /** + * \throw Denied + */ + virtual SESSION &create(Args const &, Affinity) = 0; + + virtual void upgrade(SESSION &, Args const &) = 0; + virtual void destroy(SESSION &) = 0; + }; + + /** + * Factory of a local service that provides a single static session + */ + class Single_session_factory : public Factory + { + private: + + typedef Session_state::Args Args; + + SESSION &_s; + + public: + + Single_session_factory(SESSION &session) : _s(session) { } + + SESSION &create (Args const &, Affinity) override { return _s; } + void upgrade (SESSION &, Args const &) override { } + void destroy (SESSION &) override { } + }; + private: - Root *_root; + Factory &_factory; + + template + void _apply_to_rpc_obj(Session_state &session, FUNC const &fn) + { + SESSION *rpc_obj = dynamic_cast(session.local_ptr); + + if (rpc_obj) + fn(*rpc_obj); + else + warning("local ", SESSION::service_name(), " session " + "(", session.args(), ") has no valid RPC object"); + } public: - Local_service(const char *name, Root *root) - : Service(name), _root(root) { } + /** + * Constructor + */ + Local_service(Factory &factory) + : + Service(SESSION::service_name(), Ram_session_capability()), + _factory(factory) + { } - Session_capability session(const char *args, Affinity const &affinity) override + void initiate_request(Session_state &session) override { - try { return _root->session(args, affinity); } - catch (Root::Invalid_args) { throw Invalid_args(); } - catch (Root::Unavailable) { throw Unavailable(); } - catch (Root::Quota_exceeded) { throw Quota_exceeded(); } - catch (Genode::Ipc_error) { throw Unavailable(); } - } + switch (session.phase) { - void upgrade(Session_capability session, const char *args) override - { - try { _root->upgrade(session, args); } - catch (Genode::Ipc_error) { throw Unavailable(); } - } + case Session_state::CREATE_REQUESTED: - void close(Session_capability session) override - { - try { _root->close(session); } - catch (Genode::Ipc_error) { throw Blocking_canceled(); } + try { + SESSION &rpc_obj = _factory.create(session.args(), + session.affinity()); + session.local_ptr = &rpc_obj; + session.cap = rpc_obj.cap(); + session.phase = Session_state::AVAILABLE; + } + catch (typename Factory::Denied) { + session.phase = Session_state::INVALID_ARGS; } + + break; + + case Session_state::UPGRADE_REQUESTED: + { + String<64> const args("ram_quota=", session.ram_upgrade); + + _apply_to_rpc_obj(session, [&] (SESSION &rpc_obj) { + _factory.upgrade(rpc_obj, args.string()); }); + + session.phase = Session_state::CAP_HANDED_OUT; + session.confirm_ram_upgrade(); + } + break; + + case Session_state::CLOSE_REQUESTED: + { + _apply_to_rpc_obj(session, [&] (SESSION &rpc_obj) { + _factory.destroy(rpc_obj); }); + + session.phase = Session_state::CLOSED; + } + break; + + case Session_state::INVALID_ARGS: + case Session_state::AVAILABLE: + case Session_state::CAP_HANDED_OUT: + case Session_state::CLOSED: + break; + } } }; @@ -215,31 +239,88 @@ class Genode::Local_service : public Service */ class Genode::Parent_service : public Service { + private: + + /* + * \deprecated + */ + Env &_env_deprecated(); + Env &_env; + public: - Parent_service(const char *name) : Service(name) { } + /** + * Constructor + */ + Parent_service(Env &env, Service::Name const &name) + : Service(name, Ram_session_capability()), _env(env) { } - Session_capability session(const char *args, Affinity const &affinity) override + /** + * Constructor + * + * \deprecated + */ + Parent_service(Service::Name const &name) + : Service(name, Ram_session_capability()), _env(_env_deprecated()) { } + + void initiate_request(Session_state &session) override { - try { return env()->parent()->session(name(), args, affinity); } - catch (Parent::Unavailable) { - warning("parent has no service \"", name(), "\""); - throw Unavailable(); + switch (session.phase) { + + case Session_state::CREATE_REQUESTED: + + session.id_at_parent.construct(session.parent_client, + _env.id_space()); + try { + session.cap = _env.session(name().string(), + session.id_at_parent->id(), + session.args().string(), + session.affinity()); + + session.phase = Session_state::AVAILABLE; + } + catch (Parent::Quota_exceeded) { + session.id_at_parent.destruct(); + session.phase = Session_state::INVALID_ARGS; } + + catch (Parent::Service_denied) { + session.id_at_parent.destruct(); + session.phase = Session_state::INVALID_ARGS; } + + break; + + case Session_state::UPGRADE_REQUESTED: + { + String<64> const args("ram_quota=", session.ram_upgrade); + + if (!session.id_at_parent.constructed()) + error("invalid parent-session state: ", session); + + try { + _env.upgrade(session.id_at_parent->id(), args.string()); } + catch (Parent::Quota_exceeded) { + warning("quota exceeded while upgrading parent session"); } + + session.confirm_ram_upgrade(); + session.phase = Session_state::CAP_HANDED_OUT; + } + break; + + case Session_state::CLOSE_REQUESTED: + + if (session.id_at_parent.constructed()) + _env.close(session.id_at_parent->id()); + + session.id_at_parent.destruct(); + session.phase = Session_state::CLOSED; + break; + + case Session_state::INVALID_ARGS: + case Session_state::AVAILABLE: + case Session_state::CAP_HANDED_OUT: + case Session_state::CLOSED: + break; } - catch (Parent::Quota_exceeded) { throw Quota_exceeded(); } - catch (Genode::Ipc_error) { throw Unavailable(); } - } - - void upgrade(Session_capability session, const char *args) override - { - try { env()->parent()->upgrade(session, args); } - catch (Genode::Ipc_error) { throw Unavailable(); } - } - - void close(Session_capability session) override - { - try { env()->parent()->close(session); } - catch (Genode::Ipc_error) { throw Blocking_canceled(); } } }; @@ -249,205 +330,66 @@ class Genode::Parent_service : public Service */ class Genode::Child_service : public Service { + public: + + struct Wakeup { virtual void wakeup_child_service() = 0; }; + private: - Root_capability _root_cap; - Root_client _root; - Server *_server; + Id_space &_server_id_space; + + Session_state::Factory &_server_factory; + + Wakeup &_wakeup; + + protected: + + /* + * In contrast to local services and parent services, session-state + * objects for child services are owned by the server. This enables + * the server to asynchronouly respond to close requests when the + * client is already gone. + */ + Factory &_factory(Factory &) override { return _server_factory; } public: + /** * Constructor * - * \param name name of service - * \param root capability to root interface - * \param server server process providing the service - */ - Child_service(const char *name, - Root_capability root, - Server *server) - : Service(name), _root_cap(root), _root(root), _server(server) { } - - Server *server() const override { return _server; } - - Session_capability session(const char *args, Affinity const &affinity) override - { - if (!_root_cap.valid()) - throw Unavailable(); - - try { return _root.session(args, affinity); } - catch (Root::Invalid_args) { throw Invalid_args(); } - catch (Root::Unavailable) { throw Unavailable(); } - catch (Root::Quota_exceeded) { throw Quota_exceeded(); } - catch (Genode::Ipc_error) { throw Unavailable(); } - } - - void upgrade(Session_capability sc, const char *args) override - { - if (!_root_cap.valid()) - throw Unavailable(); - - try { _root.upgrade(sc, args); } - catch (Root::Invalid_args) { throw Invalid_args(); } - catch (Root::Unavailable) { throw Unavailable(); } - catch (Root::Quota_exceeded) { throw Quota_exceeded(); } - catch (Genode::Ipc_error) { throw Unavailable(); } - } - - void close(Session_capability sc) override - { - try { _root.close(sc); } - catch (Genode::Ipc_error) { throw Blocking_canceled(); } - } -}; - - -/** - * Container for holding service representations - */ -class Genode::Service_registry -{ - protected: - - Lock _service_wait_queue_lock; - List _service_wait_queue; - List _services; - - public: - - /** - * Probe for service with specified name + * \param factory server-side session-state factory + * \param name name of service + * \param ram recipient of session quota + * \param wakeup callback to be notified on the arrival of new + * session requests * - * \param name service name - * \param server server providing the service, - * default (0) for any server */ - Service *find(const char *name, Server *server = 0) + Child_service(Id_space &server_id_space, + Session_state::Factory &factory, + Service::Name const &name, + Ram_session_capability ram, + Wakeup &wakeup) + : + Service(name, ram), + _server_id_space(server_id_space), + _server_factory(factory), _wakeup(wakeup) + { } + + void initiate_request(Session_state &session) override { - if (!name) return 0; + if (!session.id_at_server.constructed()) + session.id_at_server.construct(session, _server_id_space); - Lock::Guard lock_guard(_service_wait_queue_lock); - - for (Service *s = _services.first(); s; s = s->next()) - if (strcmp(s->name(), name) == 0 - && (!server || s->server() == server)) return s; - - return 0; + session.async_client_notify = true; } - /** - * Check if service name is ambiguous - * - * \return true if the same service is provided multiple - * times - */ - bool is_ambiguous(const char *name) + bool has_id_space(Id_space const &id_space) const { - Lock::Guard lock_guard(_service_wait_queue_lock); - - /* count number of services with the specified name */ - unsigned cnt = 0; - for (Service *s = _services.first(); s; s = s->next()) - cnt += (strcmp(s->name(), name) == 0); - return cnt > 1; + return &_server_id_space == &id_space; } - /** - * Return first service provided by specified server - */ - Service *find_by_server(Server *server) - { - Lock::Guard lock_guard(_service_wait_queue_lock); - - for (Service *s = _services.first(); s; s = s->next()) - if (s->server() == server) - return s; - - return 0; - } - - /** - * Wait for service - * - * This method is called by the clients's thread when requesting a - * session creation. It blocks if the requested service is not - * available. - * - * \return service structure that matches the request or - * 0 if the waiting was canceled. - */ - Service *wait_for_service(const char *name, Client *client, const char *client_name) - { - Service *service; - - client->apply_for(name); - - _service_wait_queue_lock.lock(); - _service_wait_queue.insert(client); - _service_wait_queue_lock.unlock(); - - do { - service = find(name); - - /* - * The service that we are seeking is not available today. - * Lets sleep a night over it. - */ - if (!service) { - log(client_name, ": service ", name, " not yet available - sleeping"); - - try { - client->sleep(); - log(client_name, ": service ", name, " got available"); - } catch (Blocking_canceled) { - log(client_name, ": cancel waiting for service"); - break; - } - } - - } while (!service); - - /* we got what we needed, stop applying */ - _service_wait_queue_lock.lock(); - _service_wait_queue.remove(client); - _service_wait_queue_lock.unlock(); - - client->apply_for(0); - - return service; - } - - /** - * Register service - * - * This method is called by the server's thread. - */ - void insert(Service *service) - { - /* make new service known */ - _services.insert(service); - - /* wake up applicants waiting for the service */ - Lock::Guard lock_guard(_service_wait_queue_lock); - for (Client *c = _service_wait_queue.first(); c; c = c->next()) - if (strcmp(service->name(), c->apply_for()) == 0) - c->wakeup(); - } - - /** - * Unregister service - */ - void remove(Service *service) { _services.remove(service); } - - /** - * Unregister all services - */ - void remove_all() - { - while (_services.first()) - remove(_services.first()); - } + void wakeup() override { _wakeup.wakeup_child_service(); } }; #endif /* _INCLUDE__BASE__SERVICE_H_ */ diff --git a/repos/base/include/base/session_state.h b/repos/base/include/base/session_state.h new file mode 100644 index 000000000..21241758d --- /dev/null +++ b/repos/base/include/base/session_state.h @@ -0,0 +1,245 @@ +/* + * \brief Representation of a session request + * \author Norman Feske + * \date 2016-10-10 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__BASE__SESSION_STATE_H_ +#define _INCLUDE__BASE__SESSION_STATE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Genode { + + class Session_state; + class Service; +} + + +class Genode::Session_state : public Parent::Client, public Parent::Server, + Noncopyable +{ + public: + + class Factory; + + typedef String<32> Name; + typedef String<256> Args; + + struct Ready_callback + { + virtual void session_ready(Session_state &) = 0; + }; + + struct Closed_callback + { + virtual void session_closed(Session_state &) = 0; + }; + + private: + + Service &_service; + + /** + * Total of quota associated with this session + */ + size_t _donated_ram_quota = 0; + + Factory *_factory = nullptr; + + Volatile_object::Element> _id_at_client; + + Args _args; + Affinity _affinity; + + public: + + Lazy_volatile_object::Element> id_at_server; + + /* ID for session requests towards the parent */ + Lazy_volatile_object::Element> id_at_parent; + + Parent::Client parent_client; + + enum Phase { CREATE_REQUESTED, + INVALID_ARGS, + AVAILABLE, + CAP_HANDED_OUT, + UPGRADE_REQUESTED, + CLOSE_REQUESTED, + CLOSED }; + + /** + * If set, the server reponds asynchronously to the session request. + * The client waits for a notification that is delivered as soon as + * the server delivers the session capability. + */ + bool async_client_notify = false; + + Phase phase = CREATE_REQUESTED; + + Ready_callback *ready_callback = nullptr; + Closed_callback *closed_callback = nullptr; + + /* + * Pointer to session interface for sessions that are implemented + * locally. + */ + Session *local_ptr = nullptr; + + Session_capability cap; + + size_t ram_upgrade = 0; + + void print(Output &out) const; + + /** + * Constructor + * + * \param service interface that was used to create the session + * \param client_id_space ID space for client-side session IDs + * \param client_id session ID picked by the client + * \param args session arguments + * + * \throw Id_space::Conflicting_id + */ + Session_state(Service &service, + Id_space &client_id_space, + Parent::Client::Id client_id, + Args const &args, + Affinity const &affinity); + + ~Session_state() + { + if (id_at_parent.is_constructed()) + error("dangling session in parent-side ID space: ", *this); + } + + Service &service() { return _service; } + + /** + * Extend amount of ram attached to the session + */ + void confirm_ram_upgrade() + { + ram_upgrade = 0; + } + + void increase_donated_quota(size_t upgrade) + { + _donated_ram_quota += upgrade; + ram_upgrade = upgrade; + } + + Parent::Client::Id id_at_client() const + { + return _id_at_client->id(); + } + + void discard_id_at_client() + { + _id_at_client.destruct(); + } + + Args const &args() const { return _args; } + + Affinity const &affinity() const { return _affinity; } + + void generate_session_request(Xml_generator &xml) const; + + size_t donated_ram_quota() const { return _donated_ram_quota; } + + bool alive() const + { + switch (phase) { + + case CREATE_REQUESTED: + case INVALID_ARGS: + case CLOSED: + return false; + + case AVAILABLE: + case CAP_HANDED_OUT: + case UPGRADE_REQUESTED: + case CLOSE_REQUESTED: + return true; + } + return false; + } + + /** + * Assign owner + * + * This function is called if the session-state object is created by + * 'Factory'. For statically created session objects, the '_factory' is + * a nullptr. The owner can be defined only once. + */ + void owner(Factory &factory) { if (!_factory) _factory = &factory; } + + /** + * Destroy session-state object + * + * This function has no effect for sessions not created via a 'Factory'. + */ + void destroy(); +}; + + +class Genode::Session_state::Factory : Noncopyable +{ + private: + + Allocator &_md_alloc; + + public: + + /** + * Constructor + * + * \param md_alloc meta-data allocator used for allocating + * 'Session_state' objects + */ + Factory(Allocator &md_alloc) : _md_alloc(md_alloc) { } + + /** + * Create a new session-state object + * + * The 'args' are passed the 'Session_state' constructor. + * + * \throw Allocator::Out_of_memory + */ + template + Session_state &create(ARGS &&... args) + { + Session_state &session = *new (_md_alloc) Session_state(args...); + session.owner(*this); + return session; + } + + private: + + /* + * Allow only 'Session_state::destroy' to call 'Factory::_destroy'. + * This way, we make sure that the 'Session_state' is destroyed with + * the same factory that was used for creating it. + */ + friend class Session_state; + + void _destroy(Session_state &session) { Genode::destroy(_md_alloc, &session); } +}; + + +#endif /* _INCLUDE__BASE__SESSION_STATE_H_ */ diff --git a/repos/base/include/cpu_session/cpu_session.h b/repos/base/include/cpu_session/cpu_session.h index 3c0f157cd..35a2e2a88 100644 --- a/repos/base/include/cpu_session/cpu_session.h +++ b/repos/base/include/cpu_session/cpu_session.h @@ -23,11 +23,20 @@ #include #include -namespace Genode { struct Cpu_session; } +namespace Genode { + + struct Cpu_session; + struct Cpu_session_client; +} struct Genode::Cpu_session : Session { + static const char *service_name() { return "CPU"; } + + typedef Cpu_session_client Client; + + /********************* ** Exception types ** *********************/ @@ -36,7 +45,6 @@ struct Genode::Cpu_session : Session class Quota_exceeded : public Thread_creation_failed { }; class Out_of_metadata : public Exception { }; - static const char *service_name() { return "CPU"; } enum { THREAD_NAME_LEN = 32 }; enum { PRIORITY_LIMIT = 1 << 16 }; diff --git a/repos/base/include/deprecated/env.h b/repos/base/include/deprecated/env.h index d6fd7ee4c..aa6d08418 100644 --- a/repos/base/include/deprecated/env.h +++ b/repos/base/include/deprecated/env.h @@ -119,7 +119,6 @@ struct Genode::Env_deprecated * \noapi */ virtual void reinit_main_thread(Capability &stack_area_rm) = 0; - }; #endif /* _INCLUDE__DEPRECATED__ENV_H_ */ diff --git a/repos/base/include/log_session/connection.h b/repos/base/include/log_session/connection.h index 0d05c3ec0..905fd8b3a 100644 --- a/repos/base/include/log_session/connection.h +++ b/repos/base/include/log_session/connection.h @@ -23,14 +23,16 @@ namespace Genode { struct Log_connection; } struct Genode::Log_connection : Connection, Log_session_client { + enum { RAM_QUOTA = 8*1024UL }; + /** * Constructor */ Log_connection(Env &env, Session_label label = Session_label()) : Connection(env, session(env.parent(), - "ram_quota=8K, label=\"%s\"", - label.string())), + "ram_quota=%ld, label=\"%s\"", + RAM_QUOTA, label.string())), Log_session_client(cap()) { } @@ -43,8 +45,8 @@ struct Genode::Log_connection : Connection, Log_session_client */ Log_connection(Session_label label = Session_label()) : - Connection(session("ram_quota=8K, label=\"%s\"", - label.string())), + Connection(session("ram_quota=%ld, label=\"%s\"", + RAM_QUOTA, label.string())), Log_session_client(cap()) { } }; diff --git a/repos/base/include/log_session/log_session.h b/repos/base/include/log_session/log_session.h index 0a57d69e4..9d38eb929 100644 --- a/repos/base/include/log_session/log_session.h +++ b/repos/base/include/log_session/log_session.h @@ -19,13 +19,19 @@ #include #include -namespace Genode { struct Log_session; } +namespace Genode { + + struct Log_session; + struct Log_session_client; +} struct Genode::Log_session : Session { static const char *service_name() { return "LOG"; } + typedef Log_session_client Client; + virtual ~Log_session() { } /* the lowest platform-specific maximum IPC payload size (OKL4) */ diff --git a/repos/base/include/parent/client.h b/repos/base/include/parent/client.h index d75ec89f4..825548994 100644 --- a/repos/base/include/parent/client.h +++ b/repos/base/include/parent/client.h @@ -27,18 +27,32 @@ struct Genode::Parent_client : Rpc_client void exit(int exit_value) override { call(exit_value); } - void announce(Service_name const &service, Root_capability root) override { - call(service, root); } + void announce(Service_name const &service) override { + call(service); } - Session_capability session(Service_name const &service, + void session_sigh(Signal_context_capability sigh) override { + call(sigh); } + + Session_capability session(Client::Id id, + Service_name const &service, Session_args const &args, Affinity const &affinity) override { - return call(service, args, affinity); } + return call(id, service, args, affinity); } - void upgrade(Session_capability to_session, Upgrade_args const &args) override { - call(to_session, args); } + Session_capability session_cap(Client::Id id) override { + return call(id); } - void close(Session_capability session) override { call(session); } + Upgrade_result upgrade(Client::Id to_session, Upgrade_args const &args) override { + return call(to_session, args); } + + Close_result close(Client::Id id) override { return call(id); } + + void session_response(Id_space::Id id, Session_response response) override { + call(id, response); } + + void deliver_session_cap(Id_space::Id id, + Session_capability cap) override { + call(id, cap); } Thread_capability main_thread_cap() const override { return call(); } diff --git a/repos/base/include/parent/parent.h b/repos/base/include/parent/parent.h index a15b7d426..76c20c2d5 100644 --- a/repos/base/include/parent/parent.h +++ b/repos/base/include/parent/parent.h @@ -18,10 +18,15 @@ #include #include #include +#include #include #include -namespace Genode { class Parent; } +namespace Genode { + + class Session_state; + class Parent; +} class Genode::Parent @@ -61,13 +66,35 @@ class Genode::Parent typedef Rpc_in_buffer<160> Session_args; typedef Rpc_in_buffer<160> Upgrade_args; + struct Client { typedef Id_space::Id Id; }; + struct Server { typedef Id_space::Id Id; }; + + /** + * Predefined session IDs corresponding to the environment sessions + * created by the parent at the component-construction time + */ + struct Env + { + static Client::Id ram() { return { 1 }; } + static Client::Id cpu() { return { 2 }; } + static Client::Id pd() { return { 3 }; } + static Client::Id log() { return { 4 }; } + static Client::Id binary() { return { 5 }; } + static Client::Id linker() { return { 6 }; } + + /** + * True if session ID refers to an environment session + */ + static bool session_id(Client::Id id) { + return id.value >= 1 && id.value <= 6; } + }; + /** * Use 'String' instead of 'Rpc_in_buffer' because 'Resource_args' * is used as both in and out parameter. */ typedef String<160> Resource_args; - virtual ~Parent() { } /** @@ -78,9 +105,20 @@ class Genode::Parent /** * Announce service to the parent */ - virtual void announce(Service_name const &service_name, - Root_capability service_root) = 0; + virtual void announce(Service_name const &service_name) = 0; + /** + * Emulation of the original synchronous root interface + * + * This function transparently spawns a proxy "root" entrypoint that + * dispatches asynchronous session-management operations (as issued + * by the parent) to the local root interfaces via component-local + * RPC calls. + * + * The function solely exists for API compatibility. + */ + static void announce(Service_name const &service_name, + Root_capability service_root); /** * Announce service to the parent @@ -109,9 +147,15 @@ class Genode::Parent (Meta::Bool_to_type::VALUE> *)0); } + /** + * Register signal handler for session notifications + */ + virtual void session_sigh(Signal_context_capability) = 0; + /** * Create session to a service * + * \param id client-side ID of new session * \param service_name name of the requested interface * \param args session constructor arguments * \param affinity preferred CPU affinity for the session @@ -119,60 +163,73 @@ class Genode::Parent * \throw Service_denied parent denies session request * \throw Quota_exceeded our own quota does not suffice for * the creation of the new session + * + * \return session capability of the new session is immediately + * available, or an invalid capability + * + * If the returned capability is invalid, the request is pending at the + * server. The parent delivers a signal to the handler as registered + * via 'session_sigh' once the server responded to the request. Now the + * session capability can be picked up by calling 'session_cap'. + * * \throw Unavailable - * - * \return untyped capability to new session - * - * The use of this method is discouraged. Please use the type safe - * 'session()' template instead. */ - virtual Session_capability session(Service_name const &service_name, + virtual Session_capability session(Client::Id id, + Service_name const &service_name, Session_args const &args, Affinity const &affinity = Affinity()) = 0; /** - * Create session to a service + * Request session capability * - * \param SESSION_TYPE session interface type - * \param args session constructor arguments - * \param affinity preferred CPU affinity for the session + * \throw Service_denied * - * \throw Service_denied parent denies session request - * \throw Quota_exceeded our own quota does not suffice for - * the creation of the new session - * \throw Unavailable - * - * \return capability to new session + * In the exception case, the parent implicitly closes the session. */ - template - Capability session(Session_args const &args, - Affinity const &affinity = Affinity()) - { - Session_capability cap = session(SESSION_TYPE::service_name(), - args, affinity); - return reinterpret_cap_cast(cap); - } + virtual Session_capability session_cap(Client::Id id) = 0; + + enum Upgrade_result { UPGRADE_DONE, UPGRADE_PENDING }; /** * Transfer our quota to the server that provides the specified session * - * \param to_session recipient session + * \param id ID of recipient session * \param args description of the amount of quota to transfer * * \throw Quota_exceeded quota could not be transferred * * The 'args' argument has the same principle format as the 'args' * argument of the 'session' operation. - * The error case indicates that there is not enough unused quota on - * the source side. */ - virtual void upgrade(Session_capability to_session, - Upgrade_args const &args) = 0; + virtual Upgrade_result upgrade(Client::Id to_session, + Upgrade_args const &args) = 0; + + enum Close_result { CLOSE_DONE, CLOSE_PENDING }; /** * Close session */ - virtual void close(Session_capability session) = 0; + virtual Close_result close(Client::Id) = 0; + + /* + * Interface for providing services + */ + + enum Session_response { SESSION_OK, SESSION_CLOSED, INVALID_ARGS }; + + /** + * Set state of a session provided by the child service + */ + virtual void session_response(Server::Id, Session_response) = 0; + + /** + * Deliver capability for a new session provided by the child service + */ + virtual void deliver_session_cap(Server::Id, Session_capability) = 0; + + /* + * Dynamic resource balancing + */ /** * Provide thread_cap of main thread @@ -233,14 +290,23 @@ class Genode::Parent GENODE_RPC(Rpc_exit, void, exit, int); GENODE_RPC(Rpc_announce, void, announce, - Service_name const &, Root_capability); + 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, Quota_exceeded, Unavailable), - Service_name const &, Session_args const &, Affinity const &); - GENODE_RPC_THROW(Rpc_upgrade, void, upgrade, + 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, Quota_exceeded, Unavailable), + Client::Id); + GENODE_RPC_THROW(Rpc_upgrade, Upgrade_result, upgrade, GENODE_TYPE_LIST(Quota_exceeded), - Session_capability, Upgrade_args const &); - GENODE_RPC(Rpc_close, void, close, Session_capability); + Client::Id, Upgrade_args const &); + GENODE_RPC(Rpc_close, Close_result, close, Client::Id); + GENODE_RPC(Rpc_session_response, void, session_response, + Server::Id, Session_response); + GENODE_RPC(Rpc_deliver_session_cap, void, deliver_session_cap, + Server::Id, Session_capability); GENODE_RPC(Rpc_main_thread, Thread_capability, main_thread_cap); GENODE_RPC(Rpc_resource_avail_sigh, void, resource_avail_sigh, Signal_context_capability); @@ -250,10 +316,12 @@ class Genode::Parent GENODE_RPC(Rpc_yield_request, Resource_args, yield_request); GENODE_RPC(Rpc_yield_response, void, yield_response); - GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session, Rpc_upgrade, - Rpc_close, Rpc_main_thread, Rpc_resource_avail_sigh, - Rpc_resource_request, Rpc_yield_sigh, Rpc_yield_request, - Rpc_yield_response); + GENODE_RPC_INTERFACE(Rpc_exit, Rpc_announce, Rpc_session_sigh, + Rpc_session, Rpc_session_cap, Rpc_upgrade, + Rpc_close, Rpc_session_response, Rpc_main_thread, + Rpc_deliver_session_cap, Rpc_resource_avail_sigh, + Rpc_resource_request, Rpc_yield_sigh, + Rpc_yield_request, Rpc_yield_response); }; diff --git a/repos/base/include/pd_session/pd_session.h b/repos/base/include/pd_session/pd_session.h index b3053382c..67d51dde6 100644 --- a/repos/base/include/pd_session/pd_session.h +++ b/repos/base/include/pd_session/pd_session.h @@ -25,6 +25,7 @@ namespace Genode { struct Pd_session; + struct Pd_session_client; struct Parent; struct Signal_context; } @@ -34,6 +35,8 @@ struct Genode::Pd_session : Session { static const char *service_name() { return "PD"; } + typedef Pd_session_client Client; + virtual ~Pd_session() { } /** diff --git a/repos/base/include/ram_session/ram_session.h b/repos/base/include/ram_session/ram_session.h index 5cc41bd63..e1a79e08a 100644 --- a/repos/base/include/ram_session/ram_session.h +++ b/repos/base/include/ram_session/ram_session.h @@ -27,6 +27,7 @@ namespace Genode { struct Ram_dataspace; typedef Capability Ram_dataspace_capability; + struct Ram_session_client; struct Ram_session; } @@ -41,6 +42,8 @@ struct Genode::Ram_session : Session { static const char *service_name() { return "RAM"; } + typedef Ram_session_client Client; + /********************* ** Exception types ** diff --git a/repos/base/include/rom_session/connection.h b/repos/base/include/rom_session/connection.h index 6d962a85e..9a5f29862 100644 --- a/repos/base/include/rom_session/connection.h +++ b/repos/base/include/rom_session/connection.h @@ -28,11 +28,13 @@ class Genode::Rom_connection : public Connection, class Rom_connection_failed : public Parent::Exception { }; + enum { RAM_QUOTA = 4096UL }; + private: Rom_session_capability _session(Parent &parent, char const *label) { - return session("ram_quota=4K, label=\"%s\"", label); + return session("ram_quota=%ld, label=\"%s\"", RAM_QUOTA, label); } public: diff --git a/repos/base/include/rom_session/rom_session.h b/repos/base/include/rom_session/rom_session.h index a218703b7..da6256cc4 100644 --- a/repos/base/include/rom_session/rom_session.h +++ b/repos/base/include/rom_session/rom_session.h @@ -25,6 +25,7 @@ namespace Genode { struct Rom_dataspace; struct Rom_session; + struct Rom_session_client; typedef Capability Rom_dataspace_capability; } @@ -37,6 +38,8 @@ struct Genode::Rom_session : Session { static const char *service_name() { return "ROM"; } + typedef Rom_session_client Client; + virtual ~Rom_session() { } /** diff --git a/repos/base/include/root/component.h b/repos/base/include/root/component.h index 9eda01444..7bf58d3fc 100644 --- a/repos/base/include/root/component.h +++ b/repos/base/include/root/component.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -95,6 +96,7 @@ struct Genode::Multiple_clients */ template class Genode::Root_component : public Rpc_object >, + public Local_service::Factory, private POLICY { private: @@ -113,6 +115,53 @@ class Genode::Root_component : public Rpc_object >, */ Allocator *_md_alloc; + /* + * Used by both the legacy 'Root::session' and the new 'Factory::create' + */ + SESSION_TYPE &_create(Session_state::Args const &args, Affinity affinity) + { + POLICY::aquire(args.string()); + + /* + * We need to decrease 'ram_quota' by + * the size of the session object. + */ + size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE)); + + if (needed > ram_quota) { + error("insufficient ram quota, provided=", ram_quota, + ", required=", needed); + throw Root::Quota_exceeded(); + } + + size_t const remaining_ram_quota = ram_quota - needed; + + /* + * Deduce ram quota needed for allocating the session object from the + * donated ram quota. + * + * XXX the size of the 'adjusted_args' buffer should dependent + * on the message-buffer size and stack size. + */ + enum { MAX_ARGS_LEN = 256 }; + char adjusted_args[MAX_ARGS_LEN]; + strncpy(adjusted_args, args.string(), sizeof(adjusted_args)); + char ram_quota_buf[64]; + snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%lu", + remaining_ram_quota); + Arg_string::set_arg(adjusted_args, sizeof(adjusted_args), + "ram_quota", ram_quota_buf); + + SESSION_TYPE *s = 0; + try { s = _create_session(adjusted_args, affinity); } + catch (Allocator::Out_of_memory) { throw Root::Unavailable(); } + + _ep->manage(s); + + return *s; + } + protected: /** @@ -169,7 +218,7 @@ class Genode::Root_component : public Rpc_object >, virtual void _upgrade_session(SESSION_TYPE *, const char *) { } virtual void _destroy_session(SESSION_TYPE *session) { - destroy(_md_alloc, session); } + Genode::destroy(_md_alloc, session); } /** * Return allocator to allocate server object in '_create_session()' @@ -208,6 +257,31 @@ class Genode::Root_component : public Rpc_object >, { } + /************************************** + ** Local_service::Factory interface ** + **************************************/ + + SESSION_TYPE &create(Session_state::Args const &args, + Affinity affinity) override + { + try { + return _create(args, affinity); } + catch (...) { + throw typename Local_service::Factory::Denied(); } + } + + void upgrade(SESSION_TYPE &session, + Session_state::Args const &args) override + { + _upgrade_session(&session, args.string()); + } + + void destroy(SESSION_TYPE &session) override + { + close(session.cap()); + } + + /******************** ** Root interface ** ********************/ @@ -216,45 +290,8 @@ class Genode::Root_component : public Rpc_object >, Affinity const &affinity) override { if (!args.valid_string()) throw Root::Invalid_args(); - - POLICY::aquire(args.string()); - - /* - * We need to decrease 'ram_quota' by - * the size of the session object. - */ - size_t ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); - size_t needed = sizeof(SESSION_TYPE) + md_alloc()->overhead(sizeof(SESSION_TYPE)); - - if (needed > ram_quota) { - error("Insufficient ram quota, provided=", ram_quota, - ", required=", needed); - throw Root::Quota_exceeded(); - } - - size_t const remaining_ram_quota = ram_quota - needed; - - /* - * Deduce ram quota needed for allocating the session object from the - * donated ram quota. - * - * XXX the size of the 'adjusted_args' buffer should dependent - * on the message-buffer size and stack size. - */ - enum { MAX_ARGS_LEN = 256 }; - char adjusted_args[MAX_ARGS_LEN]; - strncpy(adjusted_args, args.string(), sizeof(adjusted_args)); - char ram_quota_buf[64]; - snprintf(ram_quota_buf, sizeof(ram_quota_buf), "%lu", - remaining_ram_quota); - Arg_string::set_arg(adjusted_args, sizeof(adjusted_args), - "ram_quota", ram_quota_buf); - - SESSION_TYPE *s = 0; - try { s = _create_session(adjusted_args, affinity); } - catch (Allocator::Out_of_memory) { throw Root::Unavailable(); } - - return _ep->manage(s); + SESSION_TYPE &session = _create(args.string(), affinity); + return session.cap(); } void upgrade(Session_capability session, Root::Upgrade_args const &args) override diff --git a/repos/base/include/session/session.h b/repos/base/include/session/session.h index dfbb2e6f4..342f68a03 100644 --- a/repos/base/include/session/session.h +++ b/repos/base/include/session/session.h @@ -21,13 +21,13 @@ */ #include -namespace Genode { class Session; } +namespace Genode { struct Session; } /** * Base class of session interfaces */ -class Genode::Session +struct Genode::Session { /* * Each session interface must implement the class function 'service_name' @@ -35,6 +35,8 @@ class Genode::Session * This function returns the name of the service provided via the session * interface. */ + + virtual ~Session() { } }; #endif /* _INCLUDE__SESSION__SESSION_H_ */ diff --git a/repos/base/lib/mk/base-common.inc b/repos/base/lib/mk/base-common.inc index b74b9fec4..694b4bd61 100644 --- a/repos/base/lib/mk/base-common.inc +++ b/repos/base/lib/mk/base-common.inc @@ -15,6 +15,7 @@ SRC_CC += console.cc SRC_CC += output.cc SRC_CC += child.cc SRC_CC += child_process.cc +SRC_CC += session_state.cc SRC_CC += elf_binary.cc SRC_CC += ipc.cc SRC_CC += lock.cc @@ -29,6 +30,8 @@ SRC_CC += region_map_client.cc SRC_CC += rm_session_client.cc SRC_CC += stack_allocator.cc SRC_CC += trace.cc +SRC_CC += root_proxy.cc +SRC_CC += env_session_id_space.cc INC_DIR += $(REP_DIR)/src/include $(BASE_DIR)/src/include diff --git a/repos/base/src/core/cpu_session_component.cc b/repos/base/src/core/cpu_session_component.cc index 823d3159d..8aa6111d7 100644 --- a/repos/base/src/core/cpu_session_component.cc +++ b/repos/base/src/core/cpu_session_component.cc @@ -56,6 +56,7 @@ Thread_capability Cpu_session_component::create_thread(Capability pd error("create_thread: invalid PD argument"); throw Thread_creation_failed(); } + Lock::Guard slab_lock_guard(_thread_alloc_lock); thread = new (&_thread_alloc) Cpu_thread_component( diff --git a/repos/base/src/core/include/core_env.h b/repos/base/src/core/include/core_env.h index 6fc7075ec..dd18b2604 100644 --- a/repos/base/src/core/include/core_env.h +++ b/repos/base/src/core/include/core_env.h @@ -116,8 +116,6 @@ namespace Genode { typedef Synchronized_ram_session Core_ram_session; - Core_parent _core_parent; - enum { ENTRYPOINT_STACK_SIZE = 2048 * sizeof(Genode::addr_t) }; /* @@ -144,6 +142,10 @@ namespace Genode { Heap _heap; + Registry _services; + + Core_parent _core_parent { _heap, _services }; + public: /** @@ -202,6 +204,8 @@ namespace Genode { void reinit(Capability::Raw) override { } void reinit_main_thread(Capability &) override { } + + Registry &services() { return _services; } }; diff --git a/repos/base/src/core/include/core_parent.h b/repos/base/src/core/include/core_parent.h index c0239a846..e01d6c872 100644 --- a/repos/base/src/core/include/core_parent.h +++ b/repos/base/src/core/include/core_parent.h @@ -16,38 +16,83 @@ #define _CORE__INCLUDE__CORE_PARENT_H_ #include +#include +#include -namespace Genode { struct Core_parent; } +namespace Genode { + + template struct Core_service; + struct Core_parent; +} + + +template +struct Genode::Core_service : Local_service, Registry::Element +{ + Core_service(Registry ®istry, + typename Local_service::Factory &factory) + : + Local_service(factory), + Registry::Element(registry, *this) + { } +}; /** - * In fact, Core has _no_ parent. But most of our libraries could work - * seamlessly inside Core too, if it had one. Core_parent fills this gap. + * Core has no parent. But most of Genode's library code could work seamlessly + * inside core if it had one. Core_parent fills this gap. */ -struct Genode::Core_parent : Parent +class Genode::Core_parent : public Parent { - void exit(int); + private: - void announce(Service_name const &, Root_capability) { } + Id_space _id_space; + Allocator &_alloc; + Registry &_services; - Session_capability session(Service_name const &, Session_args const &, - Affinity const &); + public: - void upgrade(Session_capability, Upgrade_args const &) { throw Quota_exceeded(); } + /** + * Constructor + * + * \alloc allocator to be used for allocating core-local + * 'Session_state' objects + */ + Core_parent(Allocator &alloc, Registry &services) + : _alloc(alloc), _services(services) { } - void close(Session_capability) { } + void exit(int) override; - Thread_capability main_thread_cap() const { return Thread_capability(); } + void announce(Service_name const &) override { } - void resource_avail_sigh(Signal_context_capability) { } + void session_sigh(Signal_context_capability) override { } - void resource_request(Resource_args const &) { } + Session_capability session(Client::Id, Service_name const &, Session_args const &, + Affinity const &) override; - void yield_sigh(Signal_context_capability) { } + Session_capability session_cap(Client::Id) override { return Session_capability(); } - Resource_args yield_request() { return Resource_args(); } + Upgrade_result upgrade(Client::Id, Upgrade_args const &) override { + throw Quota_exceeded(); } - void yield_response() { } + Close_result close(Client::Id) override { return CLOSE_DONE; } + + void session_response(Server::Id, Session_response) override { } + + void deliver_session_cap(Server::Id, + Session_capability) override { } + + Thread_capability main_thread_cap() const override { return Thread_capability(); } + + void resource_avail_sigh(Signal_context_capability) override { } + + void resource_request(Resource_args const &) override { } + + void yield_sigh(Signal_context_capability) override { } + + Resource_args yield_request() override { return Resource_args(); } + + void yield_response() override { } }; #endif /* _CORE__INCLUDE__CORE_PARENT_H_ */ diff --git a/repos/base/src/core/include/platform_services.h b/repos/base/src/core/include/platform_services.h index d8fa53aee..3ca7f637e 100644 --- a/repos/base/src/core/include/platform_services.h +++ b/repos/base/src/core/include/platform_services.h @@ -14,11 +14,12 @@ #ifndef _CORE__INCLUDE__PLATFORM_SERVICES_H_ #define _CORE__INCLUDE__PLATFORM_SERVICES_H_ +#include + namespace Genode { class Rpc_entrypoint; class Sliced_heap; - class Service_registry; /** @@ -31,7 +32,7 @@ namespace Genode { */ void platform_add_local_services(Rpc_entrypoint *ep, Sliced_heap *md, - Service_registry *reg); + Registry *reg); } #endif /* _CORE__INCLUDE__PLATFORM_SERVICES_H_ */ diff --git a/repos/base/src/core/main.cc b/repos/base/src/core/main.cc index 840ece989..adbf96c70 100644 --- a/repos/base/src/core/main.cc +++ b/repos/base/src/core/main.cc @@ -42,10 +42,6 @@ using namespace Genode; -/* pool of provided core services */ -static Service_registry local_services; - - /*************************************** ** Core environment/platform support ** ***************************************/ @@ -85,17 +81,30 @@ Platform_generic *Genode::platform() { return platform_specific(); } ** Core parent support ** *************************/ -Session_capability Core_parent::session(Parent::Service_name const &name, +Session_capability Core_parent::session(Parent::Client::Id id, + Parent::Service_name const &name, Parent::Session_args const &args, Affinity const &affinity) { - Service *service = local_services.find(name.string()); + Session_capability cap; - if (service) - return service->session(args.string(), affinity); + _services.for_each([&] (Service &service) { - warning("service_name=\"", name.string(), "\" args=\"", args.string(), "\" not handled"); - return Session_capability(); + if ((service.name() != name.string()) || cap.valid()) + return; + + Session_state &session = *new (_alloc) + Session_state(service, _id_space, id, args.string(), affinity); + + service.initiate_request(session); + + cap = session.cap; + }); + + if (!cap.valid()) + error("unexpected core-parent ", name.string(), " session request"); + + return cap; } @@ -113,21 +122,15 @@ class Core_child : public Child_policy Rpc_entrypoint _entrypoint; enum { STACK_SIZE = 2 * 1024 * sizeof(Genode::addr_t)}; - Service_registry &_local_services; + Registry &_services; - /* - * Dynamic linker, does not need to be valid because init is statically - * linked - */ - Dataspace_capability _ldso_ds; + Capability _core_ram_cap; + Ram_session &_core_ram; - Pd_session_client _pd; - Ram_session_client _ram; - Cpu_session_client _cpu; + Capability _core_cpu_cap; + Cpu_session &_core_cpu; - Child::Initial_thread _initial_thread { _cpu, _pd, "init_main" }; - - Region_map_client _address_space; + size_t const _ram_quota; Child _child; @@ -136,20 +139,16 @@ class Core_child : public Child_policy /** * Constructor */ - Core_child(Dataspace_capability elf_ds, Pd_session_capability pd, - Ram_session_capability ram, - Cpu_session_capability cpu, - Service_registry &services) + Core_child(Registry &services, Ram_session &core_ram, + Capability core_ram_cap, size_t ram_quota, + Cpu_session &core_cpu, Capability core_cpu_cap) : _entrypoint(nullptr, STACK_SIZE, "init_child", false), - _local_services(services), - _pd(pd), _ram(ram), _cpu(cpu), - _address_space(Pd_session_client(pd).address_space()), - _child(elf_ds, _ldso_ds, _pd, _pd, _ram, _ram, _cpu, _initial_thread, - *env()->rm_session(), _address_space, _entrypoint, *this, - *_local_services.find(Pd_session::service_name()), - *_local_services.find(Ram_session::service_name()), - *_local_services.find(Cpu_session::service_name())) + _services(services), + _core_ram_cap(core_ram_cap), _core_ram(core_ram), + _core_cpu_cap(core_cpu_cap), _core_cpu(core_cpu), + _ram_quota(Child::effective_ram_quota(ram_quota)), + _child(*env()->rm_session(), _entrypoint, *this) { _entrypoint.activate(); } @@ -159,8 +158,7 @@ class Core_child : public Child_policy ** Child-policy interface ** ****************************/ - void filter_session_args(const char *, char *args, - Genode::size_t args_len) + void filter_session_args(Service::Name const &, char *args, size_t args_len) override { using namespace Genode; @@ -177,13 +175,36 @@ class Core_child : public Child_policy Arg_string::set_arg(args, args_len, "label", value_buf); } + Name name() const { return "init"; } - const char *name() const { return "init"; } - - Service *resolve_session_request(const char *service, const char *) + Service &resolve_session_request(Service::Name const &name, + Session_state::Args const &args) override { - return _local_services.find(service); + Service *service = nullptr; + _services.for_each([&] (Service &s) { + if (!service && s.name() == name) + service = &s; }); + + if (!service) + throw Parent::Service_denied(); + + return *service; } + + void init(Ram_session &session, Capability cap) override + { + session.ref_account(_core_ram_cap); + _core_ram.transfer_quota(cap, _ram_quota); + } + + void init(Cpu_session &session, Capability cap) override + { + session.ref_account(_core_cpu_cap); + _core_cpu.transfer_quota(cap, Cpu_session::quota_lim_upscale(100, 100)); + } + + Ram_session &ref_ram() { return _core_ram; } + Ram_session_capability ref_ram_cap() const { return _core_ram_cap; } }; @@ -240,6 +261,8 @@ int main() */ Rpc_entrypoint *e = core_env()->entrypoint(); + Registry &services = core_env()->services(); + /* * Allocate session meta data on distinct dataspaces to enable independent * destruction (to enable quota trading) of session component objects. @@ -266,70 +289,42 @@ int main() platform()->irq_alloc(), &sliced_heap); static Trace::Root trace_root (e, &sliced_heap, Trace::sources(), trace_policies); - /* - * Play our role as parent of init and declare our services. - */ - - static Local_service ls[] = { - Local_service(Rom_session::service_name(), &rom_root), - Local_service(Ram_session::service_name(), &ram_root), - Local_service(Rm_session::service_name(), &rm_root), - Local_service(Cpu_session::service_name(), &cpu_root), - Local_service(Pd_session::service_name(), &pd_root), - Local_service(Log_session::service_name(), &log_root), - Local_service(Io_mem_session::service_name(), &io_mem_root), - Local_service(Irq_session::service_name(), &irq_root), - Local_service(Trace::Session::service_name(), &trace_root) - }; - - /* make our local services known to service pool */ - for (unsigned i = 0; i < sizeof(ls) / sizeof(Local_service); i++) - local_services.insert(&ls[i]); + static Core_service rom_service (services, rom_root); + static Core_service ram_service (services, ram_root); + static Core_service rm_service (services, rm_root); + static Core_service cpu_service (services, cpu_root); + static Core_service pd_service (services, pd_root); + static Core_service log_service (services, log_root); + static Core_service io_mem_service (services, io_mem_root); + static Core_service irq_service (services, irq_root); + static Core_service trace_service (services, trace_root); /* make platform-specific services known to service pool */ - platform_add_local_services(e, &sliced_heap, &local_services); + platform_add_local_services(e, &sliced_heap, &services); - /* obtain ROM session with init binary */ - Rom_session_capability init_rom_session_cap; - try { - static Rom_connection rom("init"); - init_rom_session_cap = rom.cap(); } - catch (...) { - error("ROM module \"init\" not present"); } - - /* create ram session for init and transfer some of our own quota */ - Ram_session_capability init_ram_session_cap - = static_cap_cast(ram_root.session("ram_quota=32K", Affinity())); - Ram_session_client(init_ram_session_cap).ref_account(env()->ram_session_cap()); - - /* create CPU session for init and transfer all of the CPU quota to it */ + /* create CPU session representing core */ static Cpu_session_component - cpu(e, e, &pager_ep, &sliced_heap, Trace::sources(), - "label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT); - Cpu_session_capability cpu_cap = core_env()->entrypoint()->manage(&cpu); - Cpu_connection init_cpu("init"); - init_cpu.ref_account(cpu_cap); - cpu.transfer_quota(init_cpu, Cpu_session::quota_lim_upscale(100, 100)); + core_cpu(e, e, &pager_ep, &sliced_heap, Trace::sources(), + "label=\"core\"", Affinity(), Cpu_session::QUOTA_LIMIT); + Cpu_session_capability core_cpu_cap = core_env()->entrypoint()->manage(&core_cpu); - /* transfer all left memory to init, but leave some memory left for core */ - /* NOTE: exception objects thrown in core components are currently allocated on - core's heap and not accounted by the component's meta data allocator */ - Genode::size_t init_quota = platform()->ram_alloc()->avail() - 224*1024; - env()->ram_session()->transfer_quota(init_ram_session_cap, init_quota); - log("", init_quota / (1024*1024), " MiB RAM assigned to init"); + /* + * Transfer all left memory to init, but leave some memory left for core + * + * NOTE: exception objects thrown in core components are currently + * allocated on core's heap and not accounted by the component's meta data + * allocator + */ - Pd_connection init_pd("init"); - Core_child *init = new (env()->heap()) - Core_child(Rom_session_client(init_rom_session_cap).dataspace(), - init_pd, init_ram_session_cap, init_cpu.cap(), - local_services); + Genode::size_t const ram_quota = platform()->ram_alloc()->avail() - 224*1024; + log("", ram_quota / (1024*1024), " MiB RAM assigned to init"); + + static Volatile_object + init(services, *env()->ram_session(), env()->ram_session_cap(), + ram_quota, core_cpu, core_cpu_cap); platform()->wait_for_exit(); - destroy(env()->heap(), init); - - rom_root.close(init_rom_session_cap); - ram_root.close(init_ram_session_cap); - + init.destruct(); return 0; } diff --git a/repos/base/src/core/platform_services.cc b/repos/base/src/core/platform_services.cc index 55ab6d00b..68cbfed5b 100644 --- a/repos/base/src/core/platform_services.cc +++ b/repos/base/src/core/platform_services.cc @@ -15,5 +15,5 @@ #include -void Genode::platform_add_local_services(Rpc_entrypoint*, Sliced_heap*, - Service_registry*) { } +void Genode::platform_add_local_services(Rpc_entrypoint *, Sliced_heap*, + Registry *) { } diff --git a/repos/base/src/core/ram_session_component.cc b/repos/base/src/core/ram_session_component.cc index e31d2f38c..d49170f67 100644 --- a/repos/base/src/core/ram_session_component.cc +++ b/repos/base/src/core/ram_session_component.cc @@ -78,8 +78,10 @@ int Ram_session_component::_transfer_quota(Ram_session_component *dst, size_t am /* decrease quota limit of this session - check against used quota */ if (_quota_limit < amount + _payload) { - warning("Insufficient quota for transfer: ", Cstring(_label)); - warning(" have ", _quota_limit - _payload, ", need ", amount); + warning("insufficient quota for transfer: " + "'", Cstring(_label), "' to '", Cstring(dst->_label), "' " + "have ", (_quota_limit - _payload)/1024, " KiB, " + "need ", amount/1024, " KiB"); return -3; } @@ -262,6 +264,10 @@ int Ram_session_component::transfer_quota(Ram_session_capability ram_session_cap { auto lambda = [&] (Ram_session_component *dst) { return _transfer_quota(dst, amount); }; + + if (this->cap() == ram_session_cap) + return 0; + return _ram_session_ep->apply(ram_session_cap, lambda); } diff --git a/repos/base/src/core/spec/x86/platform_services.cc b/repos/base/src/core/spec/x86/platform_services.cc index af266aff9..87df265fa 100644 --- a/repos/base/src/core/spec/x86/platform_services.cc +++ b/repos/base/src/core/spec/x86/platform_services.cc @@ -26,11 +26,11 @@ */ void Genode::platform_add_local_services(Rpc_entrypoint*, Sliced_heap *sliced_heap, - Service_registry *local_services) + Registry *local_services) { static Io_port_root io_port_root(core_env()->pd_session(), platform()->io_port_alloc(), sliced_heap); - static Local_service io_port_ls(Io_port_session::service_name(), - &io_port_root); - local_services->insert(&io_port_ls); + + static Core_service + io_port_ls(*local_services, io_port_root); } diff --git a/repos/base/src/include/base/internal/attached_stack_area.h b/repos/base/src/include/base/internal/attached_stack_area.h index 9e9fc5361..f6bea35e5 100644 --- a/repos/base/src/include/base/internal/attached_stack_area.h +++ b/repos/base/src/include/base/internal/attached_stack_area.h @@ -30,7 +30,8 @@ struct Genode::Attached_stack_area : Expanding_region_map_client { Attached_stack_area(Parent &parent, Pd_session_capability pd) : - Expanding_region_map_client(pd, Pd_session_client(pd).stack_area()) + Expanding_region_map_client(pd, Pd_session_client(pd).stack_area(), + Parent::Env::pd()) { Region_map_client address_space(Pd_session_client(pd).address_space()); diff --git a/repos/base/src/include/base/internal/expanding_cpu_session_client.h b/repos/base/src/include/base/internal/expanding_cpu_session_client.h index 140c44e9a..6a7f9c3c0 100644 --- a/repos/base/src/include/base/internal/expanding_cpu_session_client.h +++ b/repos/base/src/include/base/internal/expanding_cpu_session_client.h @@ -27,7 +27,7 @@ namespace Genode { struct Expanding_cpu_session_client; } struct Genode::Expanding_cpu_session_client : Upgradeable_client { - Expanding_cpu_session_client(Genode::Cpu_session_capability cap) + Expanding_cpu_session_client(Genode::Cpu_session_capability cap, Parent::Client::Id id) : /* * We need to upcast the capability because on some platforms (i.e., @@ -35,7 +35,7 @@ struct Genode::Expanding_cpu_session_client : Upgradeable_client - (static_cap_cast(cap)) + (static_cap_cast(cap), id) { } Thread_capability diff --git a/repos/base/src/include/base/internal/expanding_parent_client.h b/repos/base/src/include/base/internal/expanding_parent_client.h index 9e29b82f2..6ef975c2f 100644 --- a/repos/base/src/include/base/internal/expanding_parent_client.h +++ b/repos/base/src/include/base/internal/expanding_parent_client.h @@ -87,13 +87,14 @@ class Genode::Expanding_parent_client : public Parent_client ** Parent interface ** **********************/ - Session_capability session(Service_name const &name, + Session_capability session(Client::Id id, + Service_name const &name, Session_args const &args, Affinity const &affinity) override { enum { NUM_ATTEMPTS = 2 }; return retry( - [&] () { return Parent_client::session(name, args, affinity); }, + [&] () { return Parent_client::session(id, name, args, affinity); }, [&] () { /* @@ -114,7 +115,7 @@ class Genode::Expanding_parent_client : public Parent_client NUM_ATTEMPTS); } - void upgrade(Session_capability to_session, Upgrade_args const &args) override + Upgrade_result upgrade(Client::Id id, Upgrade_args const &args) override { /* * If the upgrade fails, attempt to issue a resource request twice. @@ -131,8 +132,8 @@ class Genode::Expanding_parent_client : public Parent_client * caller to issue (and respond to) a resource request. */ enum { NUM_ATTEMPTS = 2 }; - retry( - [&] () { Parent_client::upgrade(to_session, args); }, + return retry( + [&] () { return Parent_client::upgrade(id, args); }, [&] () { resource_request(Resource_args(args.string())); }, NUM_ATTEMPTS); } 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 45d9d8198..69ac703d3 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 @@ -26,8 +26,8 @@ namespace Genode { class Expanding_ram_session_client; } struct Genode::Expanding_ram_session_client : Upgradeable_client { - Expanding_ram_session_client(Ram_session_capability cap) - : Upgradeable_client(cap) { } + Expanding_ram_session_client(Ram_session_capability cap, Parent::Client::Id id) + : Upgradeable_client(cap, id) { } Ram_dataspace_capability alloc(size_t size, Cache_attribute cached = UNCACHED) override { diff --git a/repos/base/src/include/base/internal/expanding_region_map_client.h b/repos/base/src/include/base/internal/expanding_region_map_client.h index 1ac5b45b7..4126cf78e 100644 --- a/repos/base/src/include/base/internal/expanding_region_map_client.h +++ b/repos/base/src/include/base/internal/expanding_region_map_client.h @@ -29,8 +29,9 @@ struct Genode::Expanding_region_map_client : Region_map_client { Upgradeable_client _pd_client; - Expanding_region_map_client(Pd_session_capability pd, Capability rm) - : Region_map_client(rm), _pd_client(pd) { } + Expanding_region_map_client(Pd_session_capability pd, Capability rm, + Parent::Client::Id pd_id) + : Region_map_client(rm), _pd_client(pd, pd_id) { } Local_addr attach(Dataspace_capability ds, size_t size, off_t offset, bool use_local_addr, Local_addr local_addr, diff --git a/repos/base/src/include/base/internal/globals.h b/repos/base/src/include/base/internal/globals.h index e3975b7cc..a67f897ec 100644 --- a/repos/base/src/include/base/internal/globals.h +++ b/repos/base/src/include/base/internal/globals.h @@ -17,11 +17,14 @@ #ifndef _INCLUDE__BASE__INTERNAL__GLOBALS_H_ #define _INCLUDE__BASE__INTERNAL__GLOBALS_H_ +#include + namespace Genode { class Region_map; class Ram_session; class Env; + class Local_session_id_space; extern Region_map *env_stack_area_region_map; extern Ram_session *env_stack_area_ram_session; @@ -30,7 +33,11 @@ namespace Genode { void init_cxx_heap(Env &); void init_ldso_phdr(Env &); void init_signal_thread(Env &); + void init_root_proxy(Env &); void init_log(); + + Id_space &env_session_id_space(); + Env &internal_env(); } #endif /* _INCLUDE__BASE__INTERNAL__GLOBALS_H_ */ diff --git a/repos/base/src/include/base/internal/platform_env.h b/repos/base/src/include/base/internal/platform_env.h index 32173dcaf..47064442e 100644 --- a/repos/base/src/include/base/internal/platform_env.h +++ b/repos/base/src/include/base/internal/platform_env.h @@ -48,9 +48,9 @@ class Genode::Platform_env : public Env_deprecated, struct Resources { template - Capability request(Parent &parent, char const *service) + Capability request(Parent &parent, Parent::Client::Id id) { - return static_cap_cast(parent.session(service, "")); + return static_cap_cast(parent.session_cap(id)); } Expanding_ram_session_client ram; @@ -60,10 +60,12 @@ class Genode::Platform_env : public Env_deprecated, Resources(Parent &parent) : - ram(request(parent, "Env::ram_session")), - cpu(request(parent, "Env::cpu_session")), - pd (request (parent, "Env::pd_session")), - rm (pd, pd.address_space()) + ram(request(parent, Parent::Env::ram()), + Parent::Env::ram()), + cpu(request(parent, Parent::Env::cpu()), + Parent::Env::cpu()), + pd (request (parent, Parent::Env::pd())), + rm (pd, pd.address_space(), Parent::Env::pd()) { } }; diff --git a/repos/base/src/include/base/internal/upgradeable_client.h b/repos/base/src/include/base/internal/upgradeable_client.h index 49311fffb..0c47b18dc 100644 --- a/repos/base/src/include/base/internal/upgradeable_client.h +++ b/repos/base/src/include/base/internal/upgradeable_client.h @@ -28,19 +28,17 @@ struct Genode::Upgradeable_client : CLIENT { typedef Genode::Capability Capability; - Capability _cap; + Parent::Client::Id _id; - Upgradeable_client(Capability cap) : CLIENT(cap), _cap(cap) { } + Upgradeable_client(Capability cap, Parent::Client::Id id) + : CLIENT(cap), _id(id) { } void upgrade_ram(size_t quota) { - log("upgrading quota donation for Env::", CLIENT::Rpc_interface::service_name(), - " (", quota, " bytes)"); - char buf[128]; snprintf(buf, sizeof(buf), "ram_quota=%lu", quota); - env()->parent()->upgrade(_cap, buf); + env()->parent()->upgrade(_id, buf); } }; diff --git a/repos/base/src/lib/base/child.cc b/repos/base/src/lib/base/child.cc index 7d366fff1..096d06bc1 100644 --- a/repos/base/src/lib/base/child.cc +++ b/repos/base/src/lib/base/child.cc @@ -43,12 +43,16 @@ namespace { public: + class Quota_exceeded : Exception { }; + /** * Constructor * * \param quantim number of bytes to transfer * \param from donator RAM session * \param to receiver RAM session + * + * \throw Quota_exceeded */ Transfer(size_t quantum, Ram_session_capability from, @@ -58,7 +62,7 @@ namespace { if (_from.valid() && _to.valid() && Ram_session_client(_from).transfer_quota(_to, quantum)) { warning("not enough quota for a donation of ", quantum, " bytes"); - throw Parent::Quota_exceeded(); + throw Quota_exceeded(); } } @@ -83,198 +87,15 @@ namespace { } -/******************** - ** Child::Session ** - ********************/ - -class Child::Session : public Object_pool::Entry, - public List::Element -{ - private: - - enum { IDENT_LEN = 16 }; - - /** - * Session capability at the server - */ - Session_capability _cap; - - /** - * Service interface that was used to create the session - */ - Service *_service; - - /** - * Server implementing the session - * - * Even though we can normally determine the server of the session via - * '_service->server()', this does not apply when destructing a server. - * During destruction, we use the 'Server' pointer as opaque key for - * revoking active sessions of the server. So we keep a copy - * independent of the 'Service' object. - */ - Server *_server; - - /** - * Total of quota associated with this session - */ - size_t _donated_ram_quota; - - /** - * Name of session, used for debugging - */ - char _ident[IDENT_LEN]; - - public: - - /** - * Constructor - * - * \param session session capability - * \param service service that implements the session - * \param ram_quota initial quota donation associated with - * the session - * \param ident optional session identifier, used for - * debugging - */ - Session(Session_capability session, Service *service, - size_t ram_quota, const char *ident = "") - : - Object_pool::Entry(session), _cap(session), - _service(service), _server(service->server()), - _donated_ram_quota(ram_quota) { - strncpy(_ident, ident, sizeof(_ident)); } - - /** - * Default constructor creates invalid session - */ - Session() : _service(0), _donated_ram_quota(0) { } - - /** - * Extend amount of ram attached to the session - */ - void upgrade_ram_quota(size_t ram_quota) { - _donated_ram_quota += ram_quota; } - - /** - * Accessors - */ - Session_capability cap() const { return _cap; } - size_t donated_ram_quota() const { return _donated_ram_quota; } - bool valid() const { return _service != 0; } - Service *service() const { return _service; } - Server *server() const { return _server; } - const char *ident() const { return _ident; } -}; - - /*********** ** Child ** ***********/ -void Child::_add_session(Child::Session const &s) +template +static Service &parent_service() { - Lock::Guard lock_guard(_lock); - - /* - * Store session information in a new child's meta data structure. The - * allocation from 'heap()' may throw a 'Ram_session::Quota_exceeded' - * exception. - */ - Session *session = 0; - try { - session = new (heap()) - Session(s.cap(), s.service(), - s.donated_ram_quota(), s.ident()); } - catch (Allocator::Out_of_memory) { - throw Parent::Quota_exceeded(); } - - /* these functions may also throw 'Ram_session::Quota_exceeded' */ - _session_pool.insert(session); - _session_list.insert(session); -} - - -void Child::_remove_session(Child::Session *s) -{ - /* forget about this session */ - _session_list.remove(s); - - /* return session quota to the ram session of the child */ - if (_policy.ref_ram_session()->transfer_quota(_ram, s->donated_ram_quota())) - error("We ran out of our own quota"); - - destroy(heap(), s); -} - - -Service &Child::_parent_service() -{ - static Parent_service parent_service(""); - return parent_service; -} - - -void Child::_close(Session* s) -{ - if (!s) { - warning("no session structure found"); - return; - } - - /* - * There is a chance that the server is not responding to the 'close' call, - * making us block infinitely. However, by using core's cancel-blocking - * mechanism, we can cancel the 'close' call by another (watchdog) thread - * that invokes 'cancel_blocking' at our thread after a timeout. The - * unblocking is reflected at the API level as an 'Blocking_canceled' - * exception. We catch this exception to proceed with normal operation - * after being unblocked. - */ - try { s->service()->close(s->cap()); } - catch (Blocking_canceled) { - warning("Got Blocking_canceled exception during ", s->ident(), "->close call"); } - - /* - * If the session was provided by a child of us, - * 'server()->ram_session_cap()' returns the RAM session of the - * corresponding child. Since the session to the server is closed now, we - * expect that the server released all donated resources and we can - * decrease the servers' quota. - * - * If this goes wrong, the server is misbehaving. - */ - if (s->service()->ram_session_cap().valid()) { - Ram_session_client server_ram(s->service()->ram_session_cap()); - if (server_ram.transfer_quota(_policy.ref_ram_cap(), - s->donated_ram_quota())) { - error("Misbehaving server '", s->service()->name(), "'!"); - } - } - - { - Lock::Guard lock_guard(_lock); - _remove_session(s); - } -} - - -void Child::revoke_server(Server const *server) -{ - Lock::Guard lock_guard(_lock); - - while (1) { - /* search session belonging to the specified server */ - Session *s = _session_list.first(); - for ( ; s && (s->server() != server); s = s->next()); - - /* if no matching session exists, we are done */ - if (!s) return; - - _session_pool.apply(s->cap(), [&] (Session *s) { - if (s) _session_pool.remove(s); }); - _remove_session(s); - } + static Parent_service service(SESSION::service_name()); + return service; } @@ -298,133 +119,374 @@ void Child::notify_resource_avail() const } -void Child::announce(Parent::Service_name const &name, Root_capability root) +void Child::announce(Parent::Service_name const &name) { if (!name.valid_string()) return; - _policy.announce_service(name.string(), root, heap(), &_server); + _policy.announce_service(name.string()); } -Session_capability Child::session(Parent::Service_name const &name, +void Child::session_sigh(Signal_context_capability sigh) +{ + _session_sigh = sigh; + + if (!_session_sigh.valid()) + return; + + /* + * Deliver pending session response if a session became available before + * the signal handler got installed. This can happen for the very first + * asynchronously created session of a component. In 'component.cc', the + * signal handler is registered as response of the session request that + * needs asynchronous handling. + */ + _id_space.for_each([&] (Session_state const &session) { + if (session.phase == Session_state::AVAILABLE + && sigh.valid() && session.async_client_notify) + Signal_transmitter(sigh).submit(); }); +} + + +/** + * Create session-state object for a dynamically created session + * + * \throw Parent::Quota_exceeded + * \throw Parent::Service_denied + */ +Session_state & +create_session(Child_policy::Name const &child_name, Service &service, + Session_state::Factory &factory, Id_space &id_space, + Parent::Client::Id id, Session_state::Args const &args, + Affinity const &affinity) +{ + try { + return service.create_session(factory, id_space, id, args, affinity); + } + catch (Allocator::Out_of_memory) { + error("could not allocate session meta data for child ", child_name); + throw Parent::Quota_exceeded(); + } + catch (Id_space::Conflicting_id) { + + error(child_name, " requested conflicting session ID ", id, " " + "(service=", service.name(), " args=", args, ")"); + + id_space.apply(id, [&] (Session_state &session) { + error("existing session: ", session); }); + } + throw Parent::Service_denied(); +} + + +Session_capability Child::session(Parent::Client::Id id, + Parent::Service_name const &name, Parent::Session_args const &args, Affinity const &affinity) { if (!name.valid_string() || !args.valid_string()) throw Unavailable(); - /* return sessions that we created for the child */ - if (!strcmp("Env::ram_session", name.string())) return _ram; - if (!strcmp("Env::cpu_session", name.string())) return _cpu; - if (!strcmp("Env::pd_session", name.string())) return _pd; + char argbuf[Parent::Session_args::MAX_SIZE]; /* filter session arguments according to the child policy */ - strncpy(_args, args.string(), sizeof(_args)); - _policy.filter_session_args(name.string(), _args, sizeof(_args)); + strncpy(argbuf, args.string(), sizeof(argbuf)); + _policy.filter_session_args(name.string(), argbuf, sizeof(argbuf)); /* filter session affinity */ Affinity const filtered_affinity = _policy.filter_session_affinity(affinity); + /* may throw a 'Parent::Service_denied' exception */ + Service &service = _policy.resolve_session_request(name.string(), argbuf); + + Session_state &session = + create_session(_policy.name(), service, _session_factory, + _id_space, id, argbuf, filtered_affinity); + + session.ready_callback = this; + session.closed_callback = this; + /* transfer the quota donation from the child's account to ourself */ - size_t ram_quota = Arg_string::find_arg(_args, "ram_quota").ulong_value(0); + size_t ram_quota = Arg_string::find_arg(argbuf, "ram_quota").ulong_value(0); - Transfer donation_from_child(ram_quota, _ram, _policy.ref_ram_cap()); + try { + Transfer donation_from_child(ram_quota, _ram.cap(), _policy.ref_ram_cap()); - Service *service = _policy.resolve_session_request(name.string(), _args); + /* transfer session quota from ourself to the service provider */ + Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(), + service.ram()); - /* raise an error if no matching service provider could be found */ - if (!service) - throw Service_denied(); + /* try to dispatch session request synchronously */ + service.initiate_request(session); - /* transfer session quota from ourself to the service provider */ - Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(), - service->ram_session_cap()); + if (session.phase == Session_state::INVALID_ARGS) { + _revert_quota_and_destroy(session); + throw Service_denied(); + } - /* create session */ - Session_capability cap; - try { cap = service->session(_args, filtered_affinity); } - catch (Service::Invalid_args) { throw Service_denied(); } - catch (Service::Unavailable) { throw Service_denied(); } - catch (Service::Quota_exceeded) { throw Quota_exceeded(); } + /* finish transaction */ + donation_from_child.acknowledge(); + donation_to_service.acknowledge(); + } + catch (Transfer::Quota_exceeded) { + /* + * Release session meta data if one of the quota transfers went wrong. + */ + session.destroy(); + throw Parent::Quota_exceeded(); + } - /* register session */ - try { _add_session(Session(cap, service, ram_quota, name.string())); } - catch (Ram_session::Quota_exceeded) { throw Quota_exceeded(); } + /* + * Copy out the session cap before we are potentially kicking off the + * asynchonous request handling at the server to avoid doule-read + * issues with the session.cap, which will be asynchronously assigned + * by the server side. + */ + Session_capability cap = session.cap; - /* finish transaction */ - donation_from_child.acknowledge(); - donation_to_service.acknowledge(); + /* if request was not handled synchronously, kick off async operation */ + if (session.phase == Session_state::CREATE_REQUESTED) + service.wakeup(); + + if (cap.valid()) + session.phase = Session_state::CAP_HANDED_OUT; return cap; } -void Child::upgrade(Session_capability to_session, Parent::Upgrade_args const &args) +Session_capability Child::session_cap(Client::Id id) { - Service *targeted_service = 0; + Session_capability cap; - /* check of upgrade refers to an Env:: resource */ - if (to_session.local_name() == _ram.local_name()) - targeted_service = &_ram_service; - if (to_session.local_name() == _cpu.local_name()) - targeted_service = &_cpu_service; - if (to_session.local_name() == _pd.local_name()) - targeted_service = &_pd_service; + auto lamda = [&] (Session_state &session) { - /* check if upgrade refers to server */ - _session_pool.apply(to_session, [&] (Session *session) - { - if (session) - targeted_service = session->service(); + if (session.phase == Session_state::INVALID_ARGS) { - if (!targeted_service) { - warning("could not lookup service for session upgrade"); - return; + /* + * Implicity discard the session request when delivering an + * exception because the exception will trigger the deallocation + * of the session ID at the child anyway. + */ + _revert_quota_and_destroy(session); + throw Parent::Service_denied(); } - if (!args.valid_string()) { - warning("no valid session-upgrade arguments"); + if (!session.alive()) + warning(_policy.name(), ": attempt to request cap for unavailable session: ", session); + + if (session.cap.valid()) + session.phase = Session_state::CAP_HANDED_OUT; + + cap = session.cap; + }; + + try { + _id_space.apply(id, lamda); } + + catch (Id_space::Unknown_id) { + warning(_policy.name(), " requested session cap for unknown ID"); } + + return cap; +} + + +Parent::Upgrade_result Child::upgrade(Client::Id id, Parent::Upgrade_args const &args) +{ + if (!args.valid_string()) { + warning("no valid session-upgrade arguments"); + return UPGRADE_DONE; + } + + Upgrade_result result = UPGRADE_PENDING; + + _id_space.apply(id, [&] (Session_state &session) { + + if (session.phase != Session_state::CAP_HANDED_OUT) { + warning("attempt to upgrade session in invalid state"); return; } size_t const ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); - /* transfer quota from client to ourself */ - Transfer donation_from_child(ram_quota, _ram, _policy.ref_ram_cap()); + try { + /* transfer quota from client to ourself */ + Transfer donation_from_child(ram_quota, _ram.cap(), _policy.ref_ram_cap()); - /* transfer session quota from ourself to the service provider */ - Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(), - targeted_service->ram_session_cap()); + /* transfer session quota from ourself to the service provider */ + Transfer donation_to_service(ram_quota, _policy.ref_ram_cap(), + session.service().ram()); - try { targeted_service->upgrade(to_session, args.string()); } - catch (Service::Quota_exceeded) { throw Quota_exceeded(); } + session.increase_donated_quota(ram_quota); + session.phase = Session_state::UPGRADE_REQUESTED; - /* remember new amount attached to the session */ - if (session) - session->upgrade_ram_quota(ram_quota); + session.service().initiate_request(session); - /* finish transaction */ - donation_from_child.acknowledge(); - donation_to_service.acknowledge(); + /* finish transaction */ + donation_from_child.acknowledge(); + donation_to_service.acknowledge(); + } + catch (Transfer::Quota_exceeded) { + warning(_policy.name(), ": upgrade of ", session.service().name(), " failed"); + throw Parent::Quota_exceeded(); + } + + if (session.phase == Session_state::CAP_HANDED_OUT) { + result = UPGRADE_DONE; + return; + } + + session.service().wakeup(); }); + return result; } -void Child::close(Session_capability session_cap) +void Child::_revert_quota_and_destroy(Session_state &session) +{ + try { + /* transfer session quota from the service to ourself */ + Transfer donation_from_service(session.donated_ram_quota(), + session.service().ram(), _policy.ref_ram_cap()); + + /* transfer session quota from ourself to the client (our child) */ + Transfer donation_to_client(session.donated_ram_quota(), + _policy.ref_ram_cap(), ram_session_cap()); + /* finish transaction */ + donation_from_service.acknowledge(); + donation_to_client.acknowledge(); + } + catch (Transfer::Quota_exceeded) { + warning(_policy.name(), ": could not revert session quota (", session, ")"); } + + session.destroy(); +} + + +Child::Close_result Child::_close(Session_state &session) +{ + /* + * If session could not be established, destruct session immediately + * without involving the server + */ + if (session.phase == Session_state::INVALID_ARGS) { + _revert_quota_and_destroy(session); + return CLOSE_DONE; + } + + /* close session if alive */ + if (session.alive()) { + session.phase = Session_state::CLOSE_REQUESTED; + session.service().initiate_request(session); + } + + /* + * The service may have completed the close request immediately (e.g., + * a locally implemented service). In this case, we can skip the + * asynchonous handling. + */ + + if (session.phase == Session_state::CLOSED) { + _revert_quota_and_destroy(session); + return CLOSE_DONE; + } + + session.discard_id_at_client(); + session.service().wakeup(); + + return CLOSE_PENDING; +} + + +Child::Close_result Child::close(Client::Id id) { /* refuse to close the child's initial sessions */ - if (session_cap.local_name() == _ram.local_name() - || session_cap.local_name() == _cpu.local_name() - || session_cap.local_name() == _pd.local_name()) - return; + if (Parent::Env::session_id(id)) + return CLOSE_DONE; - Session *session = nullptr; - _session_pool.apply(session_cap, [&] (Session *s) - { - session = s; - if (s) _session_pool.remove(s); - }); - _close(session); + try { + Close_result result = CLOSE_PENDING; + auto lamda = [&] (Session_state &session) { result = _close(session); }; + _id_space.apply(id, lamda); + return result; + } + catch (Id_space::Unknown_id) { return CLOSE_DONE; } +} + + +void Child::session_ready(Session_state &session) +{ + if (_session_sigh.valid() && session.async_client_notify) + Signal_transmitter(_session_sigh).submit(); +} + + +void Child::session_closed(Session_state &session) +{ + /* + * If the session was provided by a child of us, 'service.ram()' returns + * the RAM session of the corresponding child. Since the session to the + * server is closed now, we expect the server to have released all donated + * resources so that we can decrease the servers' quota. + * + * If this goes wrong, the server is misbehaving. + */ + _revert_quota_and_destroy(session); + + if (_session_sigh.valid()) + Signal_transmitter(_session_sigh).submit(); +} + + +void Child::session_response(Server::Id id, Session_response response) +{ + try { + _policy.server_id_space().apply(id, [&] (Session_state &session) { + + switch (response) { + + case Parent::SESSION_CLOSED: + session.phase = Session_state::CLOSED; + if (session.closed_callback) + session.closed_callback->session_closed(session); + break; + + case Parent::INVALID_ARGS: + session.phase = Session_state::INVALID_ARGS; + 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; + if (session.ready_callback) + session.ready_callback->session_ready(session); + } + break; + } + }); + } catch (Child_policy::Nonexistent_id_space) { } +} + + +void Child::deliver_session_cap(Server::Id id, Session_capability cap) +{ + try { + _policy.server_id_space().apply(id, [&] (Session_state &session) { + + if (session.cap.valid()) { + error("attempt to assign session cap twice"); + return; + } + + session.cap = cap; + session.phase = Session_state::AVAILABLE; + + if (session.ready_callback) + session.ready_callback->session_ready(session); + }); + } catch (Child_policy::Nonexistent_id_space) { } } @@ -474,32 +536,41 @@ Parent::Resource_args Child::yield_request() void Child::yield_response() { _policy.yield_response(); } -Child::Child(Dataspace_capability elf_ds, - Dataspace_capability ldso_ds, - Pd_session_capability pd_cap, - Pd_session &pd, - Ram_session_capability ram_cap, - Ram_session &ram, - Cpu_session_capability cpu_cap, - Initial_thread_base &initial_thread, - Region_map &local_rm, - Region_map &remote_rm, +namespace { + + /** + * Return interface for interacting with the child's address space + * + * Depending on the return value of 'Child_policy::address_space', we + * either interact with a local object of via an RPC client stub. + */ + struct Child_address_space + { + Region_map_client _rm_client; + Region_map &_rm; + + Child_address_space(Pd_session &pd, Child_policy &policy) + : + _rm_client(pd.address_space()), + _rm(policy.address_space(pd) ? *policy.address_space(pd) : _rm_client) + { } + + Region_map ®ion_map() { return _rm; } + }; +} + + +Child::Child(Region_map &local_rm, Rpc_entrypoint &entrypoint, - Child_policy &policy, - Service &pd_service, - Service &ram_service, - Service &cpu_service) + Child_policy &policy) try : - _pd(pd_cap), _ram(ram_cap), _cpu(cpu_cap), - _pd_service(pd_service), - _ram_service(ram_service), - _cpu_service(cpu_service), - _heap(&ram, &local_rm), + _policy(policy), + _heap(&_ram.session(), &local_rm), _entrypoint(entrypoint), _parent_cap(_entrypoint.manage(this)), - _policy(policy), - _server(_ram), - _process(elf_ds, ldso_ds, pd_cap, pd, ram, initial_thread, local_rm, remote_rm, + _process(_binary.session().dataspace(), _linker_dataspace(), + _pd.cap(), _pd.session(), _ram.session(), _initial_thread, local_rm, + Child_address_space(_pd.session(), _policy).region_map(), _parent_cap) { } catch (Cpu_session::Thread_creation_failed) { throw Process_startup_failed(); } @@ -512,8 +583,48 @@ catch (Region_map::Attach_failed) { throw Process_startup_failed(); } Child::~Child() { _entrypoint.dissolve(this); - _policy.unregister_services(); - _session_pool.remove_all([&] (Session *s) { _close(s); }); + /* + * Purge the meta data about any dangling sessions provided by the child to + * other children. + * + * Note that the session quota is not transferred back to the respective + * clients. + * + * All the session meta data is lost after this point. In principle, we + * could accumulate the to-be-replenished quota at each client. Once the + * server is completely destroyed (and we thereby regained all of the + * server's resources, the RAM sessions of the clients could be updated. + * However, a client of a suddenly disappearing server is expected to be in + * trouble anyway and likely to get stuck on the next attempt to interact + * with the server. So the added complexity of reverting the session quotas + * would be to no benefit. + */ + try { + auto lambda = [&] (Session_state &s) { _revert_quota_and_destroy(s); }; + while (_policy.server_id_space().apply_any(lambda)); + } + catch (Child_policy::Nonexistent_id_space) { } + + /* + * Remove statically created env sessions from the child's ID space. + */ + auto discard_id_fn = [&] (Session_state &s) { s.discard_id_at_client(); }; + + _id_space.apply(Env::ram(), discard_id_fn); + _id_space.apply(Env::cpu(), discard_id_fn); + _id_space.apply(Env::pd(), discard_id_fn); + _id_space.apply(Env::log(), discard_id_fn); + + /* + * Remove dynamically created sessions from the child's ID space. + */ + auto close_fn = [&] (Session_state &session) { + session.closed_callback = nullptr; + session.ready_callback = nullptr; + _close(session); + }; + + while (_id_space.apply_any(close_fn)); } diff --git a/repos/base/src/lib/base/component.cc b/repos/base/src/lib/base/component.cc index d666d66d3..e5cd3c0ad 100644 --- a/repos/base/src/lib/base/component.cc +++ b/repos/base/src/lib/base/component.cc @@ -14,21 +14,55 @@ /* Genode includes */ #include +#include +#include #include /* base-internal includes */ #include +/* + * XXX remove this pointer once 'Env_deprecated' is removed + */ +static Genode::Env *env_ptr = nullptr; namespace { + using namespace Genode; + struct Env : Genode::Env { Genode::Entrypoint &_ep; - Env(Genode::Entrypoint &ep) : _ep(ep) { } + Genode::Parent &_parent = *env()->parent(); - Genode::Parent &parent() override { return *Genode::env()->parent(); } + /** + * Lock for serializing 'session' and 'close' + */ + Genode::Lock _lock; + + /** + * Utility to used block for single signal + */ + struct Blockade + { + Parent &_parent; + Genode::Signal_receiver _sig_rec; + Genode::Signal_context _sig_ctx; + + Blockade(Parent &parent) : _parent(parent) + { + _parent.session_sigh(_sig_rec.manage(&_sig_ctx)); + } + + void block() { _sig_rec.wait_for_signal(); } + }; + + Lazy_volatile_object _session_blockade; + + Env(Genode::Entrypoint &ep) : _ep(ep) { env_ptr = this; } + + Genode::Parent &parent() override { return _parent; } Genode::Ram_session &ram() override { return *Genode::env()->ram_session(); } Genode::Cpu_session &cpu() override { return *Genode::env()->cpu_session(); } Genode::Region_map &rm() override { return *Genode::env()->rm_session(); } @@ -49,6 +83,54 @@ namespace { { return Genode::env()->pd_session_cap(); } + + Genode::Id_space &id_space() override + { + return Genode::env_session_id_space(); + } + + void _block_for_session() + { + /* + * Construct blockade lazily be avoid it being used in core where + * all session requests are immediately answered. + */ + if (!_session_blockade.constructed()) + _session_blockade.construct(_parent); + + _session_blockade->block(); + } + + Session_capability session(Parent::Service_name const &name, + Parent::Client::Id id, + Parent::Session_args const &args, + Affinity const &affinity) override + { + Lock::Guard guard(_lock); + + Session_capability cap = _parent.session(id, name, args, affinity); + if (cap.valid()) + return cap; + + _block_for_session(); + return _parent.session_cap(id); + } + + void upgrade(Parent::Client::Id id, Parent::Upgrade_args const &args) override + { + Lock::Guard guard(_lock); + + if (_parent.upgrade(id, args) == Parent::UPGRADE_PENDING) + _block_for_session(); + } + + void close(Parent::Client::Id id) override + { + Lock::Guard guard(_lock); + + if (_parent.close(id) == Parent::CLOSE_PENDING) + _block_for_session(); + } }; } @@ -58,6 +140,15 @@ namespace Genode { struct Startup; extern void bootstrap_component(); + + Env &internal_env() + { + class Env_ptr_not_initialized { }; + if (!env_ptr) + throw Env_ptr_not_initialized(); + + return *env_ptr; + } } diff --git a/repos/base/src/lib/base/entrypoint.cc b/repos/base/src/lib/base/entrypoint.cc index 1a1ed6d48..b1ff0a4a2 100644 --- a/repos/base/src/lib/base/entrypoint.cc +++ b/repos/base/src/lib/base/entrypoint.cc @@ -85,6 +85,7 @@ void Entrypoint::_process_incoming_signals() _suspended_callback(); init_signal_thread(_env); + _rpc_ep.construct(&_env.pd(), Component::stack_size(), initial_ep_name()); _signal_proxy_cap = manage(_signal_proxy); _sig_rec.construct(); @@ -165,6 +166,9 @@ Entrypoint::Entrypoint(Env &env) /* initialize signalling after initializing but before calling the entrypoint */ init_signal_thread(_env); + /* initialize emulation of the original synchronous root interface */ + init_root_proxy(_env); + /* * Invoke Component::construct function in the context of the entrypoint. */ diff --git a/repos/base/src/lib/base/env_deprecated.cc b/repos/base/src/lib/base/env_deprecated.cc index d4ff10c74..3c90670cc 100644 --- a/repos/base/src/lib/base/env_deprecated.cc +++ b/repos/base/src/lib/base/env_deprecated.cc @@ -13,6 +13,9 @@ */ #include +#include +#include +#include namespace Genode { diff --git a/repos/base/src/lib/base/env_session_id_space.cc b/repos/base/src/lib/base/env_session_id_space.cc new file mode 100644 index 000000000..1d6d973c9 --- /dev/null +++ b/repos/base/src/lib/base/env_session_id_space.cc @@ -0,0 +1,59 @@ +/* + * \brief Component-local session ID space + * \author Norman Feske + * \date 2016-10-13 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* base-internal includes */ +#include +#include + +using namespace Genode; + + +Id_space &Genode::env_session_id_space() +{ + Id_space &id_space = + *unmanaged_singleton >(); + + /* pre-allocate env session IDs */ + static Parent::Client dummy; + static Id_space::Element + ram { dummy, id_space, Parent::Env::ram() }, + cpu { dummy, id_space, Parent::Env::cpu() }, + pd { dummy, id_space, Parent::Env::pd() }, + log { dummy, id_space, Parent::Env::log() }, + binary { dummy, id_space, Parent::Env::binary() }, + linker { dummy, id_space, Parent::Env::linker() }; + + return id_space; +} + + +/* + * \deprecated + */ +Connection_base::Connection_base() +: + _env(internal_env()), + _id_space_element(_parent_client, _env.id_space()) +{ } + + +/* + * \deprecated + */ +Env &Parent_service::_env_deprecated() { return internal_env(); } + + diff --git a/repos/base/src/lib/base/log_console.cc b/repos/base/src/lib/base/log_console.cc index 7b1c87e56..fad7497ce 100644 --- a/repos/base/src/lib/base/log_console.cc +++ b/repos/base/src/lib/base/log_console.cc @@ -26,10 +26,21 @@ class Log_console : public Console enum { _BUF_SIZE = Log_session::MAX_STRING_LEN }; - Log_connection _log; - char _buf[_BUF_SIZE]; - unsigned _num_chars; - Lock _lock; + + struct Log : Log_session_client + { + Session_capability _cap() { + return env()->parent()->session_cap(Parent::Env::log()); } + + Log() : Log_session_client(reinterpret_cap_cast(_cap())) + { } + }; + + Log _log; + + char _buf[_BUF_SIZE]; + unsigned _num_chars; + Lock _lock; void _flush() { @@ -77,7 +88,7 @@ class Log_console : public Console /** * Return LOG session interface */ - Log_session *log_session() { return &_log; } + Log_session &log_session() { return _log; } /** * Re-establish LOG session @@ -85,13 +96,13 @@ class Log_console : public Console void reconnect() { /* - * Note that the destructor of old 'Log_connection' is not called. - * This is not needed because the only designated use of this - * function is the startup procedure of noux processes created - * via fork. At the point of calling this function, the new child - * has no valid capability to the original LOG session anyway. + * We cannot use a 'Volatile_object' because we have to skip + * the object destruction inside a freshly forked process. + * Otherwise, the attempt to destruct the capability contained + * in the 'Log' object would result in an inconsistent ref counter + * of the respective capability-space element. */ - new (&_log) Log_connection; + construct_at(&_log); } }; @@ -109,7 +120,7 @@ static Log_console *stdout_log_console() { return unmanaged_singletonlog_session()->write(s); + return stdout_log_console()->log_session().write(s); } diff --git a/repos/base/src/lib/base/root_proxy.cc b/repos/base/src/lib/base/root_proxy.cc new file mode 100644 index 000000000..23472ab72 --- /dev/null +++ b/repos/base/src/lib/base/root_proxy.cc @@ -0,0 +1,251 @@ +/* + * \brief Mechanism for dispatching session requests to root interfaces + * \author Norman Feske + * \date 2016-10-07 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include +#include + +using namespace Genode; + + +namespace { + + struct Service + { + typedef Session_state::Name Name; + + Name name; + Capability root; + + struct Session : Parent::Server + { + Id_space::Element id; + Session_capability cap; + Service &service; + + Session(Id_space &id_space, Parent::Server::Id id, + Service &service, Session_capability cap) + : + id(*this, id_space, id), cap(cap), service(service) + { } + }; + }; + + + class Service_registry : Noncopyable + { + private: + + enum { MAX = 32 }; + + Lock mutable _lock; + Service _services[MAX]; + unsigned _cnt = 0; + + public: + + void insert(Service const &service) + { + Lock::Guard guard(_lock); + + if (_cnt == MAX) { + error("maximum number of services announced"); + return; + } + + _services[_cnt++] = service; + } + + /** + * Call functor 'fn' with root capability for a given service name + */ + template + void apply(Service::Name const &name, FUNC const &fn) + { + /* + * Protect '_services' but execute 'fn' with the lock released. + * + * If we called 'fn' with the lock held, the following scenario + * may result in a deadlock: + * + * A component provides two services, e.g., "Framebuffer" and + * "Input" (fb_sdl or nit_fb). In-between the two 'announce' + * calls (within the 'Component::construct' function), a + * service request for the already announced service comes in. + * The root proxy calls 'apply' with the service name, which + * results in an RPC call to the root interface that is served + * by the same entrypoint as 'Component::construct'. The RPC + * call blocks until 'Component::construct' returns. However, + * before returning, the function announces the second service, + * eventually arriving at 'Service_registry::insert', which + * tries to acquire the same lock as the blocking 'apply' call. + */ + _lock.lock(); + + for (unsigned i = 0; i < _cnt; i++) { + if (name != _services[i].name) + continue; + + _lock.unlock(); + fn(_services[i]); + return; + } + _lock.unlock(); + } + }; + + + class Root_proxy + { + private: + + Env &_env; + + Id_space _id_space; + + Entrypoint _ep { _env, 2*1024*sizeof(long), "root" }; + + Attached_rom_dataspace _session_requests { _env, "session_requests" }; + + Signal_handler _session_request_handler { + _ep, *this, &Root_proxy::_handle_session_requests }; + + Sliced_heap _sliced_heap { _env.ram(), _env.rm() }; + + Tslab _session_slab { &_sliced_heap }; + + void _handle_session_request(Xml_node); + void _handle_session_requests(); + + Service_registry _services; + + public: + + Root_proxy(Env &env) : _env(env) + { + _session_requests.sigh(_session_request_handler); + } + + void announce(Service const &service) + { + _services.insert(service); + + /* trigger re-interpretation of the "session_requests" ROM */ + Signal_transmitter(_session_request_handler).submit(); + + /* notify parent */ + _env.parent().announce(service.name.string()); + } + }; +} + + +void Root_proxy::_handle_session_request(Xml_node request) +{ + if (!request.has_attribute("id")) + return; + + Parent::Server::Id const id { request.attribute_value("id", 0UL) }; + + typedef Service::Session Session; + + if (request.has_type("create")) { + + if (!request.has_sub_node("args")) + return; + + typedef Session_state::Args Args; + Args const args = request.sub_node("args").decoded_content(); + + try { + Service::Name const name = request.attribute_value("service", Service::Name()); + + _services.apply(name, [&] (Service &service) { + + // XXX affinity + Session_capability cap = + Root_client(service.root).session(args.string(), Affinity()); + + new (_session_slab) Session(_id_space, id, service, cap); + _env.parent().deliver_session_cap(id, cap); + }); + } + catch (Root::Invalid_args) { + _env.parent().session_response(id, Parent::INVALID_ARGS); } + catch (Root::Quota_exceeded) { + _env.parent().session_response(id, Parent::INVALID_ARGS); } + catch (Root::Unavailable) { + _env.parent().session_response(id, Parent::INVALID_ARGS); } + } + + if (request.has_type("upgrade")) { + + _id_space.apply(id, [&] (Session &session) { + + size_t ram_quota = request.attribute_value("ram_quota", 0UL); + + char buf[64]; + snprintf(buf, sizeof(buf), "ram_quota=%ld", ram_quota); + + // XXX handle Root::Invalid_args + Root_client(session.service.root).upgrade(session.cap, buf); + + _env.parent().session_response(id, Parent::SESSION_OK); + }); + } + + if (request.has_type("close")) { + + _id_space.apply(id, [&] (Session &session) { + + Root_client(session.service.root).close(session.cap); + + destroy(_session_slab, &session); + + _env.parent().session_response(id, Parent::SESSION_CLOSED); + }); + } +} + + +void Root_proxy::_handle_session_requests() +{ + _session_requests.update(); + + Xml_node const requests = _session_requests.xml(); + + requests.for_each_sub_node([&] (Xml_node request) { + _handle_session_request(request); }); +} + + +static Env *env_ptr = nullptr; + + +namespace Genode { void init_root_proxy(Env &env) { env_ptr = &env; } } + + +void Parent::announce(Service_name const &name, Root_capability root) +{ + if (!env_ptr) { + error("announce called prior init_root_proxy"); + return; + } + + static Root_proxy root_proxy(*env_ptr); + + root_proxy.announce({ name.string(), root }); +} diff --git a/repos/base/src/lib/base/rpc_cap_alloc.cc b/repos/base/src/lib/base/rpc_cap_alloc.cc index 3c110f419..25a33a9c4 100644 --- a/repos/base/src/lib/base/rpc_cap_alloc.cc +++ b/repos/base/src/lib/base/rpc_cap_alloc.cc @@ -26,13 +26,8 @@ Native_capability Rpc_entrypoint::_alloc_rpc_cap(Pd_session &pd, Untyped_capability new_obj_cap = retry( [&] () { return pd.alloc_rpc_cap(_cap); }, - [&] () { - Pd_session_client *client = - dynamic_cast(&pd); - - if (client) - env()->parent()->upgrade(*client, "ram_quota=16K"); - }); + [&] () { env()->parent()->upgrade(Parent::Env::pd(), + "ram_quota=16K"); }); return new_obj_cap; } diff --git a/repos/base/src/lib/base/session_state.cc b/repos/base/src/lib/base/session_state.cc new file mode 100644 index 000000000..c6dc1c8f5 --- /dev/null +++ b/repos/base/src/lib/base/session_state.cc @@ -0,0 +1,97 @@ +/* + * \brief Representation of a session request + * \author Norman Feske + * \date 2016-10-10 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +using namespace Genode; + + +void Session_state::print(Output &out) const +{ + using Genode::print; + + print(out, "service=", _service.name(), " cid=", _id_at_client, " " + "args='", _args, "' state=", (int)phase, " " + "ram_quota=", _donated_ram_quota); +} + + +void Session_state::generate_session_request(Xml_generator &xml) const +{ + if (!id_at_server.constructed()) + warning(__func__, ": id_at_server not initialized"); + + switch (phase) { + + case CREATE_REQUESTED: + + xml.node("create", [&] () { + xml.attribute("id", id_at_server->id().value); + xml.attribute("service", _service.name()); + xml.node("args", [&] () { + xml.append_sanitized(_args.string()); + }); + }); + break; + + case UPGRADE_REQUESTED: + + xml.node("upgrade", [&] () { + xml.attribute("id", id_at_server->id().value); + xml.attribute("ram_quota", ram_upgrade); + }); + break; + + case CLOSE_REQUESTED: + + xml.node("close", [&] () { + xml.attribute("id", id_at_server->id().value); }); + break; + + case INVALID_ARGS: + case AVAILABLE: + case CAP_HANDED_OUT: + case CLOSED: + break; + } +} + + +void Session_state::destroy() +{ + /* + * Manually release client-side ID so that static env sessions are + * immediately by removed from the cliend ID space when 'destroy' is + * called. Otherwise, the iterative cleanup of the cliend ID space + * via 'apply_any' would end up in an infinite loop. + */ + _id_at_client.destruct(); + + if (_factory) + _factory->_destroy(*this); +} + + +Session_state::Session_state(Service &service, + Id_space &client_id_space, + Parent::Client::Id client_id, + Args const &args, + Affinity const &affinity) +: + _service(service), + _donated_ram_quota(Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0)), + _id_at_client(*this, client_id_space, client_id), + _args(args), _affinity(affinity) +{ } diff --git a/repos/base/src/lib/base/signal.cc b/repos/base/src/lib/base/signal.cc index 10611efb8..105285890 100644 --- a/repos/base/src/lib/base/signal.cc +++ b/repos/base/src/lib/base/signal.cc @@ -241,7 +241,7 @@ Signal_context_capability Signal_receiver::manage(Signal_context *context) log("upgrading quota donation for PD session (", quota, " bytes)"); - env()->parent()->upgrade(env()->pd_session_cap(), buf); + env()->parent()->upgrade(Parent::Env::pd(), buf); } ); diff --git a/repos/base/src/lib/ldso/include/file.h b/repos/base/src/lib/ldso/include/file.h index 2400e24c5..46ab97d0d 100644 --- a/repos/base/src/lib/ldso/include/file.h +++ b/repos/base/src/lib/ldso/include/file.h @@ -75,14 +75,32 @@ struct Linker::File */ struct Linker::Elf_file : File { - Env &env; - Rom_connection rom; - Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR]; - bool const loaded; + Env &env; + Lazy_volatile_object rom_connection; + Rom_session_client rom; + Ram_dataspace_capability ram_cap[Phdr::MAX_PHDR]; + bool const loaded; + + typedef String<64> Name; + + Rom_session_capability _rom_cap(Name const &name) + { + /* request the linker and binary from the component environment */ + Session_capability cap; + if (name == binary_name()) + cap = env.parent().session_cap(Parent::Env::binary()); + if (name == linker_name()) + cap = env.parent().session_cap(Parent::Env::linker()); + if (cap.valid()) + return reinterpret_cap_cast(cap); + + rom_connection.construct(env, name.string()); + return *rom_connection; + } Elf_file(Env &env, Allocator &md_alloc, char const *name, bool load) : - env(env), rom(env, name), loaded(load) + env(env), rom(_rom_cap(name)), loaded(load) { load_phdr(); diff --git a/repos/base/src/lib/ldso/include/linker.h b/repos/base/src/lib/ldso/include/linker.h index a43ebd3f9..31d797115 100644 --- a/repos/base/src/lib/ldso/include/linker.h +++ b/repos/base/src/lib/ldso/include/linker.h @@ -103,12 +103,6 @@ namespace Linker { * Global ELF access lock */ Lock &lock(); - - /** - * Invariants - */ - constexpr char const *binary_name() { return "binary"; } - constexpr char const *linker_name() { return "ld.lib.so"; } } diff --git a/repos/base/src/lib/ldso/include/region_map.h b/repos/base/src/lib/ldso/include/region_map.h index e45ae2582..2ac5fc93f 100644 --- a/repos/base/src/lib/ldso/include/region_map.h +++ b/repos/base/src/lib/ldso/include/region_map.h @@ -96,7 +96,7 @@ class Linker::Region_map [&] () { return _rm.attach_at(ds, local_addr - _base, size, offset); }, - [&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); }); + [&] () { _env.upgrade(Parent::Env::pd(), "ram_quota=8K"); }); } /** @@ -109,7 +109,7 @@ class Linker::Region_map [&] () { return _rm.attach_executable(ds, local_addr - _base, size, offset); }, - [&] () { _env.parent().upgrade(_env.pd_session_cap(), "ram_quota=8K"); }); + [&] () { _env.upgrade(Parent::Env::pd(), "ram_quota=8K"); }); } void detach(Local_addr local_addr) { _rm.detach((addr_t)local_addr - _base); } diff --git a/repos/base/src/lib/ldso/include/types.h b/repos/base/src/lib/ldso/include/types.h index 3e5d18a8d..f84515c16 100644 --- a/repos/base/src/lib/ldso/include/types.h +++ b/repos/base/src/lib/ldso/include/types.h @@ -38,6 +38,12 @@ namespace Linker { enum Bind { BIND_LAZY = Shared_object::BIND_LAZY, BIND_NOW = Shared_object::BIND_NOW }; + + /** + * Invariants + */ + constexpr char const *binary_name() { return "binary"; } + constexpr char const *linker_name() { return "ld.lib.so"; } } #endif /* _INCLUDE__TYPES_H_ */ diff --git a/repos/base/src/test/rm_fault/main.cc b/repos/base/src/test/rm_fault/main.cc index 1e500d649..890bf21e8 100644 --- a/repos/base/src/test/rm_fault/main.cc +++ b/repos/base/src/test/rm_fault/main.cc @@ -4,10 +4,10 @@ * \date 2008-09-24 * * This program starts itself as child. When started, it first determines - * wheather it is parent or child by requesting its own file from the ROM - * service. Because the program blocks all session-creation calls for the - * ROM service, each program instance can determine its parent or child - * role by the checking the result of the session creation. + * wheather it is parent or child by requesting a RM session. Because the + * program blocks all session-creation calls for the RM service, each program + * instance can determine its parent or child role by the checking the result + * of the session creation. */ /* @@ -17,17 +17,11 @@ * under the terms of the GNU General Public License version 2. */ +#include #include -#include -#include #include -#include #include -#include -#include -#include -#include -#include +#include using namespace Genode; @@ -69,108 +63,135 @@ void main_child() ** Parent ** ************/ -class Test_child : public Child_policy +class Test_child_policy : public Child_policy { + public: + + typedef Registered Parent_service; + typedef Registry Parent_services; + private: - enum { STACK_SIZE = 8*1024 }; - - /* - * Entry point used for serving the parent interface - */ - Rpc_entrypoint _entrypoint; - - Region_map_client _address_space; - Pd_session_client _pd; - Ram_session_client _ram; - Cpu_session_client _cpu; - Child::Initial_thread _initial_thread; - - Child _child; - - Parent_service _log_service; + Env &_env; + Parent_services &_parent_services; + Signal_context_capability const _fault_handler_sigh; public: /** * Constructor */ - Test_child(Genode::Dataspace_capability elf_ds, - Genode::Pd_connection &pd, - Genode::Ram_session_capability ram, - Genode::Cpu_session_capability cpu, - Genode::Cap_session *cap) + Test_child_policy(Env &env, Parent_services &parent_services, + Signal_context_capability fault_handler_sigh) : - _entrypoint(cap, STACK_SIZE, "child", false), - _address_space(pd.address_space()), _pd(pd), _ram(ram), _cpu(cpu), - _initial_thread(_cpu, _pd, "child"), - _child(elf_ds, Dataspace_capability(), _pd, _pd, _ram, _ram, - _cpu, _initial_thread, *env()->rm_session(), _address_space, - _entrypoint, *this), - _log_service("LOG") - { - /* start execution of the new child */ - _entrypoint.activate(); - } + _env(env), + _parent_services(parent_services), + _fault_handler_sigh(fault_handler_sigh) + { } /**************************** ** Child-policy interface ** ****************************/ - const char *name() const { return "rmchild"; } + Name name() const override { return "rmchild"; } - Service *resolve_session_request(const char *service, const char *) + Binary_name binary_name() const override { return "test-rm_fault"; } + + 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 { - /* forward white-listed session requests to our parent */ - return !strcmp(service, "LOG") ? &_log_service : 0; + enum { CHILD_QUOTA = 1*1024*1024 }; + session.ref_account(_env.ram_session_cap()); + _env.ram().transfer_quota(cap, CHILD_QUOTA); } - void filter_session_args(const char *service, + void init(Pd_session &session, Pd_session_capability cap) override + { + Region_map_client address_space(session.address_space()); + address_space.fault_handler(_fault_handler_sigh); + } + + Service &resolve_session_request(Service::Name const &service_name, + Session_state::Args const &args) override + { + Service *service = nullptr; + _parent_services.for_each([&] (Service &s) { + if (!service && service_name == s.name()) + service = &s; }); + + if (!service) + throw Parent::Service_denied(); + + return *service; + } + + void filter_session_args(Service::Name const &, char *args, size_t args_len) { - /* define session label for sessions forwarded to our parent */ - Arg_string::set_arg_string(args, args_len, "label", "child"); + /* prefix session label */ + Session_label const orig(label_from_args(args)); + Arg_string::set_arg_string(args, args_len, "label", + prefixed_label(name(), orig).string()); } }; -void main_parent(Dataspace_capability elf_ds) +struct Main_parent { - log("parent role started"); + Env &_env; - /* create environment for new child */ - static Pd_connection pd; - static Ram_connection ram; - static Cpu_connection cpu; - static Cap_connection cap; + Signal_handler _fault_handler { + _env.ep(), *this, &Main_parent::_handle_fault }; - /* transfer some of our own ram quota to the new child */ - enum { CHILD_QUOTA = 1*1024*1024 }; - ram.ref_account(env()->ram_session_cap()); - env()->ram_session()->transfer_quota(ram.cap(), CHILD_QUOTA); + Heap _heap { _env.ram(), _env.rm() }; - static Signal_receiver fault_handler; + /* parent services */ + struct Parent_services : Test_child_policy::Parent_services + { + Allocator &alloc; - /* register fault handler at the child's address space */ - static Signal_context signal_context; - Region_map_client address_space(pd.address_space()); - address_space.fault_handler(fault_handler.manage(&signal_context)); + Parent_services(Allocator &alloc) : alloc(alloc) + { + static const char *names[] = { + "RAM", "PD", "CPU", "ROM", "LOG", 0 }; + for (unsigned i = 0; names[i]; i++) + new (alloc) Test_child_policy::Parent_service(*this, names[i]); + } + + ~Parent_services() + { + for_each([&] (Test_child_policy::Parent_service &s) { destroy(alloc, &s); }); + } + } _parent_services { _heap }; /* create child */ - static Test_child child(elf_ds, pd, ram.cap(), cpu.cap(), &cap); + Test_child_policy _child_policy { _env, _parent_services, _fault_handler }; - /* allocate dataspace used for creating shared memory between parent and child */ - Dataspace_capability ds = env()->ram_session()->alloc(4096); - volatile int *local_addr = env()->rm_session()->attach(ds); + Child _child { _env.rm(), _env.ep().rpc_ep(), _child_policy }; - for (int i = 0; i < 4; i++) { + Region_map_client _address_space { _child.pd().address_space() }; - log("wait for region-manager fault"); - fault_handler.wait_for_signal(); - log("received region-manager fault signal, request fault state"); + /* dataspace used for creating shared memory between parent and child */ + Attached_ram_dataspace _ds { _env.ram(), _env.rm(), 4096 }; - Region_map::State state = address_space.state(); + unsigned _fault_cnt = 0; + + long volatile &_child_value() { return *_ds.local_addr(); } + + void _handle_fault() + { + if (_fault_cnt++ == 4) { + log("--- parent role of region-manager fault test finished ---"); + _env.parent().exit(0); + } + + log("received region-map fault signal, request fault state"); + + Region_map::State state = _address_space.state(); char const *state_name = state.type == Region_map::State::READ_FAULT ? "READ_FAULT" : @@ -182,47 +203,45 @@ void main_parent(Dataspace_capability elf_ds) /* ignore spuriuous fault signal */ if (state.type == Region_map::State::READY) { log("ignoring spurious fault signal"); - continue; + return; } addr_t child_virt_addr = state.addr & ~(4096 - 1); /* allocate dataspace to resolve the fault */ log("attach dataspace to the child at ", Hex(child_virt_addr)); - *local_addr = 0x1234; + _child_value() = 0x1234; - address_space.attach_at(ds, child_virt_addr); + _address_space.attach_at(_ds.cap(), child_virt_addr); - /* wait until our child modifies the dataspace content */ - while (*local_addr == 0x1234); + /* poll until our child modifies the dataspace content */ + while (_child_value() == 0x1234); - log("child modified dataspace content, new value is ", Hex(*local_addr)); + log("child modified dataspace content, new value is ", + Hex(_child_value())); log("revoke dataspace from child"); - address_space.detach((void *)child_virt_addr); + _address_space.detach((void *)child_virt_addr); } - fault_handler.dissolve(&signal_context); - - log("--- parent role of region-manager fault test finished ---"); -} + Main_parent(Env &env) : _env(env) { } +}; -/************************* - ** Common main program ** - *************************/ - -int main(int argc, char **argv) +void Component::construct(Env &env) { log("--- region-manager fault test ---"); - /* obtain own elf file from rom service */ try { - static Rom_connection rom("test-rm_fault"); - main_parent(rom.dataspace()); - } catch (Genode::Rom_connection::Rom_connection_failed) { + /* + * Distinguish parent from child by requesting an service that is only + * available to the parent. + */ + Rm_connection rm; + static Main_parent parent(env); + log("-- parent role started --"); + } + catch (Parent::Service_denied) { main_child(); } - - return 0; } diff --git a/repos/gems/src/server/cpu_sampler/cpu_root.h b/repos/gems/src/server/cpu_sampler/cpu_root.h index fba08f7e9..ca87f2b15 100644 --- a/repos/gems/src/server/cpu_sampler/cpu_root.h +++ b/repos/gems/src/server/cpu_sampler/cpu_root.h @@ -32,6 +32,7 @@ class Cpu_sampler::Cpu_root : public Root_component private: Rpc_entrypoint &_thread_ep; + Env &_env; Allocator &_md_alloc; Thread_list &_thread_list; Thread_list_change_handler &_thread_list_change_handler; @@ -41,9 +42,8 @@ class Cpu_sampler::Cpu_root : public Root_component Cpu_session_component *_create_session(const char *args) override { Cpu_session_component *cpu_session_component = - new (md_alloc()) Cpu_session_component(_thread_ep, - _md_alloc, - _thread_list, + new (md_alloc()) Cpu_session_component(_thread_ep, _env, + _md_alloc, _thread_list, _thread_list_change_handler, args); return cpu_session_component; @@ -51,18 +51,20 @@ class Cpu_sampler::Cpu_root : public Root_component void _upgrade_session(Cpu_session_component *cpu, const char *args) override { - env()->parent()->upgrade(cpu->parent_cpu_session(), args); + size_t ram_quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0); + cpu->upgrade_ram_quota(ram_quota); } public: Cpu_root(Rpc_entrypoint &session_ep, Rpc_entrypoint &thread_ep, + Env &env, Allocator &md_alloc, Thread_list &thread_list, Thread_list_change_handler &thread_list_change_handler) : Root_component(&session_ep, &md_alloc), - _thread_ep(thread_ep), + _thread_ep(thread_ep), _env(env), _md_alloc(md_alloc), _thread_list(thread_list), _thread_list_change_handler(thread_list_change_handler) { } diff --git a/repos/gems/src/server/cpu_sampler/cpu_session_component.cc b/repos/gems/src/server/cpu_sampler/cpu_session_component.cc index 268869372..fe6d73f72 100644 --- a/repos/gems/src/server/cpu_sampler/cpu_session_component.cc +++ b/repos/gems/src/server/cpu_sampler/cpu_session_component.cc @@ -89,14 +89,18 @@ Cpu_sampler::Cpu_session_component::trace_control() } -Cpu_sampler::Cpu_session_component::Cpu_session_component( - Rpc_entrypoint &thread_ep, - Allocator &md_alloc, - Thread_list &thread_list, - Thread_list_change_handler &thread_list_change_handler, - char const *args) +Cpu_sampler:: +Cpu_session_component:: +Cpu_session_component(Rpc_entrypoint &thread_ep, + Env &env, + Allocator &md_alloc, + Thread_list &thread_list, + Thread_list_change_handler &thread_list_change_handler, + char const *args) : _thread_ep(thread_ep), - _parent_cpu_session(env()->parent()->session(args)), + _env(env), + _id_space_element(_parent_client, _env.id_space()), + _parent_cpu_session(_env.session(_id_space_element.id(), args, Affinity())), _md_alloc(md_alloc), _thread_list(thread_list), _thread_list_change_handler(thread_list_change_handler), @@ -105,6 +109,13 @@ Cpu_sampler::Cpu_session_component::Cpu_session_component( { } +void Cpu_sampler::Cpu_session_component::upgrade_ram_quota(size_t ram_quota) +{ + String<64> const args("ram_quota=", ram_quota); + _env.upgrade(_id_space_element.id(), args.string()); +} + + Cpu_sampler::Cpu_session_component::~Cpu_session_component() { _cleanup_native_cpu(); diff --git a/repos/gems/src/server/cpu_sampler/cpu_session_component.h b/repos/gems/src/server/cpu_sampler/cpu_session_component.h index fb6f90771..4fb042604 100644 --- a/repos/gems/src/server/cpu_sampler/cpu_session_component.h +++ b/repos/gems/src/server/cpu_sampler/cpu_session_component.h @@ -55,18 +55,18 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object { private: - Rpc_entrypoint &_thread_ep; - - Cpu_session_client _parent_cpu_session; - Allocator &_md_alloc; - Thread_list &_thread_list; - Thread_list_change_handler &_thread_list_change_handler; - Session_label _session_label; - unsigned int _next_thread_id = 0; - - Capability _native_cpu_cap; - - Capability _setup_native_cpu(); + Rpc_entrypoint &_thread_ep; + Env &_env; + Parent::Client _parent_client; + Id_space::Element const _id_space_element; + Cpu_session_client _parent_cpu_session; + Allocator &_md_alloc; + Thread_list &_thread_list; + Thread_list_change_handler &_thread_list_change_handler; + Session_label _session_label; + unsigned int _next_thread_id = 0; + Capability _native_cpu_cap; + Capability _setup_native_cpu(); void _cleanup_native_cpu(); public: @@ -79,6 +79,7 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object * Constructor */ Cpu_session_component(Rpc_entrypoint &thread_ep, + Env &env, Allocator &md_alloc, Thread_list &thread_list, Thread_list_change_handler &thread_list_change_handler, @@ -89,6 +90,8 @@ class Cpu_sampler::Cpu_session_component : public Rpc_object */ ~Cpu_session_component(); + void upgrade_ram_quota(size_t ram_quota); + /*************************** ** CPU session interface ** diff --git a/repos/gems/src/server/cpu_sampler/main.cc b/repos/gems/src/server/cpu_sampler/main.cc index 195011910..3880046b2 100644 --- a/repos/gems/src/server/cpu_sampler/main.cc +++ b/repos/gems/src/server/cpu_sampler/main.cc @@ -172,7 +172,7 @@ struct Cpu_sampler::Main : Thread_list_change_handler Main(Genode::Env &env) : env(env), alloc(env.ram(), env.rm()), - cpu_root(env.ep().rpc_ep(), env.ep().rpc_ep(), alloc, thread_list, *this), + cpu_root(env.ep().rpc_ep(), env.ep().rpc_ep(), env, alloc, thread_list, *this), config(env, "config") { /* diff --git a/repos/libports/run/ldso.run b/repos/libports/run/ldso.run index c86060846..eaca0e696 100644 --- a/repos/libports/run/ldso.run +++ b/repos/libports/run/ldso.run @@ -80,7 +80,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=4K, label="unknown_file") +[init -> test-ldso] Error: ROM-session creation failed (ram_quota=4096, 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/init/child.h b/repos/os/include/init/child.h index a75e2e23a..774efc90c 100644 --- a/repos/os/include/init/child.h +++ b/repos/os/include/init/child.h @@ -21,6 +21,7 @@ #include #include #include +#include #include /* init includes */ @@ -33,6 +34,16 @@ namespace Init { class Name_registry; class Child_registry; class Child; + + using Genode::log; + using Genode::error; + using Genode::warning; + using Genode::Session_state; + using Genode::Xml_generator; + using Genode::Parent; + using Genode::Id_space; + + typedef Genode::Registered Parent_service; } @@ -47,8 +58,8 @@ namespace Init { static void warn_insuff_quota(Genode::size_t const avail) { if (!config_verbose) { return; } - Genode::log("Warning: Specified quota exceeds available quota."); - Genode::log(" Proceeding with a quota of ", avail, "."); + log("Warning: Specified quota exceeds available quota."); + log(" Proceeding with a quota of ", avail, "."); } inline long read_priority(Genode::Xml_node start_node, long prio_levels) @@ -68,7 +79,7 @@ namespace Init { if (priority && (priority >= prio_levels)) { long new_prio = prio_levels ? prio_levels-1 : 0; - char name[Genode::Service::MAX_NAME_LEN]; + char name[Genode::Service::Name::capacity()]; start_node.attribute("name").value(name, sizeof(name)); Genode::warning(Genode::Cstring(name), ": invalid priority, upgrading " "from ", -priority, " to ", -new_prio); @@ -124,7 +135,6 @@ namespace Init { /** * Return sub string of label with the leading child name stripped out - * */ inline char const *skip_label_prefix(char const *child_name, char const *label) { @@ -164,21 +174,20 @@ namespace Init { * \param child_name name of the originator of the session request * \param service_name name of the requested service */ - inline bool service_node_matches(Genode::Xml_node service_node, - char const *args, - char const *child_name, - char const *service_name) + inline bool service_node_matches(Genode::Xml_node service_node, char const *args, + Genode::Child_policy::Name const &child_name, + Genode::Service::Name const &service_name) { bool const service_matches = service_node.has_type("any-service") || (service_node.has_type("service") && - service_node.attribute("name").has_value(service_name)); + service_node.attribute("name").has_value(service_name.string())); if (!service_matches) return false; Genode::Session_label const session_label(skip_label_prefix( - child_name, Genode::label_from_args(args).string())); + child_name.string(), Genode::label_from_args(args).string())); return !Genode::Xml_node_label_score(service_node, session_label).conflict(); } @@ -188,8 +197,8 @@ namespace Init { * Check if arguments satisfy the condition specified for the route */ inline bool service_node_args_condition_satisfied(Genode::Xml_node service_node, - const char *args, - char const *child_name) + Genode::Session_state::Args const &args, + Genode::Child_policy::Name const &child_name) { try { Genode::Xml_node if_arg = service_node.sub_node("if-arg"); @@ -200,7 +209,7 @@ namespace Init { if_arg.attribute("value").value(value, sizeof(value)); char arg_value[VALUE_MAX_LEN]; - Genode::Arg_string::find_arg(args, key).string(arg_value, sizeof(arg_value), ""); + Genode::Arg_string::find_arg(args.string(), key).string(arg_value, sizeof(arg_value), ""); /* * Skip child-name prefix if the key is the process "label". @@ -214,7 +223,7 @@ namespace Init { * the prefix information is redundant. */ if (Genode::strcmp("label", key) == 0) - return Genode::strcmp(value, skip_label_prefix(child_name, arg_value)) == 0; + return Genode::strcmp(value, skip_label_prefix(child_name.string(), arg_value)) == 0; return Genode::strcmp(value, arg_value) == 0; } catch (...) { } @@ -222,109 +231,80 @@ namespace Init { /* if no if-arg node exists, the condition is met */ return true; } + + + /** + * Check if service name is ambiguous + * + * \return true if the same service is provided multiple + * times + * + * \deprecated + */ + template + inline bool is_ambiguous(Genode::Registry const &services, + Service::Name const &name) + { + /* count number of services with the specified name */ + unsigned cnt = 0; + services.for_each([&] (T const &service) { + cnt += (service.name() == name); }); + + return cnt > 1; + } + + + template + inline Genode::Service *find_service(Genode::Registry &services, + Genode::Service::Name const &name) + { + T *service = nullptr; + services.for_each([&] (T &s) { + if (!service && (s.name() == name)) + service = &s; }); + return service; + } } /** * Init-specific representation of a child service - * - * For init, we introduce this 'Service' variant that distinguishes two - * phases, declared and announced. A 'Routed_service' object is created - * when a '' declaration is found in init's configuration. - * At that time, however, no children including the server do yet exist. - * If, at this stage, a client tries to open a session to this service, - * the client get enqueued in a list of applicants and blocked. When - * the server officially announces its service and passes over the root - * capability, the 'Routed_service' enters the announced stage and any - * applicants get unblocked. */ -class Init::Routed_service : public Genode::Service +class Init::Routed_service : public Genode::Child_service { + public: + + typedef Genode::Child_policy::Name Child_name; + private: - Genode::Root_capability _root; - bool _announced; - Genode::Server *_server; + Child_name _child_name; - struct Applicant : public Genode::Cancelable_lock, - public Genode::List::Element - { - Applicant() : Cancelable_lock(Genode::Lock::LOCKED) { } - }; - - Genode::Lock _applicants_lock; - Genode::List _applicants; + Genode::Registry::Element _registry_element; public: /** * Constructor * - * \param name name of service - * \param server server providing the service + * \param services registry of all services provides by children + * \param child_name child name of server, used for session routing + * + * The other arguments correspond to the arguments of 'Child_service'. */ - Routed_service(const char *name, - Genode::Server *server) - : Service(name), _announced(false), _server(server) { } + Routed_service(Genode::Registry &services, + Child_name const &child_name, + Id_space &server_id_space, + Session_state::Factory &factory, + Service::Name const &name, + Genode::Ram_session_capability ram, + Child_service::Wakeup &wakeup) + : + Child_service(server_id_space, factory, name, ram, wakeup), + _child_name(child_name), _registry_element(services, *this) + { } - Genode::Server *server() const { return _server; } - - void announce(Genode::Root_capability root) - { - Genode::Lock::Guard guard(_applicants_lock); - - _root = root; - _announced = true; - - /* wake up aspiring clients */ - for (Applicant *a; (a = _applicants.first()); ) { - _applicants.remove(a); - a->unlock(); - } - } - - Genode::Session_capability session(const char *args, - Genode::Affinity const &affinity) - { - /* - * This method is called from the context of the client's - * activation thread. If the service is not yet announced, - * we let the client block. - */ - _applicants_lock.lock(); - if (!_announced) { - Applicant myself; - _applicants.insert(&myself); - _applicants_lock.unlock(); - myself.lock(); - } else - _applicants_lock.unlock(); - - Genode::Session_capability cap; - try { cap = Genode::Root_client(_root).session(args, affinity); } - catch (Genode::Root::Invalid_args) { throw Invalid_args(); } - catch (Genode::Root::Unavailable) { throw Unavailable(); } - catch (Genode::Root::Quota_exceeded) { throw Quota_exceeded(); } - catch (Genode::Ipc_error) { throw Unavailable(); } - - if (!cap.valid()) - throw Unavailable(); - - return cap; - } - - void upgrade(Genode::Session_capability sc, const char *args) - { - try { Genode::Root_client(_root).upgrade(sc, args); } - catch (Genode::Root::Invalid_args) { throw Invalid_args(); } - catch (Genode::Ipc_error) { throw Unavailable(); } - } - - void close(Genode::Session_capability sc) - { - try { Genode::Root_client(_root).close(sc); } - catch (Genode::Ipc_error) { throw Genode::Blocking_canceled(); } - } + Child_name const &child_name() const { return _child_name; } }; @@ -335,6 +315,8 @@ struct Init::Name_registry { virtual ~Name_registry() { } + typedef Genode::Child_policy::Name Name; + /** * Check if specified name is unique * @@ -343,13 +325,16 @@ struct Init::Name_registry virtual bool unique(const char *name) const = 0; /** - * Find server with specified name + * Return child name for a given alias name + * + * If there is no alias, the function returns the original name. */ - virtual Genode::Server *lookup_server(const char *name) const = 0; + virtual Name deref_alias(Name const &) = 0; }; -class Init::Child : Genode::Child_policy +class Init::Child : Genode::Child_policy, + Genode::Child_service::Wakeup { public: @@ -362,6 +347,8 @@ class Init::Child : Genode::Child_policy friend class Child_registry; + Genode::Env &_env; + Genode::List_element _list_element; Genode::Xml_node _start_node; @@ -463,11 +450,6 @@ class Init::Child : Genode::Child_policy Genode::size_t ram_quota; Genode::size_t cpu_quota_pc; bool constrain_phys; - Genode::Pd_connection pd; - Genode::Ram_connection ram; - Genode::Cpu_connection cpu; - - inline void transfer_cpu_quota(); Resources(Genode::Xml_node start_node, const char *label, long prio_levels, @@ -477,31 +459,13 @@ class Init::Child : Genode::Child_policy prio_levels_log2(Genode::log2(prio_levels)), priority(read_priority(start_node, prio_levels)), affinity(affinity_space, - read_affinity_location(affinity_space, start_node)), - pd(label), - ram(label), - cpu(label, - priority*(Genode::Cpu_session::PRIORITY_LIMIT >> prio_levels_log2), - affinity) + read_affinity_location(affinity_space, start_node)) { /* deduce session costs from usable ram quota */ - Genode::size_t session_donations = Genode::Pd_connection::RAM_QUOTA + - Genode::Cpu_connection::RAM_QUOTA + - Genode::Ram_connection::RAM_QUOTA; - - if (ram_quota > session_donations) - ram_quota -= session_donations; - else ram_quota = 0; - - ram.ref_account(Genode::env()->ram_session_cap()); - Genode::env()->ram_session()->transfer_quota(ram.cap(), ram_quota); - - transfer_cpu_quota(); + ram_quota = Genode::Child::effective_ram_quota(ram_quota); } } _resources; - Genode::Child::Initial_thread _initial_thread { _resources.cpu, _resources.pd, - _name.unique }; /* * Entry point used for serving the parent interface and the * locally provided ROM sessions for the 'config' and 'binary' @@ -510,26 +474,21 @@ class Init::Child : Genode::Child_policy enum { ENTRYPOINT_STACK_SIZE = 12*1024 }; Genode::Rpc_entrypoint _entrypoint; - /** - * ELF binary - */ - Genode::Rom_connection _binary_rom; - Genode::Dataspace_capability _binary_rom_ds; + Genode::Parent_service _env_ram_service { _env, Genode::Ram_session::service_name() }; + Genode::Parent_service _env_cpu_service { _env, Genode::Cpu_session::service_name() }; + Genode::Parent_service _env_pd_service { _env, Genode:: Pd_session::service_name() }; + Genode::Parent_service _env_log_service { _env, Genode::Log_session::service_name() }; + Genode::Parent_service _env_rom_service { _env, Genode::Rom_session::service_name() }; + + Genode::Registry &_parent_services; + Genode::Registry &_child_services; + /** * Private child configuration */ Init::Child_config _config; - /** - * Each child of init can act as a server - */ - Genode::Server _server; - - Genode::Region_map_client _address_space { _resources.pd.address_space() }; - Genode::Child _child; - - Genode::Service_registry &_parent_services; - Genode::Service_registry &_child_services; + Genode::Session_requester _session_requester; /** * Policy helpers @@ -537,22 +496,39 @@ class Init::Child : Genode::Child_policy Init::Child_policy_enforce_labeling _labeling_policy; Init::Child_policy_handle_cpu_priorities _priority_policy; Init::Child_policy_provide_rom_file _config_policy; - Init::Child_policy_provide_rom_file _binary_policy; Init::Child_policy_redirect_rom_file _configfile_policy; Init::Child_policy_ram_phys _ram_session_policy; + Genode::Child _child { _env.rm(), _entrypoint, *this }; + + /** + * Child_service::Wakeup callback + */ + void wakeup_child_service() override + { + _session_requester.trigger_update(); + } + public: - Child(Genode::Xml_node start_node, - Genode::Xml_node default_route_node, - Name_registry &name_registry, - long prio_levels, - Genode::Affinity::Space const &affinity_space, - Genode::Service_registry &parent_services, - Genode::Service_registry &child_services, - Genode::Cap_session &cap_session, - Genode::Dataspace_capability ldso_ds) + /** + * Constructor + * + * \throw Ram_session::Alloc_failed allocation of config buffer failed + * \throw Region_map::Attach_failed failed to temporarily attach + * config dataspace to local address + * space + */ + Child(Genode::Env &env, + Genode::Xml_node start_node, + Genode::Xml_node default_route_node, + Name_registry &name_registry, + long prio_levels, + Genode::Affinity::Space const &affinity_space, + Genode::Registry &parent_services, + Genode::Registry &child_services) : + _env(env), _list_element(this), _start_node(start_node), _default_route_node(default_route_node), @@ -560,23 +536,15 @@ class Init::Child : Genode::Child_policy _name(start_node, name_registry), _resources(start_node, _name.unique, prio_levels, affinity_space), - _entrypoint(&cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false, + _entrypoint(&_env.pd(), ENTRYPOINT_STACK_SIZE, _name.unique, false, _resources.affinity.location()), - _binary_rom(_name.file), - _binary_rom_ds(_binary_rom.dataspace()), - _config(_resources.ram.cap(), start_node), - _server(_resources.ram.cap()), - _child(_binary_rom_ds, ldso_ds, - _resources.pd, _resources.pd, - _resources.ram, _resources.ram, - _resources.cpu, _initial_thread, - *Genode::env()->rm_session(), _address_space, _entrypoint, *this), _parent_services(parent_services), _child_services(child_services), + _config(_env.ram(), _env.rm(), start_node), + _session_requester(_entrypoint, _env.ram(), _env.rm()), _labeling_policy(_name.unique), _priority_policy(_resources.prio_levels_log2, _resources.priority), _config_policy("config", _config.dataspace(), &_entrypoint), - _binary_policy("binary", _binary_rom_ds, &_entrypoint), _configfile_policy("config", _config.filename()), _ram_session_policy(_resources.constrain_phys) { @@ -587,10 +555,10 @@ class Init::Child : Genode::Child_policy "\"", Genode::Cstring(_name.unique), "\""); if (config_verbose) { - Genode::log("child \"", Genode::Cstring(_name.unique), "\""); - Genode::log(" RAM quota: ", _resources.ram_quota); - Genode::log(" ELF binary: ", Cstring(_name.file)); - Genode::log(" priority: ", _resources.priority); + log("child \"", Genode::Cstring(_name.unique), "\""); + log(" RAM quota: ", _resources.ram_quota); + log(" ELF binary: ", Cstring(_name.file)); + log(" priority: ", _resources.priority); } /* @@ -601,32 +569,33 @@ class Init::Child : Genode::Child_policy for (; ; service_node = service_node.next("service")) { - char name[Genode::Service::MAX_NAME_LEN]; + char name[Genode::Service::Name::capacity()]; service_node.attribute("name").value(name, sizeof(name)); if (config_verbose) - Genode::log(" provides service ", Genode::Cstring(name)); + log(" provides service ", Genode::Cstring(name)); - child_services.insert(new (_child.heap()) - Routed_service(name, &_server)); + new (_child.heap()) + Routed_service(child_services, this->name(), + _session_requester.id_space(), + _child.session_factory(), + name, _child.ram_session_cap(), *this); } } catch (Xml_node::Nonexistent_sub_node) { } } - virtual ~Child() { - Genode::Service *s; - while ((s = _child_services.find_by_server(&_server))) { - _child_services.remove(s); - } + virtual ~Child() + { + _child_services.for_each([&] (Routed_service &service) { + if (service.has_id_space(_session_requester.id_space())) + destroy(_child.heap(), &service); }); } /** * Return true if the child has the specified name */ - bool has_name(const char *n) const { return !Genode::strcmp(name(), n); } - - Genode::Server *server() { return &_server; } + bool has_name(Child_policy::Name const &str) const { return str == name(); } /** * Start execution of child @@ -638,22 +607,70 @@ class Init::Child : Genode::Child_policy ** Child-policy interface ** ****************************/ - const char *name() const { return _name.unique; } + Child_policy::Name name() const override { return _name.unique; } - Genode::Service *resolve_session_request(const char *service_name, - const char *args) + Binary_name binary_name() const override { return _name.file; } + + Genode::Ram_session &ref_ram() override { return _env.ram(); } + Genode::Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); } + + void init(Genode::Ram_session &session, Genode::Ram_session_capability cap) override { - Genode::Service *service = 0; - using Genode::error; - using Genode::warning; + session.ref_account(Genode::env()->ram_session_cap()); + Genode::env()->ram_session()->transfer_quota(cap, _resources.ram_quota); + } - /* check for config file request */ - if ((service = _config_policy.resolve_session_request(service_name, args))) - return service; + void init(Genode::Cpu_session &session, Genode::Cpu_session_capability cap) override + { + using Genode::Cpu_session; + using Genode::size_t; - /* check for binary file request */ - if ((service = _binary_policy.resolve_session_request(service_name, args))) - return service; + static size_t avail = Cpu_session::quota_lim_upscale( 100, 100); + size_t const need = Cpu_session::quota_lim_upscale(_resources.cpu_quota_pc, 100); + size_t need_adj = 0; + if (need > avail || avail == 0) { + warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100)); + need_adj = Cpu_session::quota_lim_upscale(100, 100); + avail = 0; + } else { + need_adj = Cpu_session::quota_lim_upscale(need, avail); + avail -= need; + } + session.ref_account(_env.cpu_session_cap()); + _env.cpu().transfer_quota(cap, need_adj); + } + + Genode::Id_space &server_id_space() override { + return _session_requester.id_space(); } + + Genode::Service &resolve_session_request(Genode::Service::Name const &service_name, + Genode::Session_state::Args const &args) override + { + /* route environment session requests to the parent */ + Genode::Session_label const label(Genode::label_from_args(args.string())); + if (label == name()) { + if (service_name == Genode::Ram_session::service_name()) return _env_ram_service; + if (service_name == Genode::Cpu_session::service_name()) return _env_cpu_service; + if (service_name == Genode::Pd_session::service_name()) return _env_pd_service; + if (service_name == Genode::Log_session::service_name()) return _env_log_service; + } + + /* route initial ROM requests (binary and linker) to the parent */ + if (service_name == Genode::Rom_session::service_name()) { + if (label.last_element() == binary_name()) return _env_rom_service; + if (label.last_element() == linker_name()) return _env_rom_service; + } + + Genode::Service *service = nullptr; + + /* check for "config" ROM request */ + if ((service = _config_policy.resolve_session_request(service_name.string(), args.string()))) + return *service; + + /* check for "session_requests" ROM request */ + if (service_name == Genode::Rom_session::service_name() + && label.last_element() == Genode::Session_requester::rom_name()) + return _session_requester.service(); try { Genode::Xml_node route_node = _default_route_node; @@ -666,7 +683,7 @@ class Init::Child : Genode::Child_policy bool service_wildcard = service_node.has_type("any-service"); - if (!service_node_matches(service_node, args, name(), service_name)) + if (!service_node_matches(service_node, args.string(), name(), service_name)) continue; if (!service_node_args_condition_satisfied(service_node, args, name())) @@ -676,54 +693,51 @@ class Init::Child : Genode::Child_policy for (; ; target = target.next()) { if (target.has_type("parent")) { - service = _parent_services.find(service_name); - if (service) - return service; + + if ((service = find_service(_parent_services, service_name))) + return *service; if (!service_wildcard) { warning(name(), ": service lookup for " "\"", service_name, "\" at parent failed"); - return 0; + throw Genode::Parent::Service_denied(); } } if (target.has_type("child")) { - char server_name[Name::MAX_NAME_LEN]; - server_name[0] = 0; - target.attribute("name").value(server_name, sizeof(server_name)); - Genode::Server *server = _name_registry.lookup_server(server_name); - if (!server) { - warning(name(), ": invalid route to non-existing " - "server \"", Genode::Cstring(server_name), "\""); - return 0; - } + typedef Name_registry::Name Name; + Name server_name = target.attribute_value("name", Name()); + server_name = _name_registry.deref_alias(server_name); - service = _child_services.find(service_name, server); + _child_services.for_each([&] (Routed_service &s) { + if (s.name() == Service::Name(service_name) + && s.child_name() == server_name) + service = &s; }); if (service) - return service; + return *service; if (!service_wildcard) { Genode::warning(name(), ": lookup to child " - "service \"", service_name, "\" failed"); - return 0; + "server \"", server_name, "\" failed"); + throw Genode::Parent::Service_denied(); } } if (target.has_type("any-child")) { - if (_child_services.is_ambiguous(service_name)) { + if (is_ambiguous(_child_services, service_name)) { error(name(), ": ambiguous routes to " "service \"", service_name, "\""); - return 0; + throw Genode::Parent::Service_denied(); } - service = _child_services.find(service_name); - if (service) - return service; + + if ((service = find_service(_child_services, service_name))) + return *service; if (!service_wildcard) { warning(name(), ": lookup for service " "\"", service_name, "\" failed"); - return 0; + throw Genode::Parent::Service_denied(); } } @@ -734,19 +748,23 @@ class Init::Child : Genode::Child_policy } catch (...) { warning(name(), ": no route to service \"", service_name, "\""); } - return service; + + if (!service) + throw Genode::Parent::Service_denied(); + + return *service; } - void filter_session_args(const char *service, - char *args, Genode::size_t args_len) + void filter_session_args(Service::Name const &service, + char *args, Genode::size_t args_len) override { - _labeling_policy. filter_session_args(service, args, args_len); - _priority_policy. filter_session_args(service, args, args_len); - _configfile_policy.filter_session_args(service, args, args_len); - _ram_session_policy.filter_session_args(service, args, args_len); + _labeling_policy. filter_session_args(service.string(), args, args_len); + _priority_policy. filter_session_args(service.string(), args, args_len); + _configfile_policy. filter_session_args(service.string(), args, args_len); + _ram_session_policy.filter_session_args(service.string(), args, args_len); } - Genode::Affinity filter_session_affinity(Genode::Affinity const &session_affinity) + Genode::Affinity filter_session_affinity(Genode::Affinity const &session_affinity) override { using namespace Genode; @@ -772,28 +790,24 @@ class Init::Child : Genode::Child_policy return Affinity(space, location); } - bool announce_service(const char *service_name, - Genode::Root_capability root, - Genode::Allocator *alloc, - Genode::Server *server) + void announce_service(Genode::Service::Name const &service_name) override { - if (config_verbose) - Genode::log("child \"", name(), "\" announces service \"", service_name, "\""); + log("child \"", name(), "\" announces service \"", service_name, "\""); - Genode::Service *s = _child_services.find(service_name, &_server); - Routed_service *rs = dynamic_cast(s); - if (!s || !rs) { - Genode::error(name(), ": illegal announcement of service \"", service_name, "\""); - return false; - } + bool found = false; + _child_services.for_each([&] (Routed_service &service) { + if (service.has_id_space(_session_requester.id_space()) + && service.name() == service_name) + found = true; }); - rs->announce(root); - return true; + if (!found) + Genode::error(name(), ": illegal announcement of " + "service \"", service_name, "\""); } - void resource_request(Genode::Parent::Resource_args const &args) + void resource_request(Genode::Parent::Resource_args const &args) override { - Genode::log("child \"", name(), "\" requests resources: ", args.string()); + log("child \"", name(), "\" requests resources: ", args.string()); Genode::size_t const requested_ram_quota = Genode::Arg_string::find_arg(args.string(), "ram_quota") @@ -812,7 +826,7 @@ class Init::Child : Genode::Child_policy * when calling 'transfer_quota'. */ - Genode::env()->ram_session()->transfer_quota(_resources.ram.cap(), + Genode::env()->ram_session()->transfer_quota(_child.ram_session_cap(), requested_ram_quota); /* wake up child that was starved for resources */ @@ -838,23 +852,4 @@ class Init::Child : Genode::Child_policy }; -void Init::Child::Resources::transfer_cpu_quota() -{ - using Genode::Cpu_session; - using Genode::size_t; - static size_t avail = Cpu_session::quota_lim_upscale( 100, 100); - size_t const need = Cpu_session::quota_lim_upscale(cpu_quota_pc, 100); - size_t need_adj; - if (need > avail || avail == 0) { - warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100)); - need_adj = Cpu_session::quota_lim_upscale(100, 100); - avail = 0; - } else { - need_adj = Cpu_session::quota_lim_upscale(need, avail); - avail -= need; - } - cpu.ref_account(Genode::env()->cpu_session_cap()); - Genode::env()->cpu_session()->transfer_quota(cpu.cap(), need_adj); -} - #endif /* _INCLUDE__INIT__CHILD_H_ */ diff --git a/repos/os/include/init/child_config.h b/repos/os/include/init/child_config.h index 76f8a345b..5f49b953b 100644 --- a/repos/os/include/init/child_config.h +++ b/repos/os/include/init/child_config.h @@ -14,11 +14,9 @@ #ifndef _INCLUDE__INIT__CHILD_CONFIG_H_ #define _INCLUDE__INIT__CHILD_CONFIG_H_ -#include -#include #include -#include -#include +#include +#include namespace Init { class Child_config; } @@ -27,11 +25,62 @@ class Init::Child_config { private: - enum { CONFIGFILE_NAME_LEN = 64 }; - char _filename[CONFIGFILE_NAME_LEN]; + Genode::Ram_session &_ram; - Genode::Ram_session_capability _ram_session_cap; - Genode::Ram_dataspace_capability _config_ram_ds; + typedef Genode::String<64> Rom_name; + Rom_name const _rom_name; + + Genode::Ram_dataspace_capability const _ram_ds; + + Rom_name _rom_name_from_start_node(Genode::Xml_node start) + { + if (!start.has_sub_node("configfile")) + return Rom_name(); + + return start.sub_node("configfile").attribute_value("name", Rom_name()); + } + + /** + * Buffer '' sub node in a dedicated RAM dataspace + * + * \throw Ram_session::Alloc_failed + * \throw Rm_session::Attach_failed + */ + Genode::Ram_dataspace_capability + _ram_ds_from_start_node(Genode::Xml_node start, + Genode::Ram_session &ram, Genode::Region_map &rm) + { + /* + * If the start node contains a 'config' entry, we copy this entry + * into a fresh dataspace to be provided to our child. + */ + Genode::Xml_node const config = start.has_sub_node("config") + ? start.sub_node("config") + : Genode::Xml_node(""); + + Genode::Ram_dataspace_capability ram_ds; + try { + /* + * Allocate RAM dataspace that is big enough to hold the + * configuration and the null termination. + */ + ram_ds = ram.alloc(config.size() + 1); + + /* + * Make dataspace locally accessible, copy configuration into the + * dataspace, and append a string-terminating zero. + */ + Genode::Attached_dataspace attached(rm, ram_ds); + + Genode::memcpy(attached.local_addr(), + config.addr(), config.size()); + + attached.local_addr()[config.size()] = 0; + + return ram_ds; + } + catch (Genode::Region_map::Attach_failed) { ram.free(ram_ds); throw; } + } public: @@ -40,84 +89,31 @@ class Init::Child_config * * The provided RAM session is used to obtain a dataspace for * holding the copy of the child's configuration data unless the - * configuration is supplied via a config file. Normally, the - * child's RAM session should be used to account the consumed RAM - * quota to the child. + * configuration is supplied via a config ROM module. + * + * \throw Ram_session::Alloc_failed failed to allocate the backing + * store for holding config data + * + * \throw Region_map::Attach_failed failed to temporarily attach the + * config dataspace to the local + * address space + * + * If the start node contains a 'filename' entry, we only keep the + * information about the ROM module name. */ - Child_config(Genode::Ram_session_capability ram_session, - Genode::Xml_node start_node) - : _ram_session_cap(ram_session) - { - using namespace Genode; - - /* - * If the start node contains a 'filename' entry, we only keep - * the information about the file name. - */ - _filename[0] = 0; - try { - Xml_node configfile_node = start_node.sub_node("configfile"); - configfile_node.attribute("name") - .value(_filename, sizeof(_filename)); - - return; - } catch (...) { } - - /* - * If the start node contains a 'config' entry, we copy this - * entry into a fresh dataspace to be provided to our child. - */ - Ram_session_client rsc(_ram_session_cap); - try { - Xml_node config_node = start_node.sub_node("config"); - - const char *config = config_node.addr(); - Genode::size_t config_size = config_node.size(); - - if (!config || !config_size) return; - - /* - * Allocate RAM dataspace that is big enough to - * hold the configuration and the null termination. - */ - _config_ram_ds = rsc.alloc(config_size + 1); - - /* - * Make dataspace locally accessible, copy - * configuration into the dataspace, and append - * a string-terminating zero. - */ - void *addr = env()->rm_session()->attach(_config_ram_ds); - - Genode::memcpy(addr, config, config_size); - static_cast(addr)[config_size] = 0; - env()->rm_session()->detach(addr); - - } catch (Region_map::Attach_failed) { - rsc.free(_config_ram_ds); - return; - } catch (Ram_session::Alloc_failed) { - return; - } catch (Xml_node::Nonexistent_sub_node) { } - } + Child_config(Genode::Ram_session &ram, Genode::Region_map &local_rm, + Genode::Xml_node start) + : + _ram(ram), + _rom_name(_rom_name_from_start_node(start)), + _ram_ds(_rom_name.valid() ? Genode::Ram_dataspace_capability() + : _ram_ds_from_start_node(start, ram, local_rm)) + { } /** * Destructor */ - ~Child_config() - { - using namespace Genode; - - /* - * The configuration data is either provided as a ROM session - * (holding a complete configfile) or as a RAM dataspace - * holding a copy of the start node's config entry. In the - * latter case, the child's configuration resides in a - * shadow copy kept in '_config_ram_ds'. - */ - if (_config_ram_ds.valid()) - Ram_session_client(_ram_session_cap).free(_config_ram_ds); - } + ~Child_config() { if (_ram_ds.valid()) _ram.free(_ram_ds); } /** * Return file name if configuration comes from a file @@ -125,7 +121,7 @@ class Init::Child_config * If the configuration is provided inline, the method returns 0. */ char const *filename() const { - return _filename[0] != 0 ? _filename : 0; } + return _rom_name.valid() ? _rom_name.string() : nullptr; } /** * Request dataspace holding the start node's configuration data @@ -134,7 +130,7 @@ class Init::Child_config * inline configuration (if 'filename()' returns 0). */ Genode::Dataspace_capability dataspace() { - return Genode::Dataspace_capability(_config_ram_ds); } + return Genode::Dataspace_capability(_ram_ds); } }; #endif /* _INCLUDE__INIT__CHILD_CONFIG_H_ */ diff --git a/repos/os/include/init/child_policy.h b/repos/os/include/init/child_policy.h index d89920c20..928b0a14f 100644 --- a/repos/os/include/init/child_policy.h +++ b/repos/os/include/init/child_policy.h @@ -19,9 +19,11 @@ #include #include #include +#include #include #include #include +#include namespace Init { @@ -29,8 +31,17 @@ namespace Init { class Child_policy_enforce_labeling; class Child_policy_handle_cpu_priorities; class Child_policy_provide_rom_file; + class Child_policy_provide_dynamic_rom; class Child_policy_redirect_rom_file; class Traditional_child_policy; + + using Genode::Attached_ram_dataspace; + using Genode::size_t; + using Genode::Session_label; + using Genode::error; + using Genode::log; + using Genode::Service; + using Genode::Session_state; } @@ -154,80 +165,49 @@ class Init::Child_policy_provide_rom_file struct Local_rom_session_component : Genode::Rpc_object { + Genode::Rpc_entrypoint &ep; Genode::Dataspace_capability ds_cap; /** * Constructor */ - Local_rom_session_component(Genode::Dataspace_capability ds) - : ds_cap(ds) { } + Local_rom_session_component(Genode::Rpc_entrypoint &ep, + Genode::Dataspace_capability ds) + : ep(ep), ds_cap(ds) { ep.manage(this); } + + ~Local_rom_session_component() { ep.dissolve(this); } /*************************** ** ROM session interface ** ***************************/ - Genode::Rom_dataspace_capability dataspace() { + Genode::Rom_dataspace_capability dataspace() override { return Genode::static_cap_cast(ds_cap); } - void sigh(Genode::Signal_context_capability) { } + void sigh(Genode::Signal_context_capability) override { } - } _local_rom_session; + } _session; - Genode::Rpc_entrypoint *_ep; - Genode::Rom_session_capability _rom_session_cap; + Genode::Session_label const _module_name; - Genode::Session_label _module_name; + typedef Genode::Local_service Service; - struct Local_rom_service : public Genode::Service - { - Genode::Rom_session_capability _rom_cap; - bool _valid; - - /** - * Constructor - * - * \param rom_cap capability to return on session requests - * \param valid true if local rom service is backed by a - * valid dataspace - */ - Local_rom_service(Genode::Rom_session_capability rom_cap, bool valid) - : Genode::Service("ROM"), _rom_cap(rom_cap), _valid(valid) { } - - Genode::Session_capability session(char const * /*args*/, - Genode::Affinity const &) - { - if (!_valid) - throw Invalid_args(); - - return _rom_cap; - } - - void upgrade(Genode::Session_capability, const char * /*args*/) { } - void close(Genode::Session_capability) { } - - } _local_rom_service; + Service::Single_session_factory _session_factory { _session }; + Service _service { _session_factory }; public: /** * Constructor */ - Child_policy_provide_rom_file(const char *module_name, + Child_policy_provide_rom_file(Genode::Session_label const &module_name, Genode::Dataspace_capability ds_cap, Genode::Rpc_entrypoint *ep) : - _local_rom_session(ds_cap), _ep(ep), - _rom_session_cap(_ep->manage(&_local_rom_session)), - _module_name(module_name), - _local_rom_service(_rom_session_cap, ds_cap.valid()) + _session(*ep, ds_cap), _module_name(module_name) { } - /** - * Destructor - */ - ~Child_policy_provide_rom_file() { _ep->dissolve(&_local_rom_session); } - Genode::Service *resolve_session_request(const char *service_name, const char *args) { @@ -238,7 +218,7 @@ class Init::Child_policy_provide_rom_file { Genode::Session_label const label = Genode::label_from_args(args); return label.last_element() == _module_name - ? &_local_rom_service : nullptr; + ? &_service : nullptr; } } }; diff --git a/repos/os/include/os/child_policy_dynamic_rom.h b/repos/os/include/os/child_policy_dynamic_rom.h index 9ce5ee2b6..9b44787ef 100644 --- a/repos/os/include/os/child_policy_dynamic_rom.h +++ b/repos/os/include/os/child_policy_dynamic_rom.h @@ -2,6 +2,9 @@ * \brief Child policy helper for supplying dynamic ROM modules * \author Norman Feske * \date 2012-04-04 + * + * \deprecated use 'Local_service::Single_session_service' combined with + * 'Dynamic_rom_session' instead */ /* @@ -73,7 +76,7 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object, Rpc_entrypoint &ep, Ram_session *ram) : - Service("ROM"), + Service("ROM", Ram_session_capability()), _ram(ram), _fg(0, 0), _bg(0, 0), _bg_has_pending_data(false), @@ -147,11 +150,31 @@ class Genode::Child_policy_dynamic_rom_file : public Rpc_object, ** Service interface ** ***********************/ - Session_capability session(const char *, Affinity const &) { - return _rom_session_cap; } + void initiate_request(Session_state &session) override + { + switch (session.phase) { - void upgrade(Session_capability, const char *) { } - void close(Session_capability) { } + case Session_state::CREATE_REQUESTED: + session.cap = _rom_session_cap; + session.phase = Session_state::AVAILABLE; + break; + + case Session_state::UPGRADE_REQUESTED: + session.phase = Session_state::CAP_HANDED_OUT; + session.confirm_ram_upgrade(); + break; + + case Session_state::CLOSE_REQUESTED: + session.phase = Session_state::CLOSED; + break; + + case Session_state::INVALID_ARGS: + case Session_state::AVAILABLE: + case Session_state::CAP_HANDED_OUT: + case Session_state::CLOSED: + break; + } + } /********************** diff --git a/repos/os/include/os/config.h b/repos/os/include/os/config.h index b330e8afc..6cf0748d1 100644 --- a/repos/os/include/os/config.h +++ b/repos/os/include/os/config.h @@ -26,7 +26,7 @@ namespace Genode { /** * Return singleton instance of config */ - Config *config(); + Volatile_object &config(); } diff --git a/repos/os/include/os/dynamic_rom_session.h b/repos/os/include/os/dynamic_rom_session.h new file mode 100644 index 000000000..55b754f2d --- /dev/null +++ b/repos/os/include/os/dynamic_rom_session.h @@ -0,0 +1,195 @@ +/* + * \brief ROM session implementation for serving dynamic content + * \author Norman Feske + * \date 2016-11-03 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__OS__DYNAMIC_ROM_SESSION_H_ +#define _INCLUDE__OS__DYNAMIC_ROM_SESSION_H_ + +#include +#include +#include +#include +#include + +namespace Genode { class Dynamic_rom_session; } + + +class Genode::Dynamic_rom_session : public Rpc_object +{ + public: + + struct Content_producer + { + class Buffer_capacity_exceeded : Exception { }; + + /** + * Write content into the specified buffer + * + * \throw Buffer_capacity_exceeded + */ + virtual void produce_content(char *dst, size_t dst_len) = 0; + }; + + private: + + /* + * Synchronize calls of 'trigger_update' (called locally) with the + * 'Rom_session' methods (invoked via RPC). + */ + Lock _lock; + + Rpc_entrypoint &_ep; + Ram_session &_ram; + Region_map &_rm; + Signal_context_capability _sigh; + Content_producer &_content_producer; + + /* + * Keep track of the last version handed out to the client (at the + * time of the last 'Rom_session::update' RPC call, and the newest + * version that is available. If the client version is out of date + * when the client registers a signal handler, submit a signal + * immediately. + */ + unsigned _current_version = 0, _client_version = 0; + + size_t _ds_size = 4096; + + Lazy_volatile_object _ds; + + void _notify_client() + { + if (_sigh.valid() && (_current_version != _client_version)) + Signal_transmitter(_sigh).submit(); + } + + bool _unsynchronized_update() + { + bool ds_reallocated = false; + + for (;;) { + try { + if (!_ds.constructed()) { + _ds.construct(_ram, _rm, _ds_size); + ds_reallocated = true; + } + } + catch (Ram_session::Quota_exceeded) { + + error("ouf of child 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; + } + catch (Ram_session::Out_of_metadata) { + error("ouf of RAM session quota while delivering dynamic ROM"); + return true; + } + + try { + _content_producer.produce_content(_ds->local_addr(), + _ds->size()); + _client_version = _current_version; + return !ds_reallocated; + } + catch (Content_producer::Buffer_capacity_exceeded) { + + /* force the re-allocation of a larger buffer */ + _ds.destruct(); + _ds_size *= 2; + } + } + } + + public: + + /** + * Constructor + * + * \param ep entrypoint serving the ROM session + * \param ram RAM session used to allocate the backing + * store for the dataspace handed out to the + * client + * \param rm local region map ('env.rm()') required to + * make the dataspace locally visible to + * populate its content + * \param content_producer callback to generate the content of the + * ROM dataspace + * + * The 'Dynamic_rom_session' associates/disassociates itself with 'ep'. + */ + Dynamic_rom_session(Rpc_entrypoint &ep, + Ram_session &ram, + Region_map &rm, + Content_producer &content_producer) + : + _ep(ep), _ram(ram), _rm(rm), _content_producer(content_producer) + { + _ep.manage(this); + } + + ~Dynamic_rom_session() { _ep.dissolve(this); } + + /* + * Called locally, potentially from another thread than 'ep' + */ + void trigger_update() + { + Lock::Guard guard(_lock); + + _current_version++; + _notify_client(); + } + + + /*************************** + ** ROM session interface ** + ***************************/ + + Rom_dataspace_capability dataspace() override + { + Lock::Guard guard(_lock); + + if (!_ds.constructed()) + _unsynchronized_update(); + + Dataspace_capability ds_cap = _ds->cap(); + + return static_cap_cast(ds_cap); + } + + bool update() override + { + Lock::Guard guard(_lock); + + return _unsynchronized_update(); + } + + void sigh(Signal_context_capability sigh) override + { + Lock::Guard guard(_lock); + + _sigh = sigh; + _notify_client(); + } +}; + +#endif /* _INCLUDE__OS__DYNAMIC_ROM_SESSION_H_ */ + diff --git a/repos/os/include/os/session_requester.h b/repos/os/include/os/session_requester.h new file mode 100644 index 000000000..fda69d1c6 --- /dev/null +++ b/repos/os/include/os/session_requester.h @@ -0,0 +1,84 @@ +/* + * \brief Utility for providing "session_requests" ROM to a child service + * \author Norman Feske + * \date 2016-11-10 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__OS__SESSION_REQUESTER_H_ +#define _INCLUDE__OS__SESSION_REQUESTER_H_ + +#include +#include + +namespace Genode { class Session_requester; } + + +class Genode::Session_requester +{ + private: + + Id_space _id_space; + + struct Content_producer : Dynamic_rom_session::Content_producer + { + Id_space &_id_space; + + Content_producer(Id_space &id_space) + : _id_space(id_space) { } + + void produce_content(char *dst, Genode::size_t dst_len) override + { + Xml_generator xml(dst, dst_len, "session_requests", [&] () { + _id_space.for_each([&] (Session_state const &s) { + s.generate_session_request(xml); }); }); + } + } _content_producer { _id_space }; + + typedef Local_service Service; + + Dynamic_rom_session _session; + Service::Single_session_factory _factory { _session }; + Service _service { _factory }; + + public: + + typedef String<32> Rom_name; + + static Rom_name rom_name() { return "session_requests"; } + + /** + * Constructor + * + * \param ep entrypoint serving the local ROM service + * \param ram backing store for the ROM dataspace + * \param rm local address space, needed to populate the dataspace + */ + Session_requester(Rpc_entrypoint &ep, Ram_session &ram, Region_map &rm) + : + _session(ep, ram, rm, _content_producer) + { } + + /** + * Inform the child about a new version of the "session_requests" ROM + */ + void trigger_update() { _session.trigger_update(); } + + /** + * ID space for sessios requests supplied to the child + */ + Id_space &id_space() { return _id_space; } + + /** + * ROM service providing a single "session_requests" session + */ + Service &service() { return _service; } +}; + +#endif /* _INCLUDE__OS__SESSION_REQUESTER_H_ */ diff --git a/repos/os/include/os/slave.h b/repos/os/include/os/slave.h index 90e4ed1dd..8b02b73ae 100644 --- a/repos/os/include/os/slave.h +++ b/repos/os/include/os/slave.h @@ -17,31 +17,41 @@ /* Genode includes */ #include +#include #include #include -#include -#include -#include -#include #include +#include -namespace Genode { - - class Slave_policy; - class Slave; -} +namespace Genode { struct Slave; } -/** - * Slave-policy class - * - * This class provides a convenience policy for single-service slaves using a - * white list of parent services. - */ -class Genode::Slave_policy : public Genode::Child_policy +struct Genode::Slave { + typedef Session_state::Args Args; + + class Policy; + + template + class Connection_base; + + template + struct Connection; +}; + + +class Genode::Slave::Policy : public Child_policy +{ + public: + + typedef Child_policy::Name Name; + typedef Session_label Label; + protected: + typedef Registered Parent_service; + typedef Registry Parent_services; + /** * Return white list of services the slave is permitted to use * @@ -51,58 +61,66 @@ class Genode::Slave_policy : public Genode::Child_policy private: - char const *_label; - Genode::Service_registry _parent_services; - Genode::Rpc_entrypoint &_entrypoint; - Genode::Rom_connection _binary_rom; + Label const _label; + Binary_name const _binary_name; + Ram_session_client _ram; + Genode::Parent_service _binary_service; + size_t _ram_quota; + Parent_services _parent_services; + Rpc_entrypoint &_ep; Init::Child_policy_enforce_labeling _labeling_policy; - Init::Child_policy_provide_rom_file _binary_policy; - Genode::Child_policy_dynamic_rom_file _config_policy; + Child_policy_dynamic_rom_file _config_policy; - bool _service_permitted(const char *service_name) + bool _service_permitted(Service::Name const &service_name) const { for (const char **s = _permitted_services(); *s; ++s) - if (!Genode::strcmp(service_name, *s)) + if (service_name == *s) return true; return false; } + Session_requester _session_requester; + public: + class Connection; + /** * Slave-policy constructor * - * \param label name of the program to start - * \param entrypoint entrypoint used to provide local services - * such as the config ROM service - * \param ram RAM session used for buffering config data + * \param ep entrypoint used to provide local services + * such as the config ROM service + * \param local_rm local address space, needed to populate dataspaces + * provided to the child (config, session_requests) * - * If 'ram' is set to 0, no configuration can be supplied to the - * slave. + * \throw Ram_session::Alloc_failed by 'Child_policy_dynamic_rom_file' + * \throw Rm_session::Attach_failed by 'Child_policy_dynamic_rom_file' */ - Slave_policy(const char *label, - Genode::Rpc_entrypoint &entrypoint, - Genode::Ram_session *ram = 0, - const char *binary = nullptr) + Policy(Label const &label, + Name const &binary_name, + Rpc_entrypoint &ep, + Region_map &rm, + Ram_session_capability ram_cap, + size_t ram_quota) : - _label(label), - _entrypoint(entrypoint), - _binary_rom(binary ? prefixed_label(Session_label(label), - Session_label(binary)).string() : label), - _labeling_policy(_label), - _binary_policy("binary", _binary_rom.dataspace(), &_entrypoint), - _config_policy("config", _entrypoint, ram) - { } - - Genode::Dataspace_capability binary() { return _binary_rom.dataspace(); } + _label(label), _binary_name(binary_name), _ram(ram_cap), + _binary_service(Rom_session::service_name()), + _ram_quota(ram_quota), _ep(ep), _labeling_policy(_label.string()), + _config_policy("config", _ep, &_ram), + _session_requester(ep, _ram, rm) + { + configure(""); + } /** * Assign new configuration to slave + * + * \param config new configuration as null-terminated string */ void configure(char const *config) { - _config_policy.load(config, Genode::strlen(config) + 1); + _config_policy.load(config, strlen(config) + 1); } void configure(char const *config, size_t len) @@ -110,106 +128,187 @@ class Genode::Slave_policy : public Genode::Child_policy _config_policy.load(config, len); } + void trigger_session_requests() + { + _session_requester.trigger_update(); + } + /**************************** ** Child_policy interface ** ****************************/ - const char *name() const { return _label; } + Name name() const override { return _label; } - Genode::Service *resolve_session_request(const char *service_name, - const char *args) + Binary_name binary_name() const override { return _binary_name; } + + Ram_session &ref_ram() override { return _ram; } + Ram_session_capability ref_ram_cap() const override { return _ram; } + + void init(Ram_session &session, Ram_session_capability cap) override { - Genode::Service *service = 0; - - /* check for binary file request */ - if ((service = _binary_policy.resolve_session_request(service_name, args))) - return service; + session.ref_account(_ram); + _ram.transfer_quota(cap, _ram_quota); + } + Service &resolve_session_request(Service::Name const &service_name, + Session_state::Args const &args) + { /* check for config file request */ - if ((service = _config_policy.resolve_session_request(service_name, args))) - return service; + if (Service *s = _config_policy.resolve_session_request(service_name.string(), args.string())) + return *s; + + if (service_name == "ROM") { + Session_label const rom_name(label_from_args(args.string()).last_element()); + if (rom_name == _binary_name) return _binary_service; + if (rom_name == "session_requests") return _session_requester.service(); + } if (!_service_permitted(service_name)) { - error(name(), ": illegal session request of service \"", service_name, "\""); - return 0; + error(name(), ": illegal session request of " + "service \"", service_name, "\" (", args, ")"); + throw Parent::Service_denied(); } /* fill parent service registry on demand */ - if (!(service = _parent_services.find(service_name))) { - service = new (Genode::env()->heap()) - Genode::Parent_service(service_name); - _parent_services.insert(service); - } + Parent_service *service = nullptr; + _parent_services.for_each([&] (Parent_service &s) { + if (!service && s.name() == service_name) + service = &s; }); - /* return parent service */ - return service; + if (!service) + service = new (env()->heap()) + Parent_service(_parent_services, service_name); + + return *service; } - void filter_session_args(const char *service, - char *args, Genode::size_t args_len) + Id_space &server_id_space() override { + return _session_requester.id_space(); } + + void filter_session_args(Service::Name const &service, + char *args, size_t args_len) { - _labeling_policy.filter_session_args(service, args, args_len); + _labeling_policy.filter_session_args(service.string(), args, args_len); } }; -class Genode::Slave +template +class Genode::Slave::Connection_base { - private: + protected: - struct Resources + /* each connection appears as a separate client */ + Id_space _id_space; + + Policy &_policy; + + struct Service : Genode::Service, Session_state::Ready_callback, + Session_state::Closed_callback { - Genode::Pd_connection pd; - Genode::Ram_connection ram; - Genode::Cpu_connection cpu; + Id_space &_id_space; - class Quota_exceeded : public Genode::Exception { }; + Lock _lock { Lock::LOCKED }; + bool _alive = false; - Resources(const char *label, Genode::size_t ram_quota, - Ram_session_capability ram_ref_cap) - : pd(label), ram(label), cpu(label) + Service(Policy &policy) + : + Genode::Service(CONNECTION::service_name(), policy.ref_ram_cap()), + _id_space(policy.server_id_space()) + { } + + void initiate_request(Session_state &session) override { - ram.ref_account(ram_ref_cap); - Ram_session_client ram_ref(ram_ref_cap); + switch (session.phase) { - if (ram_ref.transfer_quota(ram.cap(), ram_quota)) - throw Quota_exceeded(); + case Session_state::CREATE_REQUESTED: + + if (!session.id_at_server.constructed()) + session.id_at_server.construct(session, _id_space); + + session.ready_callback = this; + session.async_client_notify = true; + break; + + case Session_state::UPGRADE_REQUESTED: + warning("upgrading slaves is not implemented"); + session.phase = Session_state::CAP_HANDED_OUT; + break; + + case Session_state::CLOSE_REQUESTED: + warning("closing slave connections is not implemented"); + session.phase = Session_state::CLOSED; + break; + + case Session_state::INVALID_ARGS: + case Session_state::AVAILABLE: + case Session_state::CAP_HANDED_OUT: + case Session_state::CLOSED: + break; + } } - } _resources; - Genode::Child::Initial_thread _initial_thread; + void wakeup() override { } - Genode::Region_map_client _address_space { _resources.pd.address_space() }; - Genode::Child _child; + /** + * Session_state::Ready_callback + */ + void session_ready(Session_state &session) override + { + _alive = session.alive(); + _lock.unlock(); + } - public: + /** + * Session_state::Closed_callback + */ + void session_closed(Session_state &s) override { _lock.unlock(); } - Slave(Genode::Rpc_entrypoint &entrypoint, - Slave_policy &slave_policy, - Genode::size_t ram_quota, - Ram_session_capability ram_ref_cap = env()->ram_session_cap(), - Dataspace_capability ldso_ds = Dataspace_capability()) + } _service; + + Local_connection _connection; + + Connection_base(Policy &policy, Args const &args, Affinity const &affinity) : - _resources(slave_policy.name(), ram_quota, ram_ref_cap), - _initial_thread(_resources.cpu, _resources.pd, slave_policy.name()), - _child(slave_policy.binary(), ldso_ds, _resources.pd, _resources.pd, - _resources.ram, _resources.ram, _resources.cpu, _initial_thread, - *env()->rm_session(), _address_space, entrypoint, slave_policy) - { } + _policy(policy), _service(_policy), + _connection(_service, _id_space, { 1 }, args, affinity) + { + _policy.trigger_session_requests(); + _service._lock.lock(); + if (!_service._alive) + throw Parent::Service_denied(); + } - Genode::Ram_connection &ram() { return _resources.ram; } + ~Connection_base() + { + _policy.trigger_session_requests(); + _service._lock.lock(); + } + + typedef typename CONNECTION::Session_type SESSION; + + Capability _cap() const { return _connection.cap(); } +}; - /*************************************** - ** Wrappers of the 'Child' interface ** - ***************************************/ - - void yield(Genode::Parent::Resource_args const &args) { - _child.yield(args); } - - void notify_resource_avail() const { - _child.notify_resource_avail(); } +template +struct Genode::Slave::Connection : private Connection_base, + public CONNECTION::Client +{ + /** + * Constructor + * + * \throw Parent::Service_denied parent denies session request + * \throw Parent::Quota_exceeded our own quota does not suffice for + * the creation of the new session + */ + Connection(Slave::Policy &policy, Args const &args, + Affinity const &affinity = Affinity()) + : + Connection_base(policy, args, affinity), + CONNECTION::Client(Connection_base::_cap()) + { } }; #endif /* _INCLUDE__OS__SLAVE_H_ */ diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc index 596c0f3fd..57f26cbf4 100644 --- a/repos/os/src/init/main.cc +++ b/repos/os/src/init/main.cc @@ -1,16 +1,17 @@ /* - * \brief Init process + * \brief Init component * \author Norman Feske * \date 2010-04-27 */ /* - * Copyright (C) 2010-2013 Genode Labs GmbH + * Copyright (C) 2010-2016 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ +#include #include #include #include @@ -65,7 +66,7 @@ inline Genode::Affinity::Space read_affinity_space() /** * Read parent-provided services from config */ -inline void determine_parent_services(Genode::Service_registry *services) +inline void determine_parent_services(Genode::Registry *services) { using namespace Genode; @@ -75,11 +76,10 @@ inline void determine_parent_services(Genode::Service_registry *services) Xml_node node = config()->xml_node().sub_node("parent-provides").sub_node("service"); for (; ; node = node.next("service")) { - char service_name[Genode::Service::MAX_NAME_LEN]; + char service_name[Genode::Service::Name::capacity()]; node.attribute("name").value(service_name, sizeof(service_name)); - Parent_service *s = new (env()->heap()) Parent_service(service_name); - services->insert(s); + new (env()->heap()) Init::Parent_service(*services, service_name); if (Init::config_verbose) log(" service \"", Cstring(service_name), "\""); @@ -231,13 +231,6 @@ class Init::Child_registry : public Name_registry, Child_list return _aliases.first() ? _aliases.first() : 0; } - void revoke_server(Genode::Server const *server) - { - Genode::List_element *curr = first(); - for (; curr; curr = curr->next()) - curr->object()->_child.revoke_server(server); - } - /***************************** ** Name-registry interface ** @@ -260,41 +253,24 @@ class Init::Child_registry : public Name_registry, Child_list return true; } - Genode::Server *lookup_server(const char *name) const + Name deref_alias(Name const &name) override { - /* - * Check if an alias with the specified name exists. If so, - * look up the server referred to by the alias. - */ for (Alias const *a = _aliases.first(); a; a = a->next()) - if (Alias::Name(name) == a->name) - name = a->child.string(); + if (name == a->name) + return a->child; - /* look up child with the name */ - Genode::List_element const *curr = first(); - for (; curr; curr = curr->next()) - if (curr->object()->has_name(name)) - return curr->object()->server(); - - return 0; + return name; } }; -int main(int, char **) +void Component::construct(Genode::Env &env) { using namespace Init; using namespace Genode; - /* obtain dynamic linker */ - Dataspace_capability ldso_ds; - try { - static Rom_connection rom("ld.lib.so"); - ldso_ds = rom.dataspace(); - } catch (...) { } - - static Service_registry parent_services; - static Service_registry child_services; + static Registry parent_services; + static Registry child_services; static Child_registry children; static Cap_connection cap; @@ -306,7 +282,7 @@ int main(int, char **) Signal_context sig_ctx_res_avail; config()->sigh(sig_rec.manage(&sig_ctx_config)); /* prevent init to block for resource upgrades (never satisfied by core) */ - env()->parent()->resource_avail_sigh(sig_rec.manage(&sig_ctx_res_avail)); + Genode::env()->parent()->resource_avail_sigh(sig_rec.manage(&sig_ctx_res_avail)); for (;;) { @@ -327,7 +303,7 @@ int main(int, char **) config()->xml_node().for_each_sub_node("alias", [&] (Xml_node alias_node) { try { - children.insert_alias(new (env()->heap()) Alias(alias_node)); + children.insert_alias(new (Genode::env()->heap()) Alias(alias_node)); } catch (Alias::Name_is_missing) { warning("missing 'name' attribute in '' entry"); } @@ -341,12 +317,11 @@ int main(int, char **) config()->xml_node().for_each_sub_node("start", [&] (Xml_node start_node) { try { - children.insert(new (env()->heap()) - Init::Child(start_node, default_route_node, + children.insert(new (Genode::env()->heap()) + Init::Child(env, start_node, default_route_node, children, read_prio_levels(), read_affinity_space(), - parent_services, child_services, cap, - ldso_ds)); + parent_services, child_services)); } catch (Rom_connection::Rom_connection_failed) { /* @@ -354,6 +329,11 @@ int main(int, char **) * by the Rom_connection constructor. */ } + catch (Ram_session::Alloc_failed) { + warning("failed to allocate memory during child construction"); } + catch (Region_map::Attach_failed) { + warning("failed to attach dataspace to local address space " + "during child construction"); } }); /* start children */ @@ -386,35 +366,22 @@ int main(int, char **) while (children.any()) { Init::Child *child = children.any(); children.remove(child); - Genode::Server const *server = child->server(); - destroy(env()->heap(), child); - - /* - * The killed child may have provided services to other children. - * Since the server is dead by now, we cannot close its sessions - * in the cooperative way. Instead, we need to instruct each - * other child to forget about session associated with the dead - * server. Note that the 'child' pointer points a a no-more - * existing object. It is only used to identify the corresponding - * session. It must never by de-referenced! - */ - children.revoke_server(server); + destroy(Genode::env()->heap(), child); } /* remove all known aliases */ while (children.any_alias()) { Init::Alias *alias = children.any_alias(); children.remove_alias(alias); - destroy(env()->heap(), alias); + destroy(Genode::env()->heap(), alias); } /* reset knowledge about parent services */ - parent_services.remove_all(); + parent_services.for_each([&] (Init::Parent_service &service) { + destroy(Genode::env()->heap(), &service); }); /* reload config */ try { config()->reload(); } catch (...) { } } - - return 0; } diff --git a/repos/os/src/lib/config/config.cc b/repos/os/src/lib/config/config.cc index dfca1a492..51b34f4bd 100644 --- a/repos/os/src/lib/config/config.cc +++ b/repos/os/src/lib/config/config.cc @@ -50,7 +50,7 @@ void Config::reload() _config_xml = _config_xml_node(_config_ds); } catch (Genode::Xml_node::Invalid_syntax) { - Genode::error("Config file has invalid syntax"); + Genode::error("config ROM has invalid syntax"); _config_xml = fallback_config_xml(); } } @@ -80,13 +80,13 @@ Config::Config() { } -Config *Genode::config() +Volatile_object &Genode::config() { static bool config_failed = false; if (!config_failed) { try { - static Config config_inst; - return &config_inst; + static Volatile_object config_inst; + return config_inst; } catch (Genode::Rom_connection::Rom_connection_failed) { Genode::error("Could not obtain config file"); } catch (Genode::Xml_node::Invalid_syntax) { @@ -97,6 +97,7 @@ Config *Genode::config() } /* do not try again to construct 'config_inst' */ config_failed = true; - return 0; + class Config_construction_failed : Genode::Exception { }; + throw Config_construction_failed(); }