diff --git a/repos/os/include/sandbox/alias.h b/repos/os/include/sandbox/alias.h new file mode 100644 index 0000000000..17d6120341 --- /dev/null +++ b/repos/os/include/sandbox/alias.h @@ -0,0 +1,47 @@ +/* + * \brief Representation of an alias for a child + * \author Norman Feske + * \date 2010-04-27 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__ALIAS_H_ +#define _LIB__SANDBOX__ALIAS_H_ + +/* local includes */ +#include + +namespace Sandbox { struct Alias; } + +struct Sandbox::Alias : List::Element, Noncopyable +{ + typedef Child_policy::Name Name; + typedef Child_policy::Name Child; + + Name const name; + + Child child { }; /* defined by 'update' */ + + Alias(Name const &name) : name(name) { } + + class Child_attribute_missing : Exception { }; + + /* + * \throw Child_attribute_missing + */ + void update(Xml_node const &alias) + { + if (!alias.has_attribute("child")) + warning("alias node \"", name, "\" lacks child attribute"); + + child = alias.attribute_value("child", Child()); + } +}; + +#endif /* _LIB__SANDBOX__ALIAS_H_ */ diff --git a/repos/os/include/sandbox/child.h b/repos/os/include/sandbox/child.h new file mode 100644 index 0000000000..890e0344b8 --- /dev/null +++ b/repos/os/include/sandbox/child.h @@ -0,0 +1,742 @@ +/* + * \brief Child representation + * \author Norman Feske + * \date 2010-05-04 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__CHILD_H_ +#define _LIB__SANDBOX__CHILD_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* local includes */ +#include +#include +#include +#include +#include +#include +#include + +namespace Sandbox { class Child; } + +class Sandbox::Child : Child_policy, Routed_service::Wakeup +{ + public: + + 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 }; + + 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(); + } + + 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); + } + + /** + * 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; + + 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 { + 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}; + } + }; + + 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; + + 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; + Name const name = rsc.attribute_value("name", Name()); + + if (name == "RAM") + ram_bytes = rsc.attribute_value("quantum", ram_bytes); + + if (name == "CPU") + cpu_percent = rsc.attribute_value("quantum", 0U); + + if (name == "CAP") + caps = rsc.attribute_value("quantum", 0UL); + }); + + return Resources { log2(prio_levels.value), + priority_from_xml(start_node, prio_levels), + Affinity(affinity_space, + affinity_location_from_xml(affinity_space, start_node)), + Ram_quota { ram_bytes }, + Cap_quota { caps }, + Cpu_quota { cpu_percent } }; + } + + Resources _resources; + + Ram_quota _configured_ram_quota() const; + Cap_quota _configured_cap_quota() const; + + 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 + { + typedef Genode::Local_service Service; + + Child &_child; + + Dynamic_rom_session _session { _child._env.ep().rpc_ep(), + _child.ref_pd(), _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 + { + 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) { + + /* + * The 'length' is the number of bytes of the config-node + * content, which is not null-terminated. Since + * 'Genode::copy_cstring' always null-terminates the + * result, the last byte of the source string is not + * copied. Hence, it is safe to add '1' to 'length' and + * 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(); + } + + 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 { + 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) + { + 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(); + } + + 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) }; + } + + 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) { + if (service.has_id_space(_session_requester.id_space())) + service.abandon(); }); + } + + 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); + + 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) { + _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; + } + } + + /* + * 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 destroy_services(); + + void close_all_sessions() { _child.close_all_sessions(); } + + bool abandoned() const { return _state == State::ABANDONED; } + + bool restart_scheduled() const { return _state == State::RESTART_SCHEDULED; } + + bool stuck() const { return _state == State::STUCK; } + + bool env_sessions_closed() const { return _child.env_sessions_closed(); } + + enum Apply_config_result { PROVIDED_SERVICES_CHANGED, NO_SIDE_EFFECTS }; + + /** + * 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); + + bool uncertain_dependencies() const { return _uncertain_dependencies; } + + /** + * Validate that the routes of all existing sessions remain intact + * + * The child may become scheduled for restart or get stuck. + */ + void evaluate_dependencies(); + + /* common code for upgrading RAM and caps */ + template + void _apply_resource_upgrade(QUOTA &, QUOTA, LIMIT_ACCESSOR const &); + + template + void _apply_resource_downgrade(QUOTA &, QUOTA, QUOTA, + CHILD_AVAIL_QUOTA_FN const &); + + void apply_upgrade(); + void apply_downgrade(); + + void heartbeat() + { + if (_heartbeat_expected()) + _child.heartbeat(); + + unsigned const skipped_heartbeats = _child.skipped_heartbeats(); + + 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(); + + _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); + } + + void session_state_changed() override + { + _report_update_trigger.trigger_report_update(); + } + + bool initiate_env_sessions() const override { return false; } + + void yield_response() override + { + apply_downgrade(); + _report_update_trigger.trigger_report_update(); + } +}; + +#endif /* _LIB__SANDBOX__CHILD_H_ */ diff --git a/repos/os/include/sandbox/child_registry.h b/repos/os/include/sandbox/child_registry.h new file mode 100644 index 0000000000..7fdb834357 --- /dev/null +++ b/repos/os/include/sandbox/child_registry.h @@ -0,0 +1,117 @@ +/* + * \brief Child registry + * \author Norman Feske + * \date 2010-04-27 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__CHILD_REGISTRY_H_ +#define _LIB__SANDBOX__CHILD_REGISTRY_H_ + +/* local includes */ +#include +#include +#include +#include + +namespace Sandbox { struct Child_registry; } + + +class Sandbox::Child_registry : public Name_registry, Child_list +{ + private: + + List _aliases { }; + + public: + + /** + * Register child + */ + void insert(Child *child) + { + Child_list::insert(&child->_list_element); + } + + /** + * Unregister child + */ + void remove(Child *child) + { + Child_list::remove(&child->_list_element); + } + + /** + * Register alias + */ + void insert_alias(Alias *alias) + { + _aliases.insert(alias); + } + + /** + * Unregister alias + */ + void remove_alias(Alias *alias) + { + _aliases.remove(alias); + } + + template + void for_each_child(FN const &fn) const + { + Genode::List_element const *curr = first(); + for (; curr; curr = curr->next()) + fn(*curr->object()); + } + + template + void for_each_child(FN const &fn) + { + Genode::List_element *curr = first(), *next = nullptr; + for (; curr; curr = next) { + next = curr->next(); + fn(*curr->object()); + } + } + + void report_state(Xml_generator &xml, Report_detail const &detail) const + { + for_each_child([&] (Child &child) { child.report_state(xml, detail); }); + + for (Alias const *a = _aliases.first(); a; a = a->next()) { + xml.node("alias", [&] () { + xml.attribute("name", a->name); + xml.attribute("child", a->child); + }); + } + } + + Child::Sample_state_result sample_state() + { + auto result = Child::Sample_state_result::UNCHANGED; + + for_each_child([&] (Child &child) { + if (result == Child::Sample_state_result::UNCHANGED) + result = child.sample_state(); }); + + return result; + } + + Child::Name deref_alias(Child::Name const &name) override + { + for (Alias const *a = _aliases.first(); a; a = a->next()) + if (name == a->name) + return a->child; + + return name; + } +}; + +#endif /* _LIB__SANDBOX__CHILD_REGISTRY_H_ */ diff --git a/repos/os/include/sandbox/config_model.h b/repos/os/include/sandbox/config_model.h new file mode 100644 index 0000000000..1b94ba0221 --- /dev/null +++ b/repos/os/include/sandbox/config_model.h @@ -0,0 +1,292 @@ +/* + * \brief Internal model of the XML configuration + * \author Norman Feske + * \date 2021-04-01 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _CONFIG_MODEL_H_ +#define _CONFIG_MODEL_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include + +namespace Sandbox { + + struct Parent_provides_model; + struct Start_model; + struct Service_model; + struct Config_model; +} + + +struct Sandbox::Parent_provides_model : Noncopyable +{ + struct Factory : Interface, Noncopyable + { + virtual Parent_service &create_parent_service(Service::Name const &) = 0; + }; + + Allocator &_alloc; + Verbose const &_verbose; + Factory &_factory; + + struct Node : Noncopyable, List_model::Element + { + Parent_service &service; + + Node(Factory &factory, Service::Name const &name) + : + service(factory.create_parent_service(name)) + { } + + ~Node() + { + /* + * The destruction of the 'Parent_service' is deferred to the + * handling of abandoned entries of the 'Registry'. + */ + service.abandon(); + } + + static bool type_matches(Xml_node const &) { return true; } + + bool matches(Xml_node const &xml) const + { + return xml.attribute_value("name", Service::Name()) == service.name(); + }; + }; + + List_model _model { }; + + Parent_provides_model(Allocator &alloc, Verbose const &verbose, Factory &factory) + : + _alloc(alloc), _verbose(verbose), _factory(factory) + { } + + ~Parent_provides_model() + { + update_from_xml(Xml_node("")); + } + + void update_from_xml(Xml_node const &xml) + { + bool first_log = true; + + auto create = [&] (Xml_node const &xml) -> Node & + { + Service::Name const name = xml.attribute_value("name", Service::Name()); + + if (_verbose.enabled()) { + if (first_log) + log("parent provides"); + + log(" service \"", name, "\""); + first_log = false; + } + + return *new (_alloc) Node(_factory, name); + }; + + auto destroy = [&] (Node &node) { Genode::destroy(_alloc, &node); }; + + auto update = [&] (Node &, Xml_node const &) { }; + + try { + update_list_model_from_xml(_model, xml, create, destroy, update); + } catch (...) { + error("unable to apply complete configuration"); + } + } +}; + + +struct Sandbox::Start_model : Noncopyable +{ + /* + * The 'Start_model' represents both '' nodes and '' nodes + * because both node types share the same name space. + */ + + typedef Child_policy::Name Name; + typedef Child::Version Version; + + static char const *start_type() { return "start"; } + static char const *alias_type() { return "alias"; } + + struct Factory : Interface + { + class Creation_failed : Exception { }; + + virtual bool ready_to_create_child(Name const &, Version const &) const = 0; + + /* + * \throw Creation_failed + */ + virtual Child &create_child(Xml_node const &start) = 0; + + virtual void update_child(Child &child, Xml_node const &start) = 0; + + /* + * \throw Creation_failed + */ + virtual Alias &create_alias(Name const &) = 0; + + virtual void destroy_alias(Alias &) = 0; + }; + + Name const _name; + Version const _version; + + Factory &_factory; + + bool _alias = false; + + struct { Child *_child_ptr = nullptr; }; + struct { Alias *_alias_ptr = nullptr; }; + + void _reset() + { + if (_child_ptr) _child_ptr->abandon(); + if (_alias_ptr) _factory.destroy_alias(*_alias_ptr); + + _child_ptr = nullptr; + _alias_ptr = nullptr; + } + + bool matches(Xml_node const &xml) const + { + return _name == xml.attribute_value("name", Name()) + && _version == xml.attribute_value("version", Version()); + } + + Start_model(Factory &factory, Xml_node const &xml) + : + _name(xml.attribute_value("name", Name())), + _version(xml.attribute_value("version", Version())), + _factory(factory) + { } + + ~Start_model() { _reset(); } + + void update_from_xml(Xml_node const &xml) + { + /* handle case where the node keeps the name but changes the type */ + + bool const orig_alias = _alias; + _alias = xml.has_type("alias"); + if (orig_alias != _alias) + _reset(); + + /* create alias or child depending of the node type */ + + if (_alias) { + + if (!_alias_ptr) + _alias_ptr = &_factory.create_alias(_name); + + } else { + + if (!_child_ptr && _factory.ready_to_create_child(_name, _version)) + _child_ptr = &_factory.create_child(xml); + } + + /* update */ + + if (_alias_ptr) + _alias_ptr->update(xml); + + if (_child_ptr) + _factory.update_child(*_child_ptr, xml); + } + + void apply_child_restart(Xml_node const &xml) + { + if (_child_ptr && _child_ptr->restart_scheduled()) { + + /* tear down */ + _child_ptr->abandon(); + _child_ptr = nullptr; + + /* respawn */ + update_from_xml(xml); + } + } + + void trigger_start_child() + { + if (_child_ptr) + _child_ptr->try_start(); + } +}; + + +struct Sandbox::Service_model : Interface, Noncopyable +{ + struct Factory : Interface, Noncopyable + { + virtual Service_model &create_service(Xml_node const &) = 0; + virtual void destroy_service(Service_model &) = 0; + }; + + virtual void update_from_xml(Xml_node const &) = 0; + + virtual bool matches(Xml_node const &) = 0; +}; + + +class Sandbox::Config_model : Noncopyable +{ + private: + + struct Node; + + List_model _model { }; + + struct Parent_provides_node; + struct Default_route_node; + struct Default_node; + struct Affinity_space_node; + struct Start_node; + struct Report_node; + struct Resource_node; + struct Heartbeat_node; + struct Service_node; + + public: + + typedef State_reporter::Version Version; + + void update_from_xml(Xml_node const &, + Allocator &, + Reconstructible &, + Version &, + Preservation &, + Constructible &, + Cap_quota &, + Prio_levels &, + Constructible &, + Start_model::Factory &, + Parent_provides_model::Factory &, + Service_model::Factory &, + State_reporter &, + Heartbeat &); + + void apply_children_restart(Xml_node const &); + + /* + * Call 'Child::try_start' for each child in start-node order + */ + void trigger_start_children(); +}; + +#endif /* _CONFIG_MODEL_H_ */ diff --git a/repos/os/include/sandbox/heartbeat.h b/repos/os/include/sandbox/heartbeat.h new file mode 100644 index 0000000000..417b29b62a --- /dev/null +++ b/repos/os/include/sandbox/heartbeat.h @@ -0,0 +1,89 @@ +/* + * \brief Heartbeat monitoring + * \author Norman Feske + * \date 2018-11-15 + */ + +/* + * Copyright (C) 2018 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__HEARTBEAT_H_ +#define _LIB__SANDBOX__HEARTBEAT_H_ + +/* local includes */ +#include +#include +#include + +namespace Sandbox { class Heartbeat; } + + +class Sandbox::Heartbeat : Noncopyable +{ + private: + + Env &_env; + + Child_registry &_children; + + Report_update_trigger &_report_update_trigger; + + Constructible _timer { }; + + uint64_t _rate_ms = 0; + + Signal_handler _timer_handler; + + void _handle_timer() + { + bool any_skipped_heartbeats = false; + + _children.for_each_child([&] (Child &child) { + + if (child.skipped_heartbeats()) + any_skipped_heartbeats = true; + + child.heartbeat(); + }); + + if (any_skipped_heartbeats) + _report_update_trigger.trigger_report_update(); + } + + public: + + Heartbeat(Env &env, Child_registry &children, + Report_update_trigger &report_update_trigger) + : + _env(env), _children(children), + _report_update_trigger(report_update_trigger), + _timer_handler(_env.ep(), *this, &Heartbeat::_handle_timer) + { } + + void disable() + { + _timer.destruct(); + _rate_ms = 0; + } + + void apply_config(Xml_node heartbeat) + { + if (!_timer.constructed()) { + _timer.construct(_env); + _timer->sigh(_timer_handler); + } + + unsigned const rate_ms = heartbeat.attribute_value("rate_ms", 1000U); + + if (rate_ms != _rate_ms) { + _rate_ms = rate_ms; + _timer->trigger_periodic(_rate_ms*1000); + } + } +}; + +#endif /* _LIB__SANDBOX__HEARTBEAT_H_ */ diff --git a/repos/os/include/sandbox/library.h b/repos/os/include/sandbox/library.h new file mode 100644 index 0000000000..34992ff068 --- /dev/null +++ b/repos/os/include/sandbox/library.h @@ -0,0 +1,263 @@ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Sandbox { + class Library; +} + +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 +{ + 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 Child_registry = ::Sandbox::Child_registry; + 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; + +public: + Env &_env; + Heap &_heap; + + 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 { }; + + Affinity::Space _effective_affinity_space() const + { + return _affinity_space.constructed() ? *_affinity_space + : Affinity::Space { 1, 1 }; + } + + State_reporter _state_reporter; + + Heartbeat _heartbeat { _env, _children, _state_reporter }; + + /* + * Internal representation of the XML configuration + */ + 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; + + unsigned _child_cnt = 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) { + error("RAM preservation exceeds available memory"); + return Ram_quota { 0 }; + } + + /* deduce preserved quota from available quota */ + return Ram_quota { avail_ram.value - _preservation.ram.value }; + } + + Cap_quota _avail_caps() const + { + Cap_quota avail_caps { _env.pd().avail_caps().value }; + + if (_preservation.caps.value > avail_caps.value) { + error("Capability preservation exceeds available capabilities"); + return Cap_quota { 0 }; + } + + /* deduce preserved quota from available quota */ + return Cap_quota { avail_caps.value - _preservation.caps.value }; + } + + /** + * Child::Ram_limit_accessor interface + */ + Ram_quota resource_limit(Ram_quota const &) const override + { + return _avail_ram(); + } + + /** + * Child::Cap_limit_accessor interface + */ + Cap_quota resource_limit(Cap_quota const &) const override { return _avail_caps(); } + + /** + * Child::Cpu_limit_accessor interface + */ + Cpu_quota resource_limit(Cpu_quota const &) const override { return _avail_cpu; } + + /** + * Child::Cpu_quota_transfer interface + */ + void transfer_cpu_quota(Cpu_session_capability cap, Cpu_quota quota) override + { + Cpu_quota const remaining { 100 - min(100u, _transferred_cpu.percent) }; + + /* prevent division by zero in 'quota_lim_upscale' */ + if (remaining.percent == 0) + return; + + size_t const fraction = + Cpu_session::quota_lim_upscale(quota.percent, remaining.percent); + + _env.cpu().transfer_quota(cap, fraction); + + _transferred_cpu.percent += quota.percent; + } + + /** + * State_reporter::Producer interface + */ + 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); }); + + if (detail.init_caps()) + xml.node("caps", [&] () { Cap_info::from_pd(_env.pd()).generate(xml); }); + + if (detail.children()) + _children.report_state(xml, detail); + } + + /** + * State_reporter::Producer interface + */ + Child::Sample_state_result sample_children_state() override + { + return _children.sample_state(); + } + + /** + * Default_route_accessor interface + */ + Xml_node default_route() override + { + return _default_route.constructed() ? _default_route->xml() + : Xml_node(""); + } + + /** + * Default_caps_accessor interface + */ + Cap_quota default_caps() override { return _default_caps; } + + void _update_aliases_from_config(Xml_node const &); + void _update_parent_services_from_config(Xml_node const &); + void _update_children_config(Xml_node const &); + void _destroy_abandoned_parent_services(); + void _destroy_abandoned_children(); + + Server _server { _env, _heap, _child_services, _state_reporter }; + + /** + * Sandbox::Start_model::Factory + */ + virtual Child &create_child(Xml_node const &) override; + + /** + * Sandbox::Start_model::Factory + */ + virtual void update_child(Child &, Xml_node const &) override; + + /** + * Sandbox::Start_model::Factory + */ + Alias &create_alias(Child_policy::Name const &name) override + { + Alias &alias = *new (_heap) Alias(name); + _children.insert_alias(&alias); + return alias; + } + + /** + * Sandbox::Start_model::Factory + */ + void destroy_alias(Alias &alias) override + { + _children.remove_alias(&alias); + destroy(_heap, &alias); + } + + /** + * Sandbox::Start_model::Factory + */ + bool ready_to_create_child(Start_model::Name const &, + Start_model::Version const &) const override; + + /** + * Sandbox::Parent_provides_model::Factory + */ + Parent_service &create_parent_service(Service::Name const &name) override + { + return *new (_heap) Parent_service(_parent_services, _env, name); + } + + 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) + { } + + virtual void apply_config(Xml_node const &); + + virtual void generate_state_report(Xml_generator &xml) const + { + _state_reporter.generate(xml); + } +}; \ No newline at end of file diff --git a/repos/os/include/sandbox/name_registry.h b/repos/os/include/sandbox/name_registry.h new file mode 100644 index 0000000000..8ba427bdb3 --- /dev/null +++ b/repos/os/include/sandbox/name_registry.h @@ -0,0 +1,39 @@ +/* + * \brief Interface for database of child names + * \author Norman Feske + * \date 2017-03-03 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__NAME_REGISTRY_H_ +#define _LIB__SANDBOX__NAME_REGISTRY_H_ + +/* Genode includes */ +#include + +/* local includes */ +#include + +namespace Sandbox { struct Name_registry; } + +struct Sandbox::Name_registry +{ + virtual ~Name_registry() { } + + typedef Child_policy::Name Name; + + /** + * Return child name for a given alias name + * + * If there is no alias, the function returns the original name. + */ + virtual Name deref_alias(Name const &) = 0; +}; + +#endif /* _LIB__SANDBOX__NAME_REGISTRY_H_ */ diff --git a/repos/os/include/sandbox/report.h b/repos/os/include/sandbox/report.h new file mode 100644 index 0000000000..6b04a6eaee --- /dev/null +++ b/repos/os/include/sandbox/report.h @@ -0,0 +1,91 @@ +/* + * \brief Report configuration + * \author Norman Feske + * \date 2017-01-16 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__REPORT_H_ +#define _LIB__SANDBOX__REPORT_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include + +namespace Sandbox { + struct Report_update_trigger; + struct Report_detail; +} + + +class Sandbox::Report_detail : Genode::Noncopyable +{ + private: + + bool _children = false; + bool _ids = false; + bool _requested = false; + bool _provided = false; + bool _session_args = false; + bool _child_ram = false; + bool _child_caps = false; + bool _init_ram = false; + bool _init_caps = false; + + public: + + Report_detail() { } + + Report_detail(Genode::Xml_node report) + { + _children = true; + _ids = report.attribute_value("ids", false); + _requested = report.attribute_value("requested", false); + _provided = report.attribute_value("provided", false); + _session_args = report.attribute_value("session_args", false); + _child_ram = report.attribute_value("child_ram", false); + _child_caps = report.attribute_value("child_caps", false); + _init_ram = report.attribute_value("init_ram", false); + _init_caps = report.attribute_value("init_caps", false); + } + + bool children() const { return _children; } + bool ids() const { return _ids; } + bool requested() const { return _requested; } + bool provided() const { return _provided; } + bool session_args() const { return _session_args; } + bool child_ram() const { return _child_ram; } + bool child_caps() const { return _child_caps; } + bool init_ram() const { return _init_ram; } + bool init_caps() const { return _init_caps; } +}; + + +struct Sandbox::Report_update_trigger : Interface +{ + /** + * Trigger regular (rate-limited) report update + */ + virtual void trigger_report_update() = 0; + + /** + * Trigger immediate report update + * + * This method is intended for situations that require a timely response of + * the consumer of the report. This is particularly important for resource + * requests that would otherwise unnecessarily stall the execution of the + * respective child. + */ + virtual void trigger_immediate_report_update() = 0; +}; + +#endif /* _LIB__SANDBOX__REPORT_H_ */ diff --git a/repos/os/include/sandbox/route_model.h b/repos/os/include/sandbox/route_model.h new file mode 100644 index 0000000000..a0c31ff13c --- /dev/null +++ b/repos/os/include/sandbox/route_model.h @@ -0,0 +1,280 @@ +/* + * \brief Internal model of routing rules + * \author Norman Feske + * \date 2021-04-05 + */ + +/* + * Copyright (C) 2021 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _ROUTE_MODEL_H_ +#define _ROUTE_MODEL_H_ + +/* local includes */ +#include + +namespace Sandbox { + + struct Checksum; + class Route_model; +} + + +struct Sandbox::Checksum +{ + unsigned long value = 0; + + bool valid; + + /** + * Constructor + */ + Checksum(char const *s) : valid(s != nullptr) + { + if (!s) + return; + + while (uint8_t const byte = *s++) { + + /* rotate value */ + unsigned long const sign_bit = ((long)value < 0); + value = (value << 1) | sign_bit; + + /* xor byte to lowest 8 bit */ + value = value ^ (unsigned long)byte; + } + } + + template + Checksum(String const &s) : Checksum(s.string()) { } + + bool operator != (Checksum const &other) const + { + return (other.value != value) || !valid; + } +}; + + +class Sandbox::Route_model : Noncopyable +{ + public: + + struct Query : Noncopyable + { + Child_policy::Name const &child; + Service::Name const &service; + Session_label const &label; + + Checksum const service_checksum { service }; + Checksum const label_checksum { skip_label_prefix(child.string(), + label.string()) }; + + Query(Child_policy::Name const &child, + Service::Name const &service, + Session_label const &label) + : + child(child), service(service), label(label) + { } + }; + + class Rule : Noncopyable, List::Element + { + private: + + friend class List; + friend class Route_model; + friend void Genode::destroy(Allocator &, Rule *); + + Allocator &_alloc; + + Xml_node const _node; /* points to 'Route_model::_route_node' */ + + struct Selector + { + typedef String Label; + + enum class Type + { + NO_LABEL, SPECIFIC_LABEL, + + /* + * Presence of 'label_last', 'label_prefix', + * 'label_suffix', 'unscoped_label', or even + * a combination of attributes. + */ + COMPLICATED + + } type = Type::NO_LABEL; + + Checksum label_checksum { "" }; + + Selector(Xml_node const &node) + { + bool const complicated = + node.has_attribute("label_prefix") || + node.has_attribute("label_suffix") || + node.has_attribute("label_last") || + node.has_attribute("unscoped_label"); + + if (complicated) { + type = Type::COMPLICATED; + return; + } + + Label const label = node.attribute_value("label", Label()); + if (label.valid()) { + type = Type::SPECIFIC_LABEL; + label_checksum = Checksum(label); + } + } + }; + + Selector const _selector; + Checksum const _service_checksum; + bool const _specific_service { _node.has_type("service") }; + + struct Target : Noncopyable, private List::Element + { + friend class List; + friend class Rule; + + Xml_node const node; /* points to 'Route_model::_route_node' */ + + Target(Xml_node const &node) : node(node) { } + }; + + List _targets { }; + + /** + * Constructor is private to 'Route_model' + */ + Rule(Allocator &alloc, Xml_node const &node) + : + _alloc(alloc), _node(node), _selector(node), + _service_checksum(node.attribute_value("name", Service::Name())) + { + Target const *at_ptr = nullptr; + node.for_each_sub_node([&] (Xml_node sub_node) { + Target &target = *new (_alloc) Target(sub_node); + _targets.insert(&target, at_ptr); + at_ptr = ⌖ + }); + } + + ~Rule() + { + while (Target *target_ptr = _targets.first()) { + _targets.remove(target_ptr); + destroy(_alloc, target_ptr); + } + } + + /** + * Quick check for early detection of definite mismatches + * + * \return true if query definitely mismatches the rule, + * false if the undecided + */ + bool _mismatches(Query const &query) const + { + if (_specific_service + && query.service_checksum != _service_checksum) + return true; + + if (_selector.type == Selector::Type::SPECIFIC_LABEL + && query.label_checksum != _selector.label_checksum) + return true; + + return false; + } + + public: + + bool matches(Query const &query) const + { + /* handle common case */ + if (_mismatches(query)) + return false; + + return service_node_matches(_node, + query.label, + query.child, + query.service); + } + + template + Child_policy::Route resolve(FN const &fn) const + { + for (Target const *t = _targets.first(); t; t = t->next()) { + try { return fn(t->node); } + catch (Service_denied) { /* try next target */ } + } + + /* query is not accepted by any of the targets */ + throw Service_denied(); + } + }; + + private: + + Allocator &_alloc; + + Buffered_xml const _route_node; + + List _rules { }; + + public: + + Route_model(Allocator &alloc, Xml_node const &route) + : + _alloc(alloc), _route_node(_alloc, route) + { + Rule const *at_ptr = nullptr; + _route_node.xml().for_each_sub_node([&] (Xml_node const &node) { + Rule &rule = *new (_alloc) Rule(_alloc, node); + _rules.insert(&rule, at_ptr); /* append */ + at_ptr = &rule; + }); + } + + ~Route_model() + { + while (Rule *rule_ptr = _rules.first()) { + _rules.remove(rule_ptr); + destroy(_alloc, rule_ptr); + } + } + + template + Child_policy::Route resolve(Query const &query, FN const &fn) const + { + for (Rule const *r = _rules.first(); r; r = r->next()) + if (r->matches(query)) { + try { + return r->resolve(fn); + } + catch (Service_denied) { + if (r->_specific_service) + throw; + + /* + * If none of the targets of a wildcard rule was + * satisfied with the query, continue with the next + * rule. + */ + } + } + + warning(query.child, ": no route to " + "service \"", query.service, "\" " + "(label=\"", query.label, "\")"); + + throw Service_denied(); + } +}; + +#endif /* _ROUTE_MODEL_H_ */ diff --git a/repos/os/include/sandbox/sandbox.h b/repos/os/include/sandbox/sandbox.h index 9f2b7ce60f..369468cc3f 100644 --- a/repos/os/include/sandbox/sandbox.h +++ b/repos/os/include/sandbox/sandbox.h @@ -21,7 +21,7 @@ #include namespace Genode { class Sandbox; } - +namespace Sandbox { class Library; } class Genode::Sandbox : Noncopyable { @@ -50,9 +50,7 @@ class Genode::Sandbox : Noncopyable Heap _heap; - class Library; - - Library &_library; + ::Sandbox::Library &_library; Registry _local_services { }; @@ -60,7 +58,9 @@ class Genode::Sandbox : Noncopyable Sandbox(Env &, State_handler &); - void apply_config(Xml_node const &); + virtual ~Sandbox() = default; + + virtual void apply_config(Xml_node const &); /** * Generate state report as configured by the config node diff --git a/repos/os/include/sandbox/server.h b/repos/os/include/sandbox/server.h new file mode 100644 index 0000000000..c6d4e9ac61 --- /dev/null +++ b/repos/os/include/sandbox/server.h @@ -0,0 +1,131 @@ +/* + * \brief Server role of init, forwarding session requests to children + * \author Norman Feske + * \date 2017-03-07 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__SERVER_H_ +#define _LIB__SANDBOX__SERVER_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include +#include +#include +#include + +namespace Sandbox { class Server; } + + +class Sandbox::Server : Session_state::Ready_callback, + Session_state::Closed_callback, + public Service_model::Factory +{ + private: + + struct Route + { + Routed_service &service; + Session_label label; + }; + + Env &_env; + + Allocator &_alloc; + + /* + * ID space of requests originating from the parent + */ + Id_space _server_id_space { }; + + /* + * ID space of requests issued to the children + */ + Id_space _client_id_space { }; + + /** + * Exception type + */ + class Service_not_present : Exception { }; + + /** + * Meta data of service provided to our parent + */ + struct Service; + + Registry _services { }; + + /** + * Services provided by our children + */ + Registry &_child_services; + + Report_update_trigger &_report_update_trigger; + + Constructible _session_requests { }; + Constructible > _session_request_handler { }; + + /** + * \throw Service_denied + */ + Route _resolve_session_request(Genode::Service::Name const &, + Session_label const &); + + void _handle_create_session_request (Xml_node, Parent::Client::Id); + void _handle_upgrade_session_request(Xml_node, Parent::Client::Id); + void _handle_close_session_request (Xml_node, Parent::Client::Id); + + void _handle_session_request(Xml_node); + void _handle_session_requests(); + + void _close_session(Session_state &, Parent::Session_response response); + + /** + * Session_state::Closed_callback interface + */ + void session_closed(Session_state &) override; + + /** + * Session_state::Ready_callback interface + */ + void session_ready(Session_state &) override; + + public: + + /** + * Constructor + * + * \param alloc allocator used for buffering XML config data and + * for allocating per-service meta data + */ + Server(Env &env, Allocator &alloc, Registry &services, + Report_update_trigger &report_update_trigger) + : + _env(env), _alloc(alloc), _child_services(services), + _report_update_trigger(report_update_trigger) + { } + + void apply_updated_policy(); + + /** + * Service_model::Factory + */ + Service_model &create_service(Xml_node const &) override; + + /** + * Service_model::Factory + */ + void destroy_service(Service_model &) override; +}; + +#endif /* _LIB__SANDBOX__SERVER_H_ */ diff --git a/repos/os/include/sandbox/service.h b/repos/os/include/sandbox/service.h new file mode 100644 index 0000000000..9589de91b5 --- /dev/null +++ b/repos/os/include/sandbox/service.h @@ -0,0 +1,151 @@ +/* + * \brief Services as targeted by session routes + * \author Norman Feske + * \date 2017-03-03 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__SERVICE_H_ +#define _LIB__SANDBOX__SERVICE_H_ + +/* Genode includes */ +#include +#include + +namespace Sandbox { + class Abandonable; + class Parent_service; + class Routed_service; + class Forwarded_service; +} + + +class Sandbox::Abandonable : Interface +{ + private: + + bool _abandoned = false; + + public: + + void abandon() { _abandoned = true; } + + bool abandoned() const { return _abandoned; } +}; + + +class Sandbox::Parent_service : public Genode::Try_parent_service, public Abandonable +{ + private: + + Registry::Element _reg_elem; + + public: + + Parent_service(Registry ®istry, Env &env, + Service::Name const &name) + : + Genode::Try_parent_service(env, name), _reg_elem(registry, *this) + { } +}; + + +/** + * Sandbox-specific representation of a child service + */ +class Sandbox::Routed_service : public Async_service, public Abandonable +{ + public: + + typedef Child_policy::Name Child_name; + + struct Pd_accessor : Interface + { + virtual Pd_session &pd() = 0; + virtual Pd_session_capability pd_cap() const = 0; + }; + + struct Ram_accessor : Interface + { + virtual Pd_session &ram() = 0; + virtual Pd_session_capability ram_cap() const = 0; + }; + + private: + + Child_name _child_name; + + Pd_accessor &_pd_accessor; + + Session_state::Factory &_factory; + + Registry::Element _registry_element; + + public: + + /** + * Constructor + * + * \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 'Async_service'. + */ + Routed_service(Registry &services, + Child_name const &child_name, + Pd_accessor &pd_accessor, + Ram_accessor &, + Id_space &server_id_space, + Session_state::Factory &factory, + Service::Name const &name, + Wakeup &wakeup) + : + Async_service(name, server_id_space, factory, wakeup), + _child_name(child_name), _pd_accessor(pd_accessor), + _factory(factory), _registry_element(services, *this) + { } + + Child_name const &child_name() const { return _child_name; } + + Session_state::Factory &factory() { return _factory; } + + /** + * Ram_transfer::Account interface + */ + void transfer(Pd_session_capability to, Ram_quota amount) override + { + if (to.valid()) _pd_accessor.pd().transfer_quota(to, amount); + } + + /** + * Ram_transfer::Account interface + */ + Pd_session_capability cap(Ram_quota) const override + { + return _pd_accessor.pd_cap(); + } + + /** + * Cap_transfer::Account interface + */ + void transfer(Pd_session_capability to, Cap_quota amount) override + { + if (to.valid()) _pd_accessor.pd().transfer_quota(to, amount); + } + + /** + * Cap_transfer::Account interface + */ + Pd_session_capability cap(Cap_quota) const override + { + return _pd_accessor.pd_cap(); + } +}; + +#endif /* _LIB__SANDBOX__SERVICE_H_ */ diff --git a/repos/os/include/sandbox/state_reporter.h b/repos/os/include/sandbox/state_reporter.h new file mode 100644 index 0000000000..8d4015d318 --- /dev/null +++ b/repos/os/include/sandbox/state_reporter.h @@ -0,0 +1,191 @@ +/* + * \brief State reporting mechanism + * \author Norman Feske + * \date 2017-03-03 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__STATE_REPORTER_H_ +#define _LIB__SANDBOX__STATE_REPORTER_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include +#include + +namespace Sandbox { class State_reporter; } + + +class Sandbox::State_reporter : public Report_update_trigger +{ + public: + + typedef String<64> Version; + + struct Producer : Interface + { + virtual void produce_state_report(Xml_generator &xml, + Report_detail const &) const = 0; + + virtual Child::Sample_state_result sample_children_state() = 0; + }; + + private: + + using State_handler = Genode::Sandbox::State_handler; + + Env &_env; + + Producer &_producer; + + Reconstructible _report_detail { }; + + uint64_t _report_delay_ms = 0; + + /* interval used when child-ram reporting is enabled */ + uint64_t _report_period_ms = 0; + + /* version string from config, to be reflected in the report */ + Version _version { }; + + Constructible _timer { }; + Constructible _timer_periodic { }; + + Signal_handler _timer_handler { + _env.ep(), *this, &State_reporter::_handle_timer }; + + Signal_handler _timer_periodic_handler { + _env.ep(), *this, &State_reporter::_handle_periodic_timer }; + + Signal_handler _immediate_handler { + _env.ep(), *this, &State_reporter::_handle_timer }; + + bool _scheduled = false; + + State_handler &_state_handler; + + bool _periodic_sampling_needed() const + { + return _report_detail->child_ram() + || _report_detail->child_caps(); + } + + void _handle_periodic_timer() + { + if (!_periodic_sampling_needed()) + return; + + if (_producer.sample_children_state() == Child::Sample_state_result::CHANGED) + _handle_timer(); + } + + void _handle_timer() + { + _scheduled = false; + + _state_handler.handle_sandbox_state(); + } + + public: + + State_reporter(Env &env, Producer &producer, State_handler &state_handler) + : + _env(env), _producer(producer), + _state_handler(state_handler) + { } + + void generate(Xml_generator &xml) const + { + if (_version.valid()) + xml.attribute("version", _version); + + if (_report_detail.constructed()) + _producer.produce_state_report(xml, *_report_detail); + } + + void apply_config(Version const &version, Xml_node const &report) + { + if (report.type() == "report") { + _report_detail.construct(report); + _report_delay_ms = report.attribute_value("delay_ms", 100UL); + } else { + _report_detail.construct(); + _report_delay_ms = 0; + } + + bool trigger_update = false; + + if (version != _version) { + _version = version; + trigger_update = true; + } + + if (_report_delay_ms) { + if (!_timer.constructed()) { + _timer.construct(_env); + _timer->sigh(_timer_handler); + } + trigger_update = true; + } + + if (trigger_update) + trigger_report_update(); + + /* + * If the report features information about child-RAM quotas, we + * update the report periodically. Even in the absence of any other + * report-triggering event, a child may consume/free RAM from its + * RAM session without any interplay with the sandbox. The periodic + * reports ensure that such changes are reflected by the sandbox' + * state report. + * + * By default, the interval is one second. However, when the + * 'delay_ms' attribute is defined with a higher value than that, + * the user intends to limit the rate of state reports. If so, we + * use the value of 'delay_ms' as interval. + */ + uint64_t const period_ms = max((uint64_t)1000, _report_delay_ms); + bool const period_changed = (_report_period_ms != period_ms); + bool const report_periodically = _periodic_sampling_needed(); + + if (report_periodically && !_timer_periodic.constructed()) { + _timer_periodic.construct(_env); + _timer_periodic->sigh(_timer_periodic_handler); + } + + if (!report_periodically && _timer_periodic.constructed()) { + _report_period_ms = 0; + _timer_periodic.destruct(); + } + + if (period_changed && _timer_periodic.constructed()) { + _report_period_ms = period_ms; + _timer_periodic->trigger_periodic(1000*_report_period_ms); + } + } + + void trigger_report_update() override + { + if (!_scheduled && _timer.constructed() && _report_delay_ms) { + _timer->trigger_once(_report_delay_ms*1000); + _scheduled = true; + } + } + + void trigger_immediate_report_update() override + { + if (_report_delay_ms) + Signal_transmitter(_immediate_handler).submit(); + } +}; + +#endif /* _LIB__SANDBOX__STATE_REPORTER_H_ */ diff --git a/repos/os/include/sandbox/types.h b/repos/os/include/sandbox/types.h new file mode 100644 index 0000000000..56fd8ddd0d --- /dev/null +++ b/repos/os/include/sandbox/types.h @@ -0,0 +1,100 @@ +/* + * \brief Common types used within init + * \author Norman Feske + * \date 2017-03-03 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__TYPES_H_ +#define _LIB__SANDBOX__TYPES_H_ + +#include +#include +#include + +namespace Sandbox { + + class Child; + + using namespace Genode; + using Genode::size_t; + using Genode::strlen; + + struct Prio_levels { long value; }; + + struct Cpu_quota + { + unsigned percent; + + void print(Output &out) const { Genode::print(out, percent, "%"); } + }; + + typedef List > Child_list; + + template + struct Resource_info + { + T quota, used, avail; + + static Resource_info from_pd(Pd_session const &pd); + + void generate(Xml_generator &xml) const + { + typedef String<32> Value; + xml.attribute("quota", Value(quota)); + xml.attribute("used", Value(used)); + xml.attribute("avail", Value(avail)); + } + + bool operator != (Resource_info const &other) const + { + return quota.value != other.quota.value + || used.value != other.used.value + || avail.value != other.avail.value; + } + }; + + typedef Resource_info Ram_info; + typedef Resource_info Cap_info; + + template <> + inline Ram_info Ram_info::from_pd(Pd_session const &pd) + { + return { .quota = pd.ram_quota(), + .used = pd.used_ram(), + .avail = pd.avail_ram() }; + } + + template <> + inline Cap_info Cap_info::from_pd(Pd_session const &pd) + { + return { .quota = pd.cap_quota(), + .used = pd.used_caps(), + .avail = pd.avail_caps() }; + } + + struct Preservation : private Noncopyable + { + static Ram_quota default_ram() { return { 40*sizeof(long)*1024 }; } + static Cap_quota default_caps() { return { 20 }; } + + Ram_quota ram { }; + Cap_quota caps { }; + + void reset() + { + ram = default_ram(); + caps = default_caps(); + } + + Preservation() { reset(); } + }; +} + +#endif /* _LIB__SANDBOX__TYPES_H_ */ diff --git a/repos/os/include/sandbox/utils.h b/repos/os/include/sandbox/utils.h new file mode 100644 index 0000000000..cba9ca5d2c --- /dev/null +++ b/repos/os/include/sandbox/utils.h @@ -0,0 +1,233 @@ +/* + * \brief Utilities + * \author Norman Feske + * \date 2010-05-04 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__UTILS_H_ +#define _LIB__SANDBOX__UTILS_H_ + +namespace Sandbox { + + static inline void warn_insuff_quota(size_t const avail) + { + warning("specified quota exceeds available quota, " + "proceeding with a quota of ", avail); + } + + + /** + * Return sub string of label with the leading child name stripped out + * + * \return character pointer to the scoped part of the label, + * or nullptr if the label is not correctly prefixed with the child's + * name + */ + inline char const *skip_label_prefix(char const *child_name, char const *label) + { + size_t const child_name_len = strlen(child_name); + + if (strcmp(child_name, label, child_name_len) != 0) + return nullptr; + + label += child_name_len; + + /* + * Skip label separator. This condition should be always satisfied. + */ + if (strcmp(" -> ", label, 4) != 0) + return nullptr; + + return label + 4; + } + + + /** + * Return true if service XML node matches service request + * + * \param args session arguments, inspected for the session label + * \param child_name name of the originator of the session request + * \param service_name name of the requested service + */ + inline bool service_node_matches(Xml_node const service_node, + Session_label const &label, + Child_policy::Name const &child_name, + Service::Name const &service_name) + { + bool const service_matches = + service_node.has_type("any-service") || + (service_node.has_type("service") && + service_node.attribute_value("name", Service::Name()) == service_name); + + if (!service_matches) + return false; + + typedef String Label; + + char const *unscoped_attr = "unscoped_label"; + char const *label_last_attr = "label_last"; + + bool const route_depends_on_child_provided_label = + service_node.has_attribute("label") || + service_node.has_attribute("label_prefix") || + service_node.has_attribute("label_suffix") || + service_node.has_attribute(label_last_attr); + + if (service_node.has_attribute(unscoped_attr)) { + + /* + * If an 'unscoped_label' attribute is provided, don't consider any + * scoped label attribute. + */ + if (route_depends_on_child_provided_label) + warning("service node contains both scoped and unscoped label attributes"); + + return label == service_node.attribute_value(unscoped_attr, Label()); + } + + if (service_node.has_attribute(label_last_attr)) + return service_node.attribute_value(label_last_attr, Label()) == label.last_element(); + + if (!route_depends_on_child_provided_label) + return true; + + char const * const scoped_label = skip_label_prefix( + child_name.string(), label.string()); + + if (!scoped_label) + return false; + + Session_label const session_label(scoped_label); + + return !Xml_node_label_score(service_node, session_label).conflict(); + } + + + /** + * Check if service name is ambiguous + * + * \return true if the same service is provided multiple + * times + * + * \deprecated + */ + template + inline bool is_ambiguous(Registry const &services, Service::Name const &name) + { + /* count number of services with the specified name */ + unsigned cnt = 0; + services.for_each([&] (T const &service) { + if (!service.abandoned()) + cnt += (service.name() == name); }); + + return cnt > 1; + } + + /** + * Find service with certain values in given registry + * + * \param services service registry + * \param name name of wanted service + * \param filter_fn function that applies additional filters + * + * \throw Service_denied + */ + template + inline T &find_service(Registry &services, + Service::Name const &name, + FILTER_FN const &filter_fn) + { + T *service = nullptr; + services.for_each([&] (T &s) { + + if (service || s.name() != name || filter_fn(s)) + return; + + service = &s; + }); + + if (!service) + throw Service_denied(); + + if (service->abandoned()) + throw Service_denied(); + + return *service; + } + + /** + * Read priority-levels declaration from config + */ + inline Prio_levels prio_levels_from_xml(Xml_node const &config) + { + long const prio_levels = config.attribute_value("prio_levels", 0L); + + if (prio_levels && ((prio_levels >= (long)sizeof(prio_levels)*8) || + (prio_levels != (1L << log2(prio_levels))))) { + warning("prio levels is not power of two, priorities are disabled"); + return Prio_levels { 0 }; + } + return Prio_levels { prio_levels }; + } + + + inline long priority_from_xml(Xml_node start_node, Prio_levels prio_levels) + { + long const default_priority = Cpu_session::DEFAULT_PRIORITY; + + long priority = start_node.attribute_value("priority", default_priority); + + /* + * All priority declarations in the config file are + * negative because child priorities can never be higher + * than parent priorities. To simplify priority + * calculations, we use inverted values. Lower values + * correspond to higher priorities. + */ + priority = -priority; + + if (priority && (priority >= prio_levels.value)) { + long new_prio = prio_levels.value ? prio_levels.value - 1 : 0; + + Service::Name const name = start_node.attribute_value("name", Service::Name()); + warning(name, ": invalid priority, upgrading " + "from ", -priority, " to ", -new_prio); + return new_prio; + } + + return priority; + } + + + inline Affinity::Location + affinity_location_from_xml(Affinity::Space const &space, Xml_node start_node) + { + typedef Affinity::Location Location; + + Location result = Location(0, 0, space.width(), space.height()); + + start_node.with_optional_sub_node("affinity", [&] (Xml_node node) { + + Location const location = Location::from_xml(space, node); + + if (!location.within(space)) { + Service::Name const name = start_node.attribute_value("name", Service::Name()); + warning(name, ": affinity location exceeds affinity-space boundary"); + return; + } + + result = location; + }); + + return result; + } +} + +#endif /* _LIB__SANDBOX__UTILS_H_ */ diff --git a/repos/os/include/sandbox/verbose.h b/repos/os/include/sandbox/verbose.h new file mode 100644 index 0000000000..65ca297ccf --- /dev/null +++ b/repos/os/include/sandbox/verbose.h @@ -0,0 +1,43 @@ +/* + * \brief Sandbox verbosity + * \author Norman Feske + * \date 2017-01-03 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _LIB__SANDBOX__VERBOSE_H_ +#define _LIB__SANDBOX__VERBOSE_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include + +namespace Sandbox { struct Verbose; } + + +class Sandbox::Verbose : Genode::Noncopyable +{ + private: + + bool _enabled = false; + + public: + + Verbose() { } + + Verbose(Xml_node const &config) + : _enabled(config.attribute_value("verbose", false)) { } + + bool enabled() const { return _enabled; } +}; + +#endif /* _LIB__SANDBOX__VERBOSE_H_ */ diff --git a/repos/os/lib/mk/sandbox.mk b/repos/os/lib/mk/sandbox.mk index 9e30fd1828..0ebc64faed 100644 --- a/repos/os/lib/mk/sandbox.mk +++ b/repos/os/lib/mk/sandbox.mk @@ -1,5 +1,5 @@ SRC_CC = library.cc child.cc server.cc config_model.cc -INC_DIR += $(REP_DIR)/src/lib/sandbox +INC_DIR += $(REP_DIR)/include/sandbox LIBS += base SHARED_LIB = yes diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc index 4f13681880..54d1b84a70 100644 --- a/repos/os/src/init/main.cc +++ b/repos/os/src/init/main.cc @@ -25,11 +25,11 @@ namespace Init { } -struct Init::Main : Sandbox::State_handler +struct Init::Main : Genode::Sandbox::State_handler { Env &_env; - Sandbox _sandbox { _env, *this }; + Genode::Sandbox _sandbox { _env, *this }; Attached_rom_dataspace _config { _env, "config" }; diff --git a/repos/os/src/lib/sandbox/child.cc b/repos/os/src/lib/sandbox/child.cc index b6d5cfb62c..cc13eac8ed 100644 --- a/repos/os/src/lib/sandbox/child.cc +++ b/repos/os/src/lib/sandbox/child.cc @@ -15,7 +15,7 @@ #include /* local includes */ -#include +#include void Sandbox::Child::destroy_services() diff --git a/repos/os/src/lib/sandbox/config_model.cc b/repos/os/src/lib/sandbox/config_model.cc index 0dbb118276..f27ea27f19 100644 --- a/repos/os/src/lib/sandbox/config_model.cc +++ b/repos/os/src/lib/sandbox/config_model.cc @@ -11,7 +11,7 @@ * under the terms of the GNU Affero General Public License version 3. */ -#include +#include using namespace Sandbox; diff --git a/repos/os/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc index ddfef7ae9e..84f632dce1 100644 --- a/repos/os/src/lib/sandbox/library.cc +++ b/repos/os/src/lib/sandbox/library.cc @@ -16,254 +16,11 @@ #include /* local includes */ -#include -#include -#include -#include -#include - -struct Genode::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 -{ - 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 Child_registry = ::Sandbox::Child_registry; - 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; - - Env &_env; - Heap &_heap; - - 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 { }; - - Affinity::Space _effective_affinity_space() const - { - return _affinity_space.constructed() ? *_affinity_space - : Affinity::Space { 1, 1 }; - } - - State_reporter _state_reporter; - - Heartbeat _heartbeat { _env, _children, _state_reporter }; - - /* - * Internal representation of the XML configuration - */ - 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; - - unsigned _child_cnt = 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) { - error("RAM preservation exceeds available memory"); - return Ram_quota { 0 }; - } - - /* deduce preserved quota from available quota */ - return Ram_quota { avail_ram.value - _preservation.ram.value }; - } - - Cap_quota _avail_caps() const - { - Cap_quota avail_caps { _env.pd().avail_caps().value }; - - if (_preservation.caps.value > avail_caps.value) { - error("Capability preservation exceeds available capabilities"); - return Cap_quota { 0 }; - } - - /* deduce preserved quota from available quota */ - return Cap_quota { avail_caps.value - _preservation.caps.value }; - } - - /** - * Child::Ram_limit_accessor interface - */ - Ram_quota resource_limit(Ram_quota const &) const override - { - return _avail_ram(); - } - - /** - * Child::Cap_limit_accessor interface - */ - Cap_quota resource_limit(Cap_quota const &) const override { return _avail_caps(); } - - /** - * Child::Cpu_limit_accessor interface - */ - Cpu_quota resource_limit(Cpu_quota const &) const override { return _avail_cpu; } - - /** - * Child::Cpu_quota_transfer interface - */ - void transfer_cpu_quota(Cpu_session_capability cap, Cpu_quota quota) override - { - Cpu_quota const remaining { 100 - min(100u, _transferred_cpu.percent) }; - - /* prevent division by zero in 'quota_lim_upscale' */ - if (remaining.percent == 0) - return; - - size_t const fraction = - Cpu_session::quota_lim_upscale(quota.percent, remaining.percent); - - _env.cpu().transfer_quota(cap, fraction); - - _transferred_cpu.percent += quota.percent; - } - - /** - * State_reporter::Producer interface - */ - 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); }); - - if (detail.init_caps()) - xml.node("caps", [&] () { Cap_info::from_pd(_env.pd()).generate(xml); }); - - if (detail.children()) - _children.report_state(xml, detail); - } - - /** - * State_reporter::Producer interface - */ - Child::Sample_state_result sample_children_state() override - { - return _children.sample_state(); - } - - /** - * Default_route_accessor interface - */ - Xml_node default_route() override - { - return _default_route.constructed() ? _default_route->xml() - : Xml_node(""); - } - - /** - * Default_caps_accessor interface - */ - Cap_quota default_caps() override { return _default_caps; } - - void _update_aliases_from_config(Xml_node const &); - void _update_parent_services_from_config(Xml_node const &); - void _update_children_config(Xml_node const &); - void _destroy_abandoned_parent_services(); - void _destroy_abandoned_children(); - - Server _server { _env, _heap, _child_services, _state_reporter }; - - /** - * Sandbox::Start_model::Factory - */ - Child &create_child(Xml_node const &) override; - - /** - * Sandbox::Start_model::Factory - */ - void update_child(Child &, Xml_node const &) override; - - /** - * Sandbox::Start_model::Factory - */ - Alias &create_alias(Child_policy::Name const &name) override - { - Alias &alias = *new (_heap) Alias(name); - _children.insert_alias(&alias); - return alias; - } - - /** - * Sandbox::Start_model::Factory - */ - void destroy_alias(Alias &alias) override - { - _children.remove_alias(&alias); - destroy(_heap, &alias); - } - - /** - * Sandbox::Start_model::Factory - */ - bool ready_to_create_child(Start_model::Name const &, - Start_model::Version const &) const override; - - /** - * Sandbox::Parent_provides_model::Factory - */ - Parent_service &create_parent_service(Service::Name const &name) override - { - return *new (_heap) Parent_service(_parent_services, _env, name); - } - - Library(Env &env, Heap &heap, Registry &local_services, - State_handler &state_handler) - : - _env(env), _heap(heap), _local_services(local_services), - _state_reporter(_env, *this, state_handler) - { } - - void apply_config(Xml_node const &); - - void generate_state_report(Xml_generator &xml) const - { - _state_reporter.generate(xml); - } -}; +#include -void Genode::Sandbox::Library::_destroy_abandoned_parent_services() + +void Sandbox::Library::_destroy_abandoned_parent_services() { _parent_services.for_each([&] (Parent_service &service) { if (service.abandoned()) @@ -271,7 +28,7 @@ void Genode::Sandbox::Library::_destroy_abandoned_parent_services() } -void Genode::Sandbox::Library::_destroy_abandoned_children() +void Sandbox::Library::_destroy_abandoned_children() { _children.for_each_child([&] (Child &child) { @@ -300,7 +57,7 @@ void Genode::Sandbox::Library::_destroy_abandoned_children() } -bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const &name, +bool Sandbox::Library::ready_to_create_child(Start_model::Name const &name, Start_model::Version const &version) const { bool exists = false; @@ -328,7 +85,7 @@ bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const } -::Sandbox::Child &Genode::Sandbox::Library::create_child(Xml_node const &start_node) +::Sandbox::Child &Sandbox::Library::create_child(Xml_node const &start_node) { if (!_affinity_space.constructed() && start_node.has_sub_node("affinity")) warning("affinity-space configuration missing, " @@ -378,7 +135,7 @@ bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const } -void Genode::Sandbox::Library::update_child(Child &child, Xml_node const &start) +void Sandbox::Library::update_child(Child &child, Xml_node const &start) { if (child.abandoned()) return; @@ -395,7 +152,7 @@ void Genode::Sandbox::Library::update_child(Child &child, Xml_node const &start) } -void Genode::Sandbox::Library::apply_config(Xml_node const &config) +void Sandbox::Library::apply_config(Xml_node const &config) { _server_appeared_or_disappeared = false; _state_report_outdated = false; @@ -630,5 +387,5 @@ void Genode::Sandbox::generate_state_report(Xml_generator &xml) const Genode::Sandbox::Sandbox(Env &env, State_handler &state_handler) : _heap(env.ram(), env.rm()), - _library(*new (_heap) Library(env, _heap, _local_services, state_handler)) + _library(*new (_heap) ::Sandbox::Library(env, _heap, _local_services, state_handler)) { } diff --git a/repos/os/src/lib/sandbox/server.cc b/repos/os/src/lib/sandbox/server.cc index 3b43d487ee..d52dfe68ea 100644 --- a/repos/os/src/lib/sandbox/server.cc +++ b/repos/os/src/lib/sandbox/server.cc @@ -16,7 +16,7 @@ #include /* local includes */ -#include "server.h" +#include /******************************