diff --git a/repos/os/include/sandbox/child.h b/repos/os/include/sandbox/child.h index 8a7d7303be..d31f9840c4 100644 --- a/repos/os/include/sandbox/child.h +++ b/repos/os/include/sandbox/child.h @@ -31,231 +31,276 @@ #include #include -namespace Sandbox { class Child; } +namespace Sandbox +{ + class Child; +} class Sandbox::Child : Child_policy, Routed_service::Wakeup { - public: - /** - * Resources assigned to the child - */ - struct Resources - { - long prio_levels_log2; - long priority; - Affinity affinity; - Ram_quota assigned_ram_quota; - Cap_quota assigned_cap_quota; - Cpu_quota assigned_cpu_quota; +public: + using Version = String<80>; - Ram_quota effective_ram_quota() const + /** + * Exception types + */ + struct Child_name_is_not_unique : Exception + { + }; + struct Missing_name_attribute : Exception + { + }; + + /** + * Unique ID of the child, solely used for diagnostic purposes + */ + struct Id + { + unsigned value; + }; + + struct Default_route_accessor : Interface + { + virtual Xml_node default_route() = 0; + }; + struct Default_caps_accessor : Interface + { + virtual Cap_quota default_caps() = 0; + }; + + template + struct Resource_limit_accessor : Interface + { + /* + * The argument is unused. It exists solely as an overload selector. + */ + virtual QUOTA resource_limit(QUOTA const &) const = 0; + }; + + using Ram_limit_accessor = Resource_limit_accessor; + using Cap_limit_accessor = Resource_limit_accessor; + using Cpu_limit_accessor = Resource_limit_accessor; + + struct Cpu_quota_transfer : Interface + { + virtual void transfer_cpu_quota(Capability, Pd_session &, + Capability, Cpu_quota) = 0; + }; + + enum class Sample_state_result + { + CHANGED, + UNCHANGED + }; + + /* + * Helper for passing lambda functions as 'Pd_intrinsics::Fn' + */ + + using Pd_intrinsics = Genode::Sandbox::Pd_intrinsics; + + template + static void with_pd_intrinsics(Pd_intrinsics &pd_intrinsics, + Capability cap, PD_SESSION &pd, + FN const &fn) + { + struct Impl : Pd_intrinsics::Fn + { + using Intrinsics = Pd_intrinsics::Intrinsics; + + FN const &_fn; + Impl(FN const &fn) : _fn(fn) {} + void call(Intrinsics &intrinsics) const override { _fn(intrinsics); } + }; + + pd_intrinsics.with_intrinsics(cap, pd, Impl{fn}); + } + +private: + friend class Child_registry; + + Env &_env; + + Allocator &_alloc; + + Verbose const &_verbose; + + Id const _id; + + enum class State + { + + /* + * States modelling the child's boostrap phase + */ + INITIAL, + RAM_INITIALIZED, + ALIVE, + + /* + * The child is present in the config model but its bootstrapping + * permanently failed. + */ + STUCK, + + /* + * The child must be restarted because a fundamental dependency + * changed. While the child is in this state, it is still + * referenced by the config model. + */ + RESTART_SCHEDULED, + + /* + * The child is no longer referenced by config model and can + * safely be destructed. + */ + ABANDONED + }; + + State _state = State::INITIAL; + + Report_update_trigger &_report_update_trigger; + + List_element _list_element; + + Reconstructible _start_node; + + Constructible _route_model{}; + + void _construct_route_model_from_start_node(Xml_node const &start) + { + _route_model.destruct(); + + start.with_sub_node( + "route", + [&](Xml_node const &route) + { _route_model.construct(_alloc, route); }, + [&]() + { _route_model.construct(_alloc, _default_route_accessor.default_route()); }); + } + + /* + * Version attribute of the start node, used to force child restarts. + */ + Version _version{_start_node->xml().attribute_value("version", Version())}; + + bool _uncertain_dependencies = false; + + /* + * True if the binary is loaded with ld.lib.so + */ + bool const _use_ld = _start_node->xml().attribute_value("ld", true); + + Default_route_accessor &_default_route_accessor; + Default_caps_accessor &_default_caps_accessor; + Ram_limit_accessor &_ram_limit_accessor; + Cap_limit_accessor &_cap_limit_accessor; + Cpu_limit_accessor &_cpu_limit_accessor; + Cpu_quota_transfer &_cpu_quota_transfer; + + Name_registry &_name_registry; + + /** + * Read name from XML and check for name confict with other children + * + * \throw Missing_name_attribute + */ + static Name _name_from_xml(Xml_node start_node) + { + Name const name = start_node.attribute_value("name", Name()); + if (name.valid()) + return name; + + warning("missing 'name' attribute in '' entry"); + throw Missing_name_attribute(); + } + + using Name = String<64>; + Name const _unique_name{_name_from_xml(_start_node->xml())}; + + static Binary_name _binary_from_xml(Xml_node start_node, + Name const &unique_name) + { + if (!start_node.has_sub_node("binary")) + return unique_name; + + return start_node.sub_node("binary").attribute_value("name", Name()); + } + + /* updated on configuration update */ + Binary_name _binary_name{_binary_from_xml(_start_node->xml(), _unique_name)}; + + /* initialized in constructor, updated by 'apply_config' */ + bool _heartbeat_enabled; + + /* + * Number of skipped heartbeats when last checked + * + * This variable is used for the triggering of state-report updates + * due to heartbeat events. + */ + unsigned _last_skipped_heartbeats = 0; + + /* return true if heartbeat tracking is active */ + bool _heartbeat_expected() const + { + /* don't expect heartbeats from a child that is not yet complete */ + return _heartbeat_enabled && (_state == State::ALIVE); + } + + /** + * Resources assigned to the child + */ + struct Resources + { + long prio_levels_log2; + long priority; + Genode::Affinity affinity; + Ram_quota assigned_ram_quota; + Cap_quota assigned_cap_quota; + Cpu_quota assigned_cpu_quota; + + Ram_quota effective_ram_quota() const + { + return Genode::Child::effective_quota(assigned_ram_quota); + } + + Cap_quota effective_cap_quota() const + { + /* capabilities consumed by 'Genode::Child' */ + Cap_quota const effective = + Genode::Child::effective_quota(assigned_cap_quota); + + /* capabilities additionally consumed by init */ + enum { - return Genode::Child::effective_quota(assigned_ram_quota); - } + STATIC_COSTS = 1 /* possible heap backing-store + allocation for session object */ + + 1 /* buffered XML start node */ + + 2 /* dynamic ROM for config */ + + 2 /* dynamic ROM for session requester */ + }; - Cap_quota effective_cap_quota() const - { - /* capabilities consumed by 'Genode::Child' */ - Cap_quota const effective = - Genode::Child::effective_quota(assigned_cap_quota); + if (effective.value < STATIC_COSTS) + return Cap_quota{0}; - /* capabilities additionally consumed by init */ - enum { - STATIC_COSTS = 1 /* possible heap backing-store - allocation for session object */ - + 1 /* buffered XML start node */ - + 2 /* dynamic ROM for config */ - + 2 /* dynamic ROM for session requester */ - }; - - if (effective.value < STATIC_COSTS) - return Cap_quota{0}; - - return Cap_quota{effective.value - STATIC_COSTS}; - } - }; - - typedef String<80> Version; - - /** - * Exception types - */ - struct Child_name_is_not_unique : Exception { }; - struct Missing_name_attribute : Exception { }; - - /** - * Unique ID of the child, solely used for diagnostic purposes - */ - struct Id { unsigned value; }; - - struct Default_route_accessor : Interface { virtual Xml_node default_route() = 0; }; - struct Default_caps_accessor : Interface { virtual Cap_quota default_caps() = 0; }; - - template - struct Resource_limit_accessor : Interface - { - /* - * The argument is unused. It exists solely as an overload selector. - */ - virtual QUOTA resource_limit(QUOTA const &) const = 0; - }; - - typedef Resource_limit_accessor Ram_limit_accessor; - typedef Resource_limit_accessor Cap_limit_accessor; - typedef Resource_limit_accessor Cpu_limit_accessor; - - struct Cpu_quota_transfer : Interface - { - virtual void transfer_cpu_quota(Cpu_session_capability, Cpu_quota) = 0; - }; - - enum class Sample_state_result { CHANGED, UNCHANGED }; - - protected: - - friend class Child_registry; - - Env &_env; - - Allocator &_alloc; - - Verbose const &_verbose; - - Id const _id; - - enum class State { - - /* - * States modelling the child's boostrap phase - */ - INITIAL, RAM_INITIALIZED, ALIVE, - - /* - * The child is present in the config model but its bootstrapping - * permanently failed. - */ - STUCK, - - /* - * The child must be restarted because a fundamental dependency - * changed. While the child is in this state, it is still - * referenced by the config model. - */ - RESTART_SCHEDULED, - - /* - * The child is no longer referenced by config model and can - * safely be destructed. - */ - ABANDONED, - - }; - - State _state = State::INITIAL; - - Report_update_trigger &_report_update_trigger; - - List_element _list_element; - - Reconstructible _start_node; - - Constructible _route_model { }; - - void _construct_route_model_from_start_node(Xml_node const &start) - { - _route_model.destruct(); - - start.with_sub_node("route", - [&] (Xml_node const &route) { - _route_model.construct(_alloc, route); }, - [&] () { - _route_model.construct(_alloc, _default_route_accessor.default_route()); }); + return Cap_quota{effective.value - STATIC_COSTS}; } + }; - /* - * Version attribute of the start node, used to force child restarts. - */ - Version _version { _start_node->xml().attribute_value("version", Version()) }; + static Resources _resources_from_start_node(Xml_node start_node, Prio_levels prio_levels, + Affinity::Space const &affinity_space, + Cap_quota default_cap_quota) + { + unsigned cpu_percent = 0; + Number_of_bytes ram_bytes = 0; - bool _uncertain_dependencies = false; + size_t caps = start_node.attribute_value("caps", default_cap_quota.value); - /* - * True if the binary is loaded with ld.lib.so - */ - bool const _use_ld = _start_node->xml().attribute_value("ld", true); + start_node.for_each_sub_node("resource", [&](Xml_node rsc) + { - Default_route_accessor &_default_route_accessor; - Default_caps_accessor &_default_caps_accessor; - Ram_limit_accessor &_ram_limit_accessor; - Cap_limit_accessor &_cap_limit_accessor; - Cpu_limit_accessor &_cpu_limit_accessor; - Cpu_quota_transfer &_cpu_quota_transfer; - - Name_registry &_name_registry; - - /** - * Read name from XML and check for name confict with other children - * - * \throw Missing_name_attribute - */ - static Name _name_from_xml(Xml_node start_node) - { - Name const name = start_node.attribute_value("name", Name()); - if (name.valid()) - return name; - - warning("missing 'name' attribute in '' entry"); - throw Missing_name_attribute(); - } - - typedef String<64> Name; - Name const _unique_name { _name_from_xml(_start_node->xml()) }; - - static Binary_name _binary_from_xml(Xml_node start_node, - Name const &unique_name) - { - if (!start_node.has_sub_node("binary")) - return unique_name; - - return start_node.sub_node("binary").attribute_value("name", Name()); - } - - /* updated on configuration update */ - Binary_name _binary_name { _binary_from_xml(_start_node->xml(), _unique_name) }; - - /* initialized in constructor, updated by 'apply_config' */ - bool _heartbeat_enabled; - - /* - * Number of skipped heartbeats when last checked - * - * This variable is used for the triggering of state-report updates - * due to heartbeat events. - */ - unsigned _last_skipped_heartbeats = 0; - - /* return true if heartbeat tracking is active */ - bool _heartbeat_expected() const - { - /* don't expect heartbeats from a child that is not yet complete */ - return _heartbeat_enabled && (_state == State::ALIVE); - } - - - static - Resources _resources_from_start_node(Xml_node start_node, Prio_levels prio_levels, - Affinity::Space const &affinity_space, - Affinity::Location const &location, - Cap_quota default_cap_quota) - { - unsigned cpu_percent = 0; - Number_of_bytes ram_bytes = 0; - - size_t caps = start_node.attribute_value("caps", default_cap_quota.value); - - start_node.for_each_sub_node("resource", [&] (Xml_node rsc) { - - typedef String<8> Name; + using Name = String<8>; Name const name = rsc.attribute_value("name", Name()); if (name == "RAM") @@ -265,60 +310,69 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup cpu_percent = rsc.attribute_value("quantum", 0U); if (name == "CAP") - caps = rsc.attribute_value("quantum", 0UL); - }); + caps = rsc.attribute_value("quantum", 0UL); }); - return Resources { log2(prio_levels.value), - priority_from_xml(start_node, prio_levels), - Affinity(affinity_space, - (location.xpos() == -1 ? affinity_location_from_xml(affinity_space, start_node) : location)), - //affinity_location_from_xml(affinity_space, start_node)), - Ram_quota { ram_bytes }, - Cap_quota { caps }, - Cpu_quota { cpu_percent } }; - } + return Resources{log2(prio_levels.value), + priority_from_xml(start_node, prio_levels), + Genode::Affinity(affinity_space, + affinity_location_from_xml(affinity_space, start_node)), + Ram_quota{ram_bytes}, + Cap_quota{caps}, + Cpu_quota{cpu_percent}}; + } - Resources _resources; + Resources _resources; - Ram_quota _configured_ram_quota() const; - Cap_quota _configured_cap_quota() const; + Ram_quota _configured_ram_quota() const; + Cap_quota _configured_cap_quota() const; - using Local_service = Genode::Sandbox::Local_service_base; + Pd_intrinsics &_pd_intrinsics; - Registry &_parent_services; - Registry &_child_services; - Registry &_local_services; + template + void _with_pd_intrinsics(FN const &fn) + { + with_pd_intrinsics(_pd_intrinsics, _child.pd_session_cap(), _child.pd(), fn); + } - struct Inline_config_rom_service : Abandonable, Dynamic_rom_session::Content_producer + Capability _ref_pd_cap{}; /* defined by 'init' */ + + using Local_service = Genode::Sandbox::Local_service_base; + + Registry &_parent_services; + Registry &_child_services; + Registry &_local_services; + + struct Inline_config_rom_service : Abandonable, Dynamic_rom_session::Content_producer + { + using Service = Genode::Local_service; + + Child &_child; + + Dynamic_rom_session _session{_child._env.ep().rpc_ep(), + _child._env.ram(), _child._env.rm(), + *this}; + + Service::Single_session_factory _factory{_session}; + Service _service{_factory}; + + Inline_config_rom_service(Child &child) : _child(child) {} + + /** + * Dynamic_rom_session::Content_producer interface + */ + void produce_content(char *dst, Genode::size_t dst_len) override { - typedef Genode::Local_service Service; + Xml_node config = _child._start_node->xml().has_sub_node("config") + ? _child._start_node->xml().sub_node("config") + : Xml_node(""); - Child &_child; + size_t const config_len = config.size(); - Dynamic_rom_session _session { _child._env.ep().rpc_ep(), - _child.ref_pd(), _child._env.rm(), - *this }; + if (config_len + 1 /* null termination */ >= dst_len) + throw Buffer_capacity_exceeded(); - Service::Single_session_factory _factory { _session }; - Service _service { _factory }; - - Inline_config_rom_service(Child &child) : _child(child) { } - - /** - * Dynamic_rom_session::Content_producer interface - */ - void produce_content(char *dst, Genode::size_t dst_len) override - { - Xml_node config = _child._start_node->xml().has_sub_node("config") - ? _child._start_node->xml().sub_node("config") - : Xml_node(""); - - size_t const config_len = config.size(); - - if (config_len + 1 /* null termination */ >= dst_len) - throw Buffer_capacity_exceeded(); - - config.with_raw_node([&] (char const *start, size_t length) { + config.with_raw_node([&](char const *start, size_t length) + { /* * The 'length' is the number of bytes of the config-node @@ -329,419 +383,459 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup * thereby include the last actual config-content character * in the result. */ - copy_cstring(dst, start, length + 1); - }); - } - - void trigger_update() { _session.trigger_update(); } - - Service &service() { return _service; } - }; - - Constructible _config_rom_service { }; - - Session_requester _session_requester; - - /** - * CPU-session priority parameters - */ - long const _prio_levels_log2 { _resources.prio_levels_log2 }; - long const _priority { _resources.priority }; - - Cpu_quota const _effective_cpu_quota { - min(_cpu_limit_accessor.resource_limit(Cpu_quota{}).percent, - _resources.assigned_cpu_quota.percent) }; - - /** - * If set to true, the child is allowed to do system management, - * e.g., constrain physical RAM allocations. - */ - bool const _managing_system { - _start_node->xml().attribute_value("managing_system", false) }; - - /** - * Resource request initiated by the child - */ - struct Requested_resources - { - Ram_quota const ram; - Cap_quota const caps; - - Requested_resources(Parent::Resource_args const &args) - : - ram (ram_quota_from_args(args.string())), - caps(cap_quota_from_args(args.string())) - { } - }; - - Constructible _requested_resources { }; - - Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), *this }; - - struct Pd_accessor : Routed_service::Pd_accessor - { - Genode::Child &_child; - - Pd_accessor(Genode::Child &child) : _child(child) { } - - Pd_session &pd() override { return _child.pd(); } - Pd_session_capability pd_cap() const override { return _child.pd_session_cap(); } - - } _pd_accessor { _child }; - - struct Ram_accessor : Routed_service::Ram_accessor - { - Genode::Child &_child; - - Ram_accessor(Genode::Child &child) : _child(child) { } - - Pd_session &ram() override { return _child.pd(); } - Pd_session_capability ram_cap() const override { return _child.pd_session_cap(); } - - } _ram_accessor { _child }; - - /** - * Async_service::Wakeup callback - */ - void wakeup_async_service() override - { - _session_requester.trigger_update(); + copy_cstring(dst, start, length + 1); }); } - enum class Route_state { VALID, MISMATCH, UNAVAILABLE }; + void trigger_update() { _session.trigger_update(); } - /** - * Return true if the policy results in the current route of the session - * - * This method is used to check if a policy change affects an existing - * client session of a child, i.e., to determine whether the child must - * be restarted. - */ - Route_state _route_valid(Session_state const &session) + Service &service() { return _service; } + }; + + Constructible _config_rom_service{}; + + Session_requester _session_requester; + + /** + * CPU-session priority parameters + */ + long const _prio_levels_log2{_resources.prio_levels_log2}; + long const _priority{_resources.priority}; + + Cpu_quota const _effective_cpu_quota{ + min(_cpu_limit_accessor.resource_limit(Cpu_quota{}).percent, + _resources.assigned_cpu_quota.percent)}; + + /** + * If set to true, the child is allowed to do system management, + * e.g., constrain physical RAM allocations. + */ + bool const _managing_system{ + _start_node->xml().attribute_value("managing_system", false)}; + + /** + * Resource request initiated by the child + */ + struct Requested_resources + { + Ram_quota const ram; + Cap_quota const caps; + + Requested_resources(Parent::Resource_args const &args) + : ram(ram_quota_from_args(args.string())), + caps(cap_quota_from_args(args.string())) { - try { - Route const route = - resolve_session_request(session.service().name(), - session.client_label(), - session.diag()); - - bool const valid = (session.service() == route.service) - && (route.label == session.label()); - - return valid ? Route_state::VALID : Route_state::MISMATCH; - } - catch (Service_denied) { return Route_state::UNAVAILABLE; } } + }; - static Xml_node _provides_sub_node(Xml_node start_node) + Constructible _requested_resources{}; + + Genode::Child _child{_env.rm(), _env.ep().rpc_ep(), *this}; + + struct Pd_accessor : Routed_service::Pd_accessor + { + Genode::Child &_child; + + Pd_accessor(Genode::Child &child) : _child(child) {} + + Pd_session &pd() override { return _child.pd(); } + Pd_session_capability pd_cap() const override { return _child.pd_session_cap(); } + + } _pd_accessor{_child}; + + struct Ram_accessor : Routed_service::Ram_accessor + { + Genode::Child &_child; + + Ram_accessor(Genode::Child &child) : _child(child) {} + + Pd_session &ram() override { return _child.pd(); } + Pd_session_capability ram_cap() const override { return _child.pd_session_cap(); } + + } _ram_accessor{_child}; + + /** + * Async_service::Wakeup callback + */ + void wakeup_async_service() override + { + _session_requester.trigger_update(); + } + + enum class Route_state + { + VALID, + MISMATCH, + UNAVAILABLE + }; + + /** + * Return true if the policy results in the current route of the session + * + * This method is used to check if a policy change affects an existing + * client session of a child, i.e., to determine whether the child must + * be restarted. + */ + Route_state _route_valid(Session_state const &session) + { + try { - return start_node.has_sub_node("provides") - ? start_node.sub_node("provides") : Xml_node(""); + Route const route = + resolve_session_request(session.service().name(), + session.client_label(), + session.diag()); + + bool const valid = (session.service() == route.service) && (route.label == session.label()); + + return valid ? Route_state::VALID : Route_state::MISMATCH; } - - /** - * Return true if service is provided by this child - */ - bool _provided_by_this(Routed_service const &service) const + catch (Service_denied) { - return service.has_id_space(_session_requester.id_space()); + return Route_state::UNAVAILABLE; } + } - /** - * Return true if service of specified sub node is known - */ - bool _service_exists(Xml_node node) const - { - bool exists = false; - _child_services.for_each([&] (Routed_service const &service) { + static Xml_node _provides_sub_node(Xml_node start_node) + { + return start_node.has_sub_node("provides") + ? start_node.sub_node("provides") + : Xml_node(""); + } + + /** + * Return true if service is provided by this child + */ + bool _provided_by_this(Routed_service const &service) const + { + return service.has_id_space(_session_requester.id_space()); + } + + /** + * Return true if service of specified sub node is known + */ + bool _service_exists(Xml_node node) const + { + bool exists = false; + _child_services.for_each([&](Routed_service const &service) + { if (_provided_by_this(service) && service.name() == node.attribute_value("name", Service::Name())) exists = true; }); - return exists && !abandoned() && !restart_scheduled(); + return exists && !abandoned() && !restart_scheduled(); + } + + void _add_service(Xml_node service) + { + Service::Name const name = + service.attribute_value("name", Service::Name()); + + if (_verbose.enabled()) + log(" provides service ", name); + + new (_alloc) + Routed_service(_child_services, this->name(), + _pd_accessor, _ram_accessor, + _session_requester.id_space(), + _child.session_factory(), + name, *this); + } + + /* + * Exit state of the child set when 'exit()' is executed + * and reported afterwards through the state report. + */ + + bool _exited{false}; + int _exit_value{-1}; + + /** + * Return true if it's safe to call the PD for requesting resource + * information + */ + bool _pd_alive() const + { + return !abandoned() && !restart_scheduled() && !_exited; + } + + void _destroy_services(); + + struct Sampled_state + { + Ram_info ram; + Cap_info caps; + + static Sampled_state from_pd(Pd_session &pd) + { + return {.ram = Ram_info::from_pd(pd), + .caps = Cap_info::from_pd(pd)}; } - void _add_service(Xml_node service) + bool operator!=(Sampled_state const &other) const { - Service::Name const name = - service.attribute_value("name", Service::Name()); - - if (_verbose.enabled()) - log(" provides service ", name); - - new (_alloc) - Routed_service(_child_services, this->name(), - _pd_accessor, _ram_accessor, - _session_requester.id_space(), - _child.session_factory(), - name, *this); + return (ram != other.ram) || (caps != other.caps); } - /* - * Exit state of the child set when 'exit()' is executed - * and reported afterwards through the state report. - */ + } _sampled_state{}; - bool _exited { false }; - int _exit_value { -1 }; - - /** - * Return true if it's safe to call the PD for requesting resource - * information - */ - bool _pd_alive() const - { - return !abandoned() && !restart_scheduled() && !_exited; - } - - void _destroy_services(); - - struct Sampled_state - { - Ram_info ram; - Cap_info caps; - - static Sampled_state from_pd(Pd_session &pd) - { - return { .ram = Ram_info::from_pd(pd), - .caps = Cap_info::from_pd(pd) }; - } - - bool operator != (Sampled_state const &other) const - { - return (ram != other.ram) || (caps != other.caps); - } - - } _sampled_state { }; - - void _abandon_services() - { - _child_services.for_each([&] (Routed_service &service) { + void _abandon_services() + { + _child_services.for_each([&](Routed_service &service) + { if (service.has_id_space(_session_requester.id_space())) service.abandon(); }); - } + } - void _schedule_restart() + void _schedule_restart() + { + _state = State::RESTART_SCHEDULED; + _abandon_services(); + } + +public: + /** + * Constructor + * + * \param alloc allocator solely used for configuration- + * dependent allocations. It is not used for + * allocations on behalf of the child's + * behavior. + * + * \param ram_limit_accessor interface for querying the available + * RAM, used for dynamic RAM balancing at + * runtime. + * + * \throw Allocator::Out_of_memory could not buffer the XML start node + */ + Child(Env &env, + Allocator &alloc, + Verbose const &verbose, + Id id, + Report_update_trigger &report_update_trigger, + Xml_node start_node, + Default_route_accessor &default_route_accessor, + Default_caps_accessor &default_caps_accessor, + Name_registry &name_registry, + Ram_limit_accessor &ram_limit_accessor, + Cap_limit_accessor &cap_limit_accessor, + Cpu_limit_accessor &cpu_limit_accessor, + Cpu_quota_transfer &cpu_quota_transfer, + Prio_levels prio_levels, + Affinity::Space const &affinity_space, + Registry &parent_services, + Registry &child_services, + Registry &local_services, + Pd_intrinsics &pd_intrinsics); + + virtual ~Child(); + + /** + * Return true if the child has the specified name + */ + bool has_name(Child_policy::Name const &str) const { return str == name(); } + + bool has_version(Version const &version) const { return version == _version; } + + Ram_quota ram_quota() const { return _resources.assigned_ram_quota; } + Cap_quota cap_quota() const { return _resources.assigned_cap_quota; } + Cpu_quota cpu_quota() const { return _effective_cpu_quota; } + + void try_start() + { + if (_state == State::INITIAL) { - _state = State::RESTART_SCHEDULED; - _abandon_services(); - } - - public: - - /** - * Constructor - * - * \param alloc allocator solely used for configuration- - * dependent allocations. It is not used for - * allocations on behalf of the child's - * behavior. - * - * \param ram_limit_accessor interface for querying the available - * RAM, used for dynamic RAM balancing at - * runtime. - * - * \throw Allocator::Out_of_memory could not buffer the XML start node - */ - Child(Env &env, - Allocator &alloc, - Verbose const &verbose, - Id id, - Report_update_trigger &report_update_trigger, - Xml_node start_node, - Default_route_accessor &default_route_accessor, - Default_caps_accessor &default_caps_accessor, - Name_registry &name_registry, - Ram_limit_accessor &ram_limit_accessor, - Cap_limit_accessor &cap_limit_accessor, - Cpu_limit_accessor &cpu_limit_accessor, - Cpu_quota_transfer &cpu_quota_transfer, - Prio_levels prio_levels, - Affinity::Space const &affinity_space, - Affinity::Location const &location, - Registry &parent_services, - Registry &child_services, - Registry &local_services); - - virtual ~Child(); - - /** - * Return true if the child has the specified name - */ - bool has_name(Child_policy::Name const &str) const { return str == name(); } - - bool has_version(Version const &version) const { return version == _version; } - - Ram_quota ram_quota() const { return _resources.assigned_ram_quota; } - Cap_quota cap_quota() const { return _resources.assigned_cap_quota; } - Cpu_quota cpu_quota() const { return _effective_cpu_quota; } - - virtual void try_start() - { - if (_state == State::INITIAL) { - _child.initiate_env_pd_session(); - _state = State::RAM_INITIALIZED; - } - - /* - * Update the state if async env sessions have brought the child to - * life. Otherwise, we would wrongly call 'initiate_env_sessions()' - * another time. - */ - if (_state == State::RAM_INITIALIZED && _child.active()) - _state = State::ALIVE; - - if (_state == State::RAM_INITIALIZED) { - _child.initiate_env_sessions(); - - if (_child.active()) - _state = State::ALIVE; - else - _uncertain_dependencies = true; - } + _child.initiate_env_pd_session(); + _state = State::RAM_INITIALIZED; } /* - * Mark child as to be removed because its was dropped from the - * config model. Either node disappeared or 'restart_scheduled' - * was handled. + * Update the state if async env sessions have brought the child to + * life. Otherwise, we would wrongly call 'initiate_env_sessions()' + * another time. */ - void abandon() + if (_state == State::RAM_INITIALIZED && _child.active()) + _state = State::ALIVE; + + if (_state == State::RAM_INITIALIZED) { - _state = State::ABANDONED; - _abandon_services(); + _child.initiate_env_sessions(); + + if (_child.active()) + _state = State::ALIVE; + else + _uncertain_dependencies = true; } + } - void destroy_services(); + /* + * Mark child as to be removed because its was dropped from the + * config model. Either node disappeared or 'restart_scheduled' + * was handled. + */ + void abandon() + { + _state = State::ABANDONED; + _abandon_services(); + } - void close_all_sessions() { _child.close_all_sessions(); } + void destroy_services(); - bool abandoned() const { return _state == State::ABANDONED; } + void close_all_sessions() { _child.close_all_sessions(); } - bool restart_scheduled() const { return _state == State::RESTART_SCHEDULED; } + bool abandoned() const { return _state == State::ABANDONED; } - bool stuck() const { return _state == State::STUCK; } + bool restart_scheduled() const { return _state == State::RESTART_SCHEDULED; } - bool env_sessions_closed() const { return _child.env_sessions_closed(); } + bool stuck() const { return _state == State::STUCK; } - enum Apply_config_result { PROVIDED_SERVICES_CHANGED, NO_SIDE_EFFECTS }; + bool env_sessions_closed() const { return _child.env_sessions_closed(); } - /** - * Apply new configuration to child - * - * \throw Allocator::Out_of_memory unable to allocate buffer for new - * config - */ - Apply_config_result apply_config(Xml_node start_node); + enum Apply_config_result + { + PROVIDED_SERVICES_CHANGED, + NO_SIDE_EFFECTS + }; - bool uncertain_dependencies() const { return _uncertain_dependencies; } + /** + * Apply new configuration to child + * + * \throw Allocator::Out_of_memory unable to allocate buffer for new + * config + */ + Apply_config_result apply_config(Xml_node start_node); - /** - * Validate that the routes of all existing sessions remain intact - * - * The child may become scheduled for restart or get stuck. - */ - void evaluate_dependencies(); + bool uncertain_dependencies() const { return _uncertain_dependencies; } - /* common code for upgrading RAM and caps */ - template - void _apply_resource_upgrade(QUOTA &, QUOTA, LIMIT_ACCESSOR const &); + /** + * Validate that the routes of all existing sessions remain intact + * + * The child may become scheduled for restart or get stuck. + */ + void evaluate_dependencies(); - template - void _apply_resource_downgrade(QUOTA &, QUOTA, QUOTA, - CHILD_AVAIL_QUOTA_FN const &); + /* common code for upgrading RAM and caps */ + template + void _apply_resource_upgrade(QUOTA &, QUOTA, LIMIT_ACCESSOR const &); - void apply_upgrade(); - void apply_downgrade(); + template + void _apply_resource_downgrade(QUOTA &, QUOTA, QUOTA, + CHILD_AVAIL_QUOTA_FN const &); - void heartbeat() - { - if (_heartbeat_expected()) - _child.heartbeat(); + void apply_upgrade(); + void apply_downgrade(); - unsigned const skipped_heartbeats = _child.skipped_heartbeats(); + void heartbeat() + { + if (_heartbeat_expected()) + _child.heartbeat(); - if (_last_skipped_heartbeats != skipped_heartbeats) - _report_update_trigger.trigger_report_update(); - - _last_skipped_heartbeats = skipped_heartbeats; - - } - - unsigned skipped_heartbeats() const - { - return _heartbeat_expected() ? _child.skipped_heartbeats() : 0; - } - - void report_state(Xml_generator &, Report_detail const &) const; - - Sample_state_result sample_state(); - - - - /**************************** - ** Child-policy interface ** - ****************************/ - - Child_policy::Name name() const override { return _unique_name; } - - Pd_session &ref_pd() override { return _env.pd(); } - Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } - - void init(Pd_session &, Pd_session_capability) override; - void init(Cpu_session &, Cpu_session_capability) override; - - Id_space &server_id_space() override { - return _session_requester.id_space(); } - - Route resolve_session_request(Service::Name const &, - Session_label const &, Session::Diag) override; - - void filter_session_args(Service::Name const &, char *, size_t) override; - Affinity filter_session_affinity(Affinity const &) override; - void announce_service(Service::Name const &) override; - void resource_request(Parent::Resource_args const &) override; - - void exit(int exit_value) override - { - try { - if (_start_node->xml().sub_node("exit").attribute_value("propagate", false)) { - _env.parent().exit(exit_value); - return; - } - } catch (...) { } - - /* - * Trigger a new report for exited children so that any management - * component may react upon it. - */ - _exited = true; - _exit_value = exit_value; - - _child.close_all_sessions(); + unsigned const skipped_heartbeats = _child.skipped_heartbeats(); + if (_last_skipped_heartbeats != skipped_heartbeats) _report_update_trigger.trigger_report_update(); - /* - * Print a message as the exit is not handled otherwise. There are - * a number of automated tests that rely on this message. It is - * printed by the default implementation of 'Child_policy::exit'. - */ - Child_policy::exit(exit_value); - } + _last_skipped_heartbeats = skipped_heartbeats; + } - void session_state_changed() override + unsigned skipped_heartbeats() const + { + return _heartbeat_expected() ? _child.skipped_heartbeats() : 0; + } + + void report_state(Xml_generator &, Report_detail const &) const; + + Sample_state_result sample_state(); + + /**************************** + ** Child-policy interface ** + ****************************/ + + Child_policy::Name name() const override { return _unique_name; } + + Pd_session &ref_pd() override + { + Pd_session *_ref_pd_ptr = nullptr; + _with_pd_intrinsics([&](Pd_intrinsics::Intrinsics &intrinsics) + { _ref_pd_ptr = &intrinsics.ref_pd; }); + return *_ref_pd_ptr; + } + + Pd_session_capability ref_pd_cap() const override { return _ref_pd_cap; } + + Ram_allocator &session_md_ram() override { return _env.ram(); } + + void init(Pd_session &, Pd_session_capability) override; + void init(Cpu_session &, Cpu_session_capability) override; + + Id_space &server_id_space() override + { + return _session_requester.id_space(); + } + + Route resolve_session_request(Service::Name const &, + Session_label const &, Session::Diag) override; + + void filter_session_args(Service::Name const &, char *, size_t) override; + Affinity filter_session_affinity(Affinity const &) override; + void announce_service(Service::Name const &) override; + void resource_request(Parent::Resource_args const &) override; + + void exit(int exit_value) override + { + try { - _report_update_trigger.trigger_report_update(); + if (_start_node->xml().sub_node("exit").attribute_value("propagate", false)) + { + _env.parent().exit(exit_value); + return; + } } - - bool initiate_env_sessions() const override { return false; } - - void yield_response() override + catch (...) { - apply_downgrade(); - _report_update_trigger.trigger_report_update(); } + + /* + * Trigger a new report for exited children so that any management + * component may react upon it. + */ + _exited = true; + _exit_value = exit_value; + + _child.close_all_sessions(); + + _report_update_trigger.trigger_immediate_report_update(); + + /* + * Print a message as the exit is not handled otherwise. There are + * a number of automated tests that rely on this message. It is + * printed by the default implementation of 'Child_policy::exit'. + */ + Child_policy::exit(exit_value); + } + + void session_state_changed() override + { + _report_update_trigger.trigger_report_update(); + } + + bool initiate_env_sessions() const override { return false; } + + void _with_address_space(Pd_session &, With_address_space_fn const &fn) override + { + _with_pd_intrinsics([&](Pd_intrinsics::Intrinsics &intrinsics) + { fn.call(intrinsics.address_space); }); + } + + void start_initial_thread(Capability cap, addr_t ip) override + { + _pd_intrinsics.start_initial_thread(cap, ip); + } + + void yield_response() override + { + apply_downgrade(); + _report_update_trigger.trigger_report_update(); + } }; #endif /* _LIB__SANDBOX__CHILD_H_ */ + diff --git a/repos/os/include/sandbox/library.h b/repos/os/include/sandbox/library.h index 3efdf7483c..540f922825 100644 --- a/repos/os/include/sandbox/library.h +++ b/repos/os/include/sandbox/library.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -21,106 +22,111 @@ namespace Sandbox { namespace Genode { class Sandbox; } - struct Sandbox::Library : ::Sandbox::State_reporter::Producer, - ::Sandbox::Child::Default_route_accessor, - ::Sandbox::Child::Default_caps_accessor, - ::Sandbox::Child::Ram_limit_accessor, - ::Sandbox::Child::Cap_limit_accessor, - ::Sandbox::Child::Cpu_limit_accessor, - ::Sandbox::Child::Cpu_quota_transfer, - ::Sandbox::Start_model::Factory, - ::Sandbox::Parent_provides_model::Factory + ::Sandbox::Child::Default_route_accessor, + ::Sandbox::Child::Default_caps_accessor, + ::Sandbox::Child::Ram_limit_accessor, + ::Sandbox::Child::Cap_limit_accessor, + ::Sandbox::Child::Cpu_limit_accessor, + ::Sandbox::Child::Cpu_quota_transfer, + ::Sandbox::Start_model::Factory, + ::Sandbox::Parent_provides_model::Factory { using Routed_service = ::Sandbox::Routed_service; using Parent_service = ::Sandbox::Parent_service; - using Local_service = ::Genode::Sandbox::Local_service_base; - using Report_detail = ::Sandbox::Report_detail; + using Local_service = ::Genode::Sandbox::Local_service_base; + using Report_detail = ::Sandbox::Report_detail; using Child_registry = ::Sandbox::Child_registry; - using Verbose = ::Sandbox::Verbose; + using Verbose = ::Sandbox::Verbose; using State_reporter = ::Sandbox::State_reporter; - using Heartbeat = ::Sandbox::Heartbeat; - using Server = ::Sandbox::Server; - using Alias = ::Sandbox::Alias; - using Child = ::Sandbox::Child; - using Prio_levels = ::Sandbox::Prio_levels; - using Ram_info = ::Sandbox::Ram_info; - using Cap_info = ::Sandbox::Cap_info; - using Cpu_quota = ::Sandbox::Cpu_quota; - using Config_model = ::Sandbox::Config_model; - using Start_model = ::Sandbox::Start_model; - using Preservation = ::Sandbox::Preservation; + using Heartbeat = ::Sandbox::Heartbeat; + using Server = ::Sandbox::Server; + using Alias = ::Sandbox::Alias; + using Child = ::Sandbox::Child; + using Prio_levels = ::Sandbox::Prio_levels; + using Ram_info = ::Sandbox::Ram_info; + using Cap_info = ::Sandbox::Cap_info; + using Cpu_quota = ::Sandbox::Cpu_quota; + using Config_model = ::Sandbox::Config_model; + using Start_model = ::Sandbox::Start_model; + using Preservation = ::Sandbox::Preservation; + using Pd_intrinsics = Genode::Sandbox::Pd_intrinsics; -public: - Env &_env; + using State_handler = Genode::Sandbox::State_handler; + + Env &_env; Heap &_heap; - Registry _parent_services { }; - Registry _child_services { }; - Registry &_local_services; - Child_registry _children { }; + Pd_intrinsics &_pd_intrinsics; + + Registry _parent_services{}; + Registry _child_services{}; + Registry &_local_services; + Child_registry _children{}; /* * Global parameters obtained from config */ - Reconstructible _verbose { }; - Config_model::Version _version { }; - Constructible _default_route { }; - Cap_quota _default_caps { 0 }; - Prio_levels _prio_levels { }; - Constructible _affinity_space { }; - Preservation _preservation { }; + Reconstructible _verbose{}; + Config_model::Version _version{}; + Constructible _default_route{}; + Cap_quota _default_caps{0}; + Prio_levels _prio_levels{}; + Constructible _affinity_space{}; + Preservation _preservation{}; Affinity::Space _effective_affinity_space() const { return _affinity_space.constructed() ? *_affinity_space - : Affinity::Space { 1, 1 }; + : Affinity::Space{1, 1}; } State_reporter _state_reporter; - Heartbeat _heartbeat { _env, _children, _state_reporter }; + Heartbeat _heartbeat{_env, _children, _state_reporter}; /* * Internal representation of the XML configuration */ - Config_model _config_model { }; + Config_model _config_model{}; /* * Variables for tracking the side effects of updating the config model */ bool _server_appeared_or_disappeared = false; - bool _state_report_outdated = false; + bool _state_report_outdated = false; unsigned _child_cnt = 0; - Cpu_quota _avail_cpu { .percent = 100 }; - Cpu_quota _transferred_cpu { .percent = 0 }; + Cpu_quota _avail_cpu{.percent = 100}; + Cpu_quota _transferred_cpu{.percent = 0}; Ram_quota _avail_ram() const { Ram_quota avail_ram = _env.pd().avail_ram(); - if (_preservation.ram.value > avail_ram.value) { + if (_preservation.ram.value > avail_ram.value) + { error("RAM preservation exceeds available memory"); - return Ram_quota { 0 }; + return Ram_quota{0}; } /* deduce preserved quota from available quota */ - return Ram_quota { avail_ram.value - _preservation.ram.value }; + return Ram_quota{avail_ram.value - _preservation.ram.value}; } Cap_quota _avail_caps() const { - Cap_quota avail_caps { _env.pd().avail_caps().value }; + Cap_quota avail_caps{_env.pd().avail_caps().value}; - if (_preservation.caps.value > avail_caps.value) { + if (_preservation.caps.value > avail_caps.value) + { error("Capability preservation exceeds available capabilities"); - return Cap_quota { 0 }; + return Cap_quota{0}; } /* deduce preserved quota from available quota */ - return Cap_quota { avail_caps.value - _preservation.caps.value }; + return Cap_quota{avail_caps.value - _preservation.caps.value}; } /** @@ -144,9 +150,10 @@ public: /** * Child::Cpu_quota_transfer interface */ - void transfer_cpu_quota(Cpu_session_capability cap, Cpu_quota quota) override + void transfer_cpu_quota(Capability pd_cap, Pd_session &pd, + Capability cpu, Cpu_quota quota) override { - Cpu_quota const remaining { 100 - min(100u, _transferred_cpu.percent) }; + Cpu_quota const remaining{100 - min(100u, _transferred_cpu.percent)}; /* prevent division by zero in 'quota_lim_upscale' */ if (remaining.percent == 0) @@ -155,7 +162,8 @@ public: size_t const fraction = Cpu_session::quota_lim_upscale(quota.percent, remaining.percent); - _env.cpu().transfer_quota(cap, fraction); + Child::with_pd_intrinsics(_pd_intrinsics, pd_cap, pd, [&](auto &intrinsics) + { intrinsics.ref_cpu.transfer_quota(cpu, fraction); }); _transferred_cpu.percent += quota.percent; } @@ -166,10 +174,12 @@ public: void produce_state_report(Xml_generator &xml, Report_detail const &detail) const override { if (detail.init_ram()) - xml.node("ram", [&] () { Ram_info::from_pd(_env.pd()).generate(xml); }); + xml.node("ram", [&]() + { Ram_info::from_pd(_env.pd()).generate(xml); }); if (detail.init_caps()) - xml.node("caps", [&] () { Cap_info::from_pd(_env.pd()).generate(xml); }); + xml.node("caps", [&]() + { Cap_info::from_pd(_env.pd()).generate(xml); }); if (detail.children()) _children.report_state(xml, detail); @@ -189,7 +199,7 @@ public: Xml_node default_route() override { return _default_route.constructed() ? _default_route->xml() - : Xml_node(""); + : Xml_node(""); } /** @@ -201,19 +211,19 @@ public: void _update_parent_services_from_config(Xml_node const &); void _update_children_config(Xml_node const &); void _destroy_abandoned_parent_services(); - virtual void _destroy_abandoned_children(); + void _destroy_abandoned_children(); - Server _server { _env, _heap, _child_services, _state_reporter }; + Server _server{_env, _heap, _child_services, _state_reporter}; /** * Sandbox::Start_model::Factory */ - virtual Child &create_child(Xml_node const &) override; + Child &create_child(Xml_node const &) override; /** * Sandbox::Start_model::Factory */ - virtual void update_child(Child &, Xml_node const &) override; + void update_child(Child &, Xml_node const &) override; /** * Sandbox::Start_model::Factory @@ -237,8 +247,8 @@ public: /** * Sandbox::Start_model::Factory */ - bool ready_to_create_child(Start_model::Name const &, - Start_model::Version const &) const override; + bool ready_to_create_child(Start_model::Name const &, + Start_model::Version const &) const override; /** * Sandbox::Parent_provides_model::Factory @@ -248,16 +258,48 @@ public: return *new (_heap) Parent_service(_parent_services, _env, name); } + /** + * Default way of using the 'Env::pd' as the child's 'ref_pd' and accessing + * the child's address space via RPC. + */ + struct Default_pd_intrinsics : Pd_intrinsics + { + Env &_env; + + void with_intrinsics(Capability, Pd_session &pd, Fn const &fn) override + { + Region_map_client region_map(pd.address_space()); + + Intrinsics intrinsics{_env.pd(), _env.pd_session_cap(), + _env.cpu(), _env.cpu_session_cap(), region_map}; + fn.call(intrinsics); + } + + void start_initial_thread(Capability cap, addr_t ip) override + { + Cpu_thread_client(cap).start(ip, 0); + } + + Default_pd_intrinsics(Env &env) : _env(env) {} + + } _default_pd_intrinsics{_env}; + Library(Env &env, Heap &heap, Registry &local_services, - Genode::Sandbox::State_handler &state_handler) - : - _env(env), _heap(heap), _local_services(local_services), - _state_reporter(_env, *this, state_handler) - { } + State_handler &state_handler, Pd_intrinsics &pd_intrinsics) + : _env(env), _heap(heap), _pd_intrinsics(pd_intrinsics), + _local_services(local_services), _state_reporter(_env, *this, state_handler) + { + } - virtual void apply_config(Xml_node const &); + Library(Env &env, Heap &heap, Registry &local_services, + State_handler &state_handler) + : Library(env, heap, local_services, state_handler, _default_pd_intrinsics) + { + } - virtual void generate_state_report(Xml_generator &xml) const + void apply_config(Xml_node const &); + + void generate_state_report(Xml_generator &xml) const { _state_reporter.generate(xml); } diff --git a/repos/os/src/lib/sandbox/child.cc b/repos/os/src/lib/sandbox/child.cc index 04e752640e..5b909d38af 100644 --- a/repos/os/src/lib/sandbox/child.cc +++ b/repos/os/src/lib/sandbox/child.cc @@ -15,17 +15,16 @@ #include /* local includes */ -#include - +#include void Sandbox::Child::destroy_services() { - _child_services.for_each([&] (Routed_service &service) { + _child_services.for_each([&](Routed_service &service) + { if (service.has_id_space(_session_requester.id_space())) destroy(_alloc, &service); }); } - Sandbox::Child::Apply_config_result Sandbox::Child::apply_config(Xml_node start_node) { @@ -36,15 +35,17 @@ Sandbox::Child::apply_config(Xml_node start_node) * If the child was started but its environment is incomplete, mark it as * being stuck in order to restart it once the environment changes. */ - if (_state != State::INITIAL) { + if (_state != State::INITIAL) + { bool env_log_exists = false, env_binary_exists = false; - _child.for_each_session([&] (Session_state const &session) { + _child.for_each_session([&](Session_state const &session) + { Parent::Client::Id const id = session.id_at_client(); env_log_exists |= (id == Parent::Env::log()); - env_binary_exists |= (id == Parent::Env::binary()); - }); + env_binary_exists |= (id == Parent::Env::binary()); }); - if (!env_binary_exists || !env_log_exists) { + if (!env_binary_exists || !env_log_exists) + { _state = State::STUCK; return NO_SIDE_EFFECTS; } @@ -52,22 +53,29 @@ Sandbox::Child::apply_config(Xml_node start_node) bool provided_services_changed = false; - enum Config_update { CONFIG_APPEARED, CONFIG_VANISHED, - CONFIG_CHANGED, CONFIG_UNCHANGED }; + enum Config_update + { + CONFIG_APPEARED, + CONFIG_VANISHED, + CONFIG_CHANGED, + CONFIG_UNCHANGED + }; Config_update config_update = CONFIG_UNCHANGED; /* * Import new start node if it differs */ - if (start_node.differs_from(_start_node->xml())) { + if (start_node.differs_from(_start_node->xml())) + { /* * The node may affect the availability or unavailability * of dependencies. */ - start_node.with_optional_sub_node("route", [&] (Xml_node const &route) { - _start_node->xml().with_optional_sub_node("route", [&] (Xml_node const &orig) { + start_node.with_optional_sub_node("route", [&](Xml_node const &route) + { _start_node->xml().with_optional_sub_node("route", [&](Xml_node const &orig) + { if (route.differs_from(orig)) { _construct_route_model_from_start_node(start_node); _uncertain_dependencies = true; } }); }); @@ -75,9 +83,9 @@ Sandbox::Child::apply_config(Xml_node start_node) /* * Determine how the inline config is affected. */ - char const * const tag = "config"; + char const *const tag = "config"; bool const config_was_present = _start_node->xml().has_sub_node(tag); - bool const config_is_present = start_node.has_sub_node(tag); + bool const config_is_present = start_node.has_sub_node(tag); if (config_was_present != config_is_present) _uncertain_dependencies = true; @@ -88,7 +96,8 @@ Sandbox::Child::apply_config(Xml_node start_node) if (!config_was_present && config_is_present) config_update = CONFIG_APPEARED; - if (config_was_present && config_is_present) { + if (config_was_present && config_is_present) + { Xml_node const old_config = _start_node->xml().sub_node(tag); Xml_node const new_config = start_node.sub_node(tag); @@ -103,7 +112,8 @@ Sandbox::Child::apply_config(Xml_node start_node) * First abandon services that are no longer present in the * node. Then add services that have newly appeared. */ - _child_services.for_each([&] (Routed_service &service) { + _child_services.for_each([&](Routed_service &service) + { if (!_provided_by_this(service)) return; @@ -120,17 +130,15 @@ Sandbox::Child::apply_config(Xml_node start_node) if (!still_provided) { service.abandon(); provided_services_changed = true; - } - }); + } }); - _provides_sub_node(start_node).for_each_sub_node("service", - [&] (Xml_node node) { + _provides_sub_node(start_node).for_each_sub_node("service", [&](Xml_node node) + { if (_service_exists(node)) return; _add_service(node); - provided_services_changed = true; - }); + provided_services_changed = true; }); /* * Import new binary name. A change may affect the route for @@ -154,11 +162,19 @@ Sandbox::Child::apply_config(Xml_node start_node) * may in turn prompt the routing-check by 'evaluate_dependencies' * to restart the child. */ - switch (config_update) { - case CONFIG_UNCHANGED: break; - case CONFIG_CHANGED: _config_rom_service->trigger_update(); break; - case CONFIG_APPEARED: _config_rom_service.construct(*this); break; - case CONFIG_VANISHED: _config_rom_service->abandon(); break; + switch (config_update) + { + case CONFIG_UNCHANGED: + break; + case CONFIG_CHANGED: + _config_rom_service->trigger_update(); + break; + case CONFIG_APPEARED: + _config_rom_service.construct(*this); + break; + case CONFIG_VANISHED: + _config_rom_service->abandon(); + break; } if (provided_services_changed) @@ -167,24 +183,24 @@ Sandbox::Child::apply_config(Xml_node start_node) return NO_SIDE_EFFECTS; } - void Sandbox::Child::evaluate_dependencies() { - bool any_route_changed = false; + bool any_route_changed = false; bool any_route_unavailable = false; - _child.for_each_session([&] (Session_state const &session) { + _child.for_each_session([&](Session_state const &session) + { switch (_route_valid(session)) { case Route_state::VALID: break; case Route_state::UNAVAILABLE: any_route_unavailable = true; break; case Route_state::MISMATCH: any_route_changed = true; break; - } - }); + } }); _uncertain_dependencies = false; - if (any_route_unavailable) { + if (any_route_unavailable) + { _state = State::STUCK; return; } @@ -193,35 +209,33 @@ void Sandbox::Child::evaluate_dependencies() _schedule_restart(); } - Sandbox::Ram_quota Sandbox::Child::_configured_ram_quota() const { size_t assigned = 0; - _start_node->xml().for_each_sub_node("resource", [&] (Xml_node resource) { + _start_node->xml().for_each_sub_node("resource", [&](Xml_node resource) + { if (resource.attribute_value("name", String<8>()) == "RAM") assigned = resource.attribute_value("quantum", Number_of_bytes()); }); - return Ram_quota { assigned }; + return Ram_quota{assigned}; } - Sandbox::Cap_quota Sandbox::Child::_configured_cap_quota() const { size_t const default_caps = _default_caps_accessor.default_caps().value; - return Cap_quota { _start_node->xml().attribute_value("caps", default_caps) }; + return Cap_quota{_start_node->xml().attribute_value("caps", default_caps)}; } - template void Sandbox::Child::_apply_resource_upgrade(QUOTA &assigned, QUOTA const configured, - LIMIT_ACCESSOR const &limit_accessor) + LIMIT_ACCESSOR const &limit_accessor) { if (configured.value <= assigned.value) return; - QUOTA const limit = limit_accessor.resource_limit(QUOTA{}); + QUOTA const limit = limit_accessor.resource_limit(QUOTA{}); size_t const increment = configured.value - assigned.value; /* @@ -232,7 +246,7 @@ void Sandbox::Child::_apply_resource_upgrade(QUOTA &assigned, QUOTA const config if (_verbose.enabled()) warn_insuff_quota(limit.value); - QUOTA const transfer { min(increment, limit.value) }; + QUOTA const transfer{min(increment, limit.value)}; /* * Remember assignment and apply upgrade to child @@ -243,21 +257,22 @@ void Sandbox::Child::_apply_resource_upgrade(QUOTA &assigned, QUOTA const config * This way, a future config update will attempt the completion of the * upgrade if memory become available. */ - if (transfer.value) { + if (transfer.value) + { assigned.value += transfer.value; ref_pd().transfer_quota(_child.pd_session_cap(), transfer); /* wake up child that blocks on a resource request */ - if (_requested_resources.constructed()) { + if (_requested_resources.constructed()) + { _child.notify_resource_avail(); _requested_resources.destruct(); } } } - void Sandbox::Child::apply_upgrade() { /* pd_session_cap of exited child is invalid and unusable for transfers */ @@ -268,25 +283,24 @@ void Sandbox::Child::apply_upgrade() warning(name(), ": no valid RAM quota defined"); _apply_resource_upgrade(_resources.assigned_ram_quota, - _configured_ram_quota(), _ram_limit_accessor); + _configured_ram_quota(), _ram_limit_accessor); if (_resources.effective_cap_quota().value == 0) warning(name(), ": no valid capability quota defined"); _apply_resource_upgrade(_resources.assigned_cap_quota, - _configured_cap_quota(), _cap_limit_accessor); + _configured_cap_quota(), _cap_limit_accessor); } - template void Sandbox::Child::_apply_resource_downgrade(QUOTA &assigned, QUOTA const configured, - QUOTA const preserved, - CHILD_AVAIL_QUOTA_FN const &child_avail_quota_fn) + QUOTA const preserved, + CHILD_AVAIL_QUOTA_FN const &child_avail_quota_fn) { if (configured.value >= assigned.value) return; - QUOTA const decrement { assigned.value - configured.value }; + QUOTA const decrement{assigned.value - configured.value}; /* * The child may concurrently consume quota from its PD session, @@ -294,39 +308,45 @@ void Sandbox::Child::_apply_resource_downgrade(QUOTA &assigned, QUOTA const conf * attempt the transfer. */ unsigned max_attempts = 4, attempts = 0; - for (; attempts < max_attempts; attempts++) { + for (; attempts < max_attempts; attempts++) + { /* give up if the child's available quota is exhausted */ size_t const avail = child_avail_quota_fn().value; if (avail < preserved.value) break; - QUOTA const transfer { min(avail - preserved.value, decrement.value) }; + QUOTA const transfer{min(avail - preserved.value, decrement.value)}; - try { + try + { _child.pd().transfer_quota(ref_pd_cap(), transfer); assigned.value -= transfer.value; break; - } catch (...) { } + } + catch (...) + { + } } if (attempts == max_attempts) warning(name(), ": downgrade failed after ", max_attempts, " attempts"); } - void Sandbox::Child::apply_downgrade() { Ram_quota const configured_ram_quota = _configured_ram_quota(); Cap_quota const configured_cap_quota = _configured_cap_quota(); _apply_resource_downgrade(_resources.assigned_ram_quota, - configured_ram_quota, Ram_quota{16*1024}, - [&] () { return _child.pd().avail_ram(); }); + configured_ram_quota, Ram_quota{16 * 1024}, + [&]() + { return _child.pd().avail_ram(); }); _apply_resource_downgrade(_resources.assigned_cap_quota, - configured_cap_quota, Cap_quota{5}, - [&] () { return _child.pd().avail_caps(); }); + configured_cap_quota, Cap_quota{5}, + [&]() + { return _child.pd().avail_caps(); }); /* * If designated resource quota is lower than the child's consumed quota, @@ -341,21 +361,22 @@ void Sandbox::Child::apply_downgrade() if (configured_cap_quota.value < _resources.assigned_cap_quota.value) demanded_cap_quota = _resources.assigned_cap_quota.value - configured_cap_quota.value; - if (demanded_ram_quota || demanded_cap_quota) { + if (demanded_ram_quota || demanded_cap_quota) + { Parent::Resource_args const - args { "ram_quota=", Number_of_bytes(demanded_ram_quota), ", ", - "cap_quota=", demanded_cap_quota}; + args{"ram_quota=", Number_of_bytes(demanded_ram_quota), ", ", + "cap_quota=", demanded_cap_quota}; _child.yield(args); } } - void Sandbox::Child::report_state(Xml_generator &xml, Report_detail const &detail) const { if (abandoned()) return; - xml.node("child", [&] () { + xml.node("child", [&]() + { xml.attribute("name", _unique_name); xml.attribute("binary", _binary_name); @@ -422,11 +443,9 @@ void Sandbox::Child::report_state(Xml_generator &xml, Report_detail const &detai _session_requester.id_space().for_each(fn); }); - } - }); + } }); } - Sandbox::Child::Sample_state_result Sandbox::Child::sample_state() { if (!_pd_alive()) @@ -437,40 +456,42 @@ Sandbox::Child::Sample_state_result Sandbox::Child::sample_state() _sampled_state = Sampled_state::from_pd(_child.pd()); return (orig_state != _sampled_state) ? Sample_state_result::CHANGED - : Sample_state_result::UNCHANGED; + : Sample_state_result::UNCHANGED; } - void Sandbox::Child::init(Pd_session &session, Pd_session_capability cap) { size_t const initial_session_costs = - session_alloc_batch_size()*_child.session_factory().session_costs(); + session_alloc_batch_size() * _child.session_factory().session_costs(); - Ram_quota ram_quota { _resources.effective_ram_quota().value > initial_session_costs - ? _resources.effective_ram_quota().value - initial_session_costs - : 0 }; + Ram_quota ram_quota{_resources.effective_ram_quota().value > initial_session_costs + ? _resources.effective_ram_quota().value - initial_session_costs + : 0}; Ram_quota avail_ram = _ram_limit_accessor.resource_limit(Ram_quota()); avail_ram = Genode::Child::effective_quota(avail_ram); - if (ram_quota.value > avail_ram.value) { + if (ram_quota.value > avail_ram.value) + { warning(name(), ": configured RAM exceeds available RAM, proceed with ", avail_ram); ram_quota = avail_ram; } - Cap_quota cap_quota { _resources.effective_cap_quota().value }; + Cap_quota cap_quota{_resources.effective_cap_quota().value}; Cap_quota avail_caps = _cap_limit_accessor.resource_limit(avail_caps); avail_caps = Genode::Child::effective_quota(avail_caps); - if (cap_quota.value > avail_caps.value) { + if (cap_quota.value > avail_caps.value) + { warning(name(), ": configured caps exceed available caps, proceed with ", avail_caps); cap_quota = avail_caps; } - _with_pd_intrinsics([&] (Pd_intrinsics::Intrinsics &intrinsics) { + _with_pd_intrinsics([&](Pd_intrinsics::Intrinsics &intrinsics) + { _ref_pd_cap = intrinsics.ref_pd_cap; @@ -482,42 +503,41 @@ void Sandbox::Child::init(Pd_session &session, Pd_session_capability cap) try { intrinsics.ref_pd.transfer_quota(cap, ram_quota); } catch (Out_of_ram) { - error(name(), ": unable to initialize RAM quota of PD"); } - }); + error(name(), ": unable to initialize RAM quota of PD"); } }); } - void Sandbox::Child::init(Cpu_session &session, Cpu_session_capability cap) { - Cpu_quota const assigned = _resources.assigned_cpu_quota; + Cpu_quota const assigned = _resources.assigned_cpu_quota; Cpu_quota const effective = _effective_cpu_quota; if (assigned.percent > effective.percent) warning(name(), ": configured CPU quota of ", assigned, " exceeds " - "available quota, proceeding with a quota of ", effective); + "available quota, proceeding with a quota of ", + effective); - _with_pd_intrinsics([&] (Pd_intrinsics::Intrinsics &intrinsics) { - session.ref_account(intrinsics.ref_cpu_cap); }); + _with_pd_intrinsics([&](Pd_intrinsics::Intrinsics &intrinsics) + { session.ref_account(intrinsics.ref_cpu_cap); }); _cpu_quota_transfer.transfer_cpu_quota(_child.pd_session_cap(), _child.pd(), - cap, effective); + cap, effective); } - Sandbox::Child::Route Sandbox::Child::resolve_session_request(Service::Name const &service_name, - Session_label const &label, - Session::Diag const diag) + Session_label const &label, + Session::Diag const diag) { bool const rom_service = (service_name == Rom_session::service_name()); /* check for "config" ROM request */ - if (rom_service && label.last_element() == "config") { + if (rom_service && label.last_element() == "config") + { if (_config_rom_service.constructed() && - !_config_rom_service->abandoned()) - return Route { _config_rom_service->service(), label, - Session::Diag{false} }; + !_config_rom_service->abandoned()) + return Route{_config_rom_service->service(), label, + Session::Diag{false}}; /* * If there is no inline '', we apply the regular session @@ -543,9 +563,9 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name, /* check for "session_requests" ROM request */ if (rom_service && label.last_element() == Session_requester::rom_name()) - return Route { _session_requester.service(), Session::Label(), diag }; + return Route{_session_requester.service(), Session::Label(), diag}; - auto resolve_at_target = [&] (Xml_node const &target) -> Route + auto resolve_at_target = [&](Xml_node const &target) -> Route { /* * Determine session label to be provided to the server @@ -561,54 +581,77 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name, target.attribute_value("label", Label(label.string())); Session::Diag const - target_diag { target.attribute_value("diag", diag.enabled) }; + target_diag{target.attribute_value("diag", diag.enabled)}; - auto no_filter = [] (Service &) -> bool { return false; }; + auto no_filter = [](Service &) -> bool + { return false; }; - if (target.has_type("parent")) { + if (target.has_type("parent")) + { - try { - return Route { find_service(_parent_services, service_name, no_filter), - target_label, target_diag }; - } catch (Service_denied) { } + try + { + return Route{find_service(_parent_services, service_name, no_filter), + target_label, target_diag}; + } + catch (Service_denied) + { + } } - if (target.has_type("local")) { + if (target.has_type("local")) + { - try { - return Route { find_service(_local_services, service_name, no_filter), - target_label, target_diag }; - } catch (Service_denied) { } + try + { + return Route{find_service(_local_services, service_name, no_filter), + target_label, target_diag}; + } + catch (Service_denied) + { + } } - if (target.has_type("child")) { + if (target.has_type("child")) + { using Name = Name_registry::Name; Name server_name = target.attribute_value("name", Name()); server_name = _name_registry.deref_alias(server_name); - auto filter_server_name = [&] (Routed_service &s) -> bool { - return s.child_name() != server_name; }; + auto filter_server_name = [&](Routed_service &s) -> bool + { return s.child_name() != server_name; }; - try { - return Route { find_service(_child_services, service_name, - filter_server_name), target_label, target_diag }; - - } catch (Service_denied) { } + try + { + return Route{find_service(_child_services, service_name, + filter_server_name), + target_label, target_diag}; + } + catch (Service_denied) + { + } } - if (target.has_type("any-child")) { + if (target.has_type("any-child")) + { - if (is_ambiguous(_child_services, service_name)) { + if (is_ambiguous(_child_services, service_name)) + { error(name(), ": ambiguous routes to " - "service \"", service_name, "\""); + "service \"", + service_name, "\""); throw Service_denied(); } - try { - return Route { find_service(_child_services, service_name, - no_filter), target_label, target_diag }; - - } catch (Service_denied) { } + try + { + return Route{find_service(_child_services, service_name, + no_filter), + target_label, target_diag}; + } + catch (Service_denied) + { + } } throw Service_denied(); @@ -619,16 +662,16 @@ Sandbox::Child::resolve_session_request(Service::Name const &service_name, return _route_model->resolve(query, resolve_at_target); } - void Sandbox::Child::filter_session_args(Service::Name const &service, - char *args, size_t args_len) + char *args, size_t args_len) { /* * Intercept CPU session requests to scale priorities */ if ((service == Cpu_session::service_name() || - service == Vm_session::service_name()) - && _prio_levels_log2 > 0) { + service == Vm_session::service_name()) && + _prio_levels_log2 > 0) + { unsigned priority = (unsigned)Arg_string::find_arg(args, "priority").ulong_value(0); @@ -642,11 +685,10 @@ void Sandbox::Child::filter_session_args(Service::Name const &service, priority >>= _prio_levels_log2; /* assign child priority to the most significant priority bits */ - priority = priority - | (unsigned)(_priority*(Cpu_session::PRIORITY_LIMIT >> _prio_levels_log2)); + priority = priority | (unsigned)(_priority * (Cpu_session::PRIORITY_LIMIT >> _prio_levels_log2)); /* override priority when delegating the session request to the parent */ - String<64> value { Hex(priority) }; + String<64> value{Hex(priority)}; Arg_string::set_arg(args, args_len, "priority", value.string()); } @@ -654,7 +696,8 @@ void Sandbox::Child::filter_session_args(Service::Name const &service, * Unset the 'managing_system' argument unless explicitly permitted by the * child configuration. */ - if (service == Pd_session::service_name()) { + if (service == Pd_session::service_name()) + { /* * For an environment PD session created by us for a direct child, the @@ -680,53 +723,51 @@ void Sandbox::Child::filter_session_args(Service::Name const &service, } } - Genode::Affinity Sandbox::Child::filter_session_affinity(Affinity const &session_affinity) { - Affinity::Space const &child_space = _resources.affinity.space(); + Affinity::Space const &child_space = _resources.affinity.space(); Affinity::Location const &child_location = _resources.affinity.location(); /* check if no valid affinity space was specified */ if (session_affinity.space().total() == 0) return Affinity(child_space, child_location); - Affinity::Space const &session_space = session_affinity.space(); + Affinity::Space const &session_space = session_affinity.space(); Affinity::Location const &session_location = session_affinity.location(); - /* scale resolution of resulting space */ Affinity::Space space(child_space.multiply(session_space)); Affinity::Location child_session(child_location.xpos(), child_location.ypos(), - child_location.width() * session_location.width(), - child_location.height() * session_location.height()); + child_location.width() * session_location.width(), + child_location.height() * session_location.height()); /* subordinate session affinity to child affinity subspace */ Affinity::Location location(child_session - .multiply_position(session_space) - .transpose(session_location.xpos() * child_location.width(), - session_location.ypos() * child_location.height())); + .multiply_position(session_space) + .transpose(session_location.xpos() * child_location.width(), + session_location.ypos() * child_location.height())); return Affinity(space, location); } - void Sandbox::Child::announce_service(Service::Name const &service_name) { if (_verbose.enabled()) log("child \"", name(), "\" announces service \"", service_name, "\""); bool found = false; - _child_services.for_each([&] (Routed_service &service) { + _child_services.for_each([&](Routed_service &service) + { if (service.has_id_space(_session_requester.id_space()) && service.name() == service_name) found = true; }); if (!found) error(name(), ": illegal announcement of " - "service \"", service_name, "\""); + "service \"", + service_name, "\""); } - void Sandbox::Child::resource_request(Parent::Resource_args const &args) { log("child \"", name(), "\" requests resources: ", args); @@ -735,50 +776,48 @@ void Sandbox::Child::resource_request(Parent::Resource_args const &args) _report_update_trigger.trigger_immediate_report_update(); } - -Sandbox::Child::Child(Env &env, - Allocator &alloc, - Verbose const &verbose, - Id id, - Report_update_trigger &report_update_trigger, - Xml_node start_node, - Default_route_accessor &default_route_accessor, - Default_caps_accessor &default_caps_accessor, - Name_registry &name_registry, - Ram_limit_accessor &ram_limit_accessor, - Cap_limit_accessor &cap_limit_accessor, - Cpu_limit_accessor &cpu_limit_accessor, - Cpu_quota_transfer &cpu_quota_transfer, - Prio_levels prio_levels, - Affinity::Space const &affinity_space, - Affinity::Location const &location, - Registry &parent_services, - Registry &child_services, - Registry &local_services, - Pd_intrinsics &pd_intrinsics) -: - _env(env), _alloc(alloc), _verbose(verbose), _id(id), - _report_update_trigger(report_update_trigger), - _list_element(this), - _start_node(_alloc, start_node), - _default_route_accessor(default_route_accessor), - _default_caps_accessor(default_caps_accessor), - _ram_limit_accessor(ram_limit_accessor), - _cap_limit_accessor(cap_limit_accessor), - _cpu_limit_accessor(cpu_limit_accessor), - _cpu_quota_transfer(cpu_quota_transfer), - _name_registry(name_registry), - _heartbeat_enabled(start_node.has_sub_node("heartbeat")), - _resources(_resources_from_start_node(start_node, prio_levels, affinity_space, location, - default_caps_accessor.default_caps())), - _pd_intrinsics(pd_intrinsics), - _parent_services(parent_services), - _child_services(child_services), - _local_services(local_services), - _session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()) +Sandbox::Child::Child(Env &env, + Allocator &alloc, + Verbose const &verbose, + Id id, + Report_update_trigger &report_update_trigger, + Xml_node start_node, + Default_route_accessor &default_route_accessor, + Default_caps_accessor &default_caps_accessor, + Name_registry &name_registry, + Ram_limit_accessor &ram_limit_accessor, + Cap_limit_accessor &cap_limit_accessor, + Cpu_limit_accessor &cpu_limit_accessor, + Cpu_quota_transfer &cpu_quota_transfer, + Prio_levels prio_levels, + Affinity::Space const &affinity_space, + Registry &parent_services, + Registry &child_services, + Registry &local_services, + Pd_intrinsics &pd_intrinsics) + : _env(env), _alloc(alloc), _verbose(verbose), _id(id), + _report_update_trigger(report_update_trigger), + _list_element(this), + _start_node(_alloc, start_node), + _default_route_accessor(default_route_accessor), + _default_caps_accessor(default_caps_accessor), + _ram_limit_accessor(ram_limit_accessor), + _cap_limit_accessor(cap_limit_accessor), + _cpu_limit_accessor(cpu_limit_accessor), + _cpu_quota_transfer(cpu_quota_transfer), + _name_registry(name_registry), + _heartbeat_enabled(start_node.has_sub_node("heartbeat")), + _resources(_resources_from_start_node(start_node, prio_levels, affinity_space, + default_caps_accessor.default_caps())), + _pd_intrinsics(pd_intrinsics), + _parent_services(parent_services), + _child_services(child_services), + _local_services(local_services), + _session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()) { - if (_verbose.enabled()) { - log("child \"", _unique_name, "\""); + if (_verbose.enabled()) + { + log("child \"", _unique_name, "\""); log(" RAM quota: ", _resources.effective_ram_quota()); log(" cap quota: ", _resources.effective_cap_quota()); log(" ELF binary: ", _binary_name); @@ -792,7 +831,8 @@ Sandbox::Child::Child(Env &env, */ _provides_sub_node(start_node) .for_each_sub_node("service", - [&] (Xml_node node) { _add_service(node); }); + [&](Xml_node node) + { _add_service(node); }); /* * Construct inline config ROM service if "config" node is present. @@ -801,5 +841,4 @@ Sandbox::Child::Child(Env &env, _config_rom_service.construct(*this); } - -Sandbox::Child::~Child() { } +Sandbox::Child::~Child() {} \ No newline at end of file diff --git a/repos/os/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc index 56451d7ec9..cf238c3680 100644 --- a/repos/os/src/lib/sandbox/library.cc +++ b/repos/os/src/lib/sandbox/library.cc @@ -385,7 +385,7 @@ void Genode::Sandbox::generate_state_report(Xml_generator &xml) const Genode::Sandbox::Sandbox(Env &env, State_handler &state_handler, Pd_intrinsics &pd_intrinsics) : _heap(env.ram(), env.rm()), - _library(*new (_heap) Library(env, _heap, _local_services, state_handler, pd_intrinsics)) + _library(*new (_heap) ::Sandbox::Library(env, _heap, _local_services, state_handler, pd_intrinsics)) { }