From c72849ee9515eca02c24cb23a714e2bd359ba5f8 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:39:23 +0200 Subject: [PATCH 01/40] Provide children with information about what new resources have been allocated. --- repos/base/include/base/child.h | 11 +++++++++++ repos/base/include/parent/client.h | 2 ++ repos/base/include/parent/parent.h | 9 ++++++++- repos/base/src/lib/base/child.cc | 15 +++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/repos/base/include/base/child.h b/repos/base/include/base/child.h index 403be1d9e9..5512f83590 100644 --- a/repos/base/include/base/child.h +++ b/repos/base/include/base/child.h @@ -300,6 +300,8 @@ class Genode::Child : protected Rpc_object, /* arguments fetched by the child in response to a yield signal */ Mutex _yield_request_mutex { }; Resource_args _yield_request_args { }; + Mutex _resource_gain_mutex { }; + Resource_args _gained_resources { }; /* number of unanswered heartbeat signals */ unsigned _outstanding_heartbeats = 0; @@ -782,6 +784,14 @@ class Genode::Child : protected Rpc_object, */ void yield(Resource_args const &args); + /** + * Bestow resources on the child + * + * By calling this method, the child will be notified about + * the having gained the specified amount of resources. + */ + void accept(Resource_args const &args); + /** * Notify the child about newly available resources */ @@ -818,6 +828,7 @@ class Genode::Child : protected Rpc_object, void resource_request(Resource_args const &) override; void yield_sigh(Signal_context_capability) override; Resource_args yield_request() override; + Resource_args gained_resources() override; void yield_response() override; void heartbeat_sigh(Signal_context_capability) override; void heartbeat_response() override; diff --git a/repos/base/include/parent/client.h b/repos/base/include/parent/client.h index 5a6fc949c3..5cab7f396c 100644 --- a/repos/base/include/parent/client.h +++ b/repos/base/include/parent/client.h @@ -67,6 +67,8 @@ struct Genode::Parent_client : Rpc_client call(sigh); } Resource_args yield_request() override { return call(); } + + Resource_args gained_resources() override { return call(); } void yield_response() override { call(); } diff --git a/repos/base/include/parent/parent.h b/repos/base/include/parent/parent.h index 2bc99ce6c6..47e13513a8 100644 --- a/repos/base/include/parent/parent.h +++ b/repos/base/include/parent/parent.h @@ -281,6 +281,12 @@ class Genode::Parent */ virtual void yield_response() = 0; + /** + * Obtain information about the resources gained, e.g. from a resource request + * + */ + virtual Resource_args gained_resources() = 0; + /* * Health monitoring */ @@ -334,6 +340,7 @@ class Genode::Parent Resource_args const &); GENODE_RPC(Rpc_yield_sigh, void, yield_sigh, Signal_context_capability); GENODE_RPC(Rpc_yield_request, Resource_args, yield_request); + GENODE_RPC(Rpc_gained_resources, Resource_args, gained_resources); GENODE_RPC(Rpc_yield_response, void, yield_response); GENODE_RPC(Rpc_heartbeat_sigh, void, heartbeat_sigh, Signal_context_capability); GENODE_RPC(Rpc_heartbeat_response, void, heartbeat_response); @@ -343,7 +350,7 @@ class Genode::Parent Rpc_close, Rpc_session_response, Rpc_main_thread, Rpc_deliver_session_cap, Rpc_resource_avail_sigh, Rpc_resource_request, Rpc_yield_sigh, - Rpc_yield_request, Rpc_yield_response, + Rpc_yield_request, Rpc_yield_response, Rpc_gained_resources, Rpc_heartbeat_sigh, Rpc_heartbeat_response); }; diff --git a/repos/base/src/lib/base/child.cc b/repos/base/src/lib/base/child.cc index 45d96ee399..1accf6ce82 100644 --- a/repos/base/src/lib/base/child.cc +++ b/repos/base/src/lib/base/child.cc @@ -45,6 +45,15 @@ void Child::yield(Resource_args const &args) Signal_transmitter(_yield_sigh).submit(); } +void Child::accept(Resource_args const &args) +{ + Mutex::Guard guard{_resource_gain_mutex}; + + _gained_resources = args; + + if (_resource_avail_sigh.valid()) + Signal_transmitter(_resource_avail_sigh).submit(); +} void Child::notify_resource_avail() const { @@ -691,6 +700,12 @@ Parent::Resource_args Child::yield_request() return _yield_request_args; } +Parent::Resource_args Child::gained_resources() +{ + Mutex::Guard guard(_resource_gain_mutex); + + return _gained_resources; +} void Child::yield_response() { _policy.yield_response(); } From fb035977757c4c62ec911739888084af2dd35160 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:40:07 +0200 Subject: [PATCH 02/40] Updated symbol table to include new symbols for class Child. --- repos/base/lib/symbols/ld | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 719db66190..4f47dff9a4 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -261,6 +261,7 @@ _ZN6Genode5Child23initiate_env_pd_sessionEv T _ZN6Genode5Child4exitEi T _ZN6Genode5Child5closeENS_8Id_spaceINS_6Parent6ClientEE2IdE T _ZN6Genode5Child5yieldERKNS_6StringILm160EEE T +_ZN6Genode5Child6acceptERKNS_6StringILm160EEE T _ZN6Genode5Child7sessionENS_8Id_spaceINS_6Parent6ClientEE2IdERKNS_13Rpc_in_bufferILm64EEERKNS6_ILm160EEERKNS_8AffinityE T _ZN6Genode5Child7upgradeENS_8Id_spaceINS_6Parent6ClientEE2IdERKNS_13Rpc_in_bufferILm160EEE T _ZN6Genode5Child8announceERKNS_13Rpc_in_bufferILm64EEE T @@ -424,7 +425,7 @@ _ZTVN6Genode18Allocator_avl_baseE D 128 _ZTVN6Genode4HeapE D 72 _ZTVN6Genode4SlabE D 72 _ZTVN6Genode5Child14Initial_threadE D 48 -_ZTVN6Genode5ChildE D 440 +_ZTVN6Genode5ChildE D 456 _ZTVN6Genode6OutputE D 48 _ZTVN6Genode6ThreadE D 48 _ZTVN6Genode7ConsoleE D 48 From 04264bc67833cb00e6b097dc3b7ae849642ec046 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:40:38 +0200 Subject: [PATCH 03/40] Fixed bug in enumeration of performance counters. --- repos/base-nova/src/lib/base/perf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/base-nova/src/lib/base/perf.cc b/repos/base-nova/src/lib/base/perf.cc index 242a3228e7..fd51679e4d 100644 --- a/repos/base-nova/src/lib/base/perf.cc +++ b/repos/base-nova/src/lib/base/perf.cc @@ -46,7 +46,7 @@ void Genode::Trace::Performance_counter::start(unsigned counter) { Nova::uint8_t rc; Nova::mword_t type = (counter >> 4); - Nova::mword_t sel = type == Performance_counter::CORE ? counter : counter >>4; + Nova::mword_t sel = type == Performance_counter::CORE ? counter : counter & 0xf; if ((rc = Nova::hpc_start(sel, type)) != Nova::NOVA_OK) throw Genode::Trace::Pfc_access_error(rc); From 5c13d93050305eb1c6a47fe0cc7bdee89d8b0cbe Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:44:04 +0200 Subject: [PATCH 04/40] Refactored sandbox implementation to make its functionality available to alternative implementations of Init, e.g. Hoitaja. --- repos/os/include/sandbox/alias.h | 47 ++ repos/os/include/sandbox/child.h | 742 ++++++++++++++++++++++ repos/os/include/sandbox/child_registry.h | 117 ++++ repos/os/include/sandbox/config_model.h | 292 +++++++++ repos/os/include/sandbox/heartbeat.h | 89 +++ repos/os/include/sandbox/library.h | 263 ++++++++ repos/os/include/sandbox/name_registry.h | 39 ++ repos/os/include/sandbox/report.h | 91 +++ repos/os/include/sandbox/route_model.h | 280 ++++++++ repos/os/include/sandbox/sandbox.h | 10 +- repos/os/include/sandbox/server.h | 131 ++++ repos/os/include/sandbox/service.h | 151 +++++ repos/os/include/sandbox/state_reporter.h | 191 ++++++ repos/os/include/sandbox/types.h | 100 +++ repos/os/include/sandbox/utils.h | 233 +++++++ repos/os/include/sandbox/verbose.h | 43 ++ repos/os/lib/mk/sandbox.mk | 2 +- repos/os/src/init/main.cc | 4 +- repos/os/src/lib/sandbox/child.cc | 2 +- repos/os/src/lib/sandbox/config_model.cc | 2 +- repos/os/src/lib/sandbox/library.cc | 261 +------- repos/os/src/lib/sandbox/server.cc | 2 +- 22 files changed, 2829 insertions(+), 263 deletions(-) create mode 100644 repos/os/include/sandbox/alias.h create mode 100644 repos/os/include/sandbox/child.h create mode 100644 repos/os/include/sandbox/child_registry.h create mode 100644 repos/os/include/sandbox/config_model.h create mode 100644 repos/os/include/sandbox/heartbeat.h create mode 100644 repos/os/include/sandbox/library.h create mode 100644 repos/os/include/sandbox/name_registry.h create mode 100644 repos/os/include/sandbox/report.h create mode 100644 repos/os/include/sandbox/route_model.h create mode 100644 repos/os/include/sandbox/server.h create mode 100644 repos/os/include/sandbox/service.h create mode 100644 repos/os/include/sandbox/state_reporter.h create mode 100644 repos/os/include/sandbox/types.h create mode 100644 repos/os/include/sandbox/utils.h create mode 100644 repos/os/include/sandbox/verbose.h 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 /****************************** From c1b8001cb91533cd8401e9aa58dc2ab461fa4cf7 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:44:59 +0200 Subject: [PATCH 05/40] Small test app for new resource allocation interface. --- repos/os/src/test/resource_yield/main.cc | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/repos/os/src/test/resource_yield/main.cc b/repos/os/src/test/resource_yield/main.cc index ff36796ed9..f83a46fdcd 100644 --- a/repos/os/src/test/resource_yield/main.cc +++ b/repos/os/src/test/resource_yield/main.cc @@ -134,6 +134,16 @@ void Test::Child::_handle_yield() size_t const requested_ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + size_t const requested_cpu_quota = + Arg_string::find_arg(args.string(), "cpu_quota").ulong_value(0); + + log("released ", requested_cpu_quota, " portions of cpu_quota"); + + size_t const requested_gpu_quota = + Arg_string::find_arg(args.string(), "gpus").ulong_value(0); + + log("got request to release ", requested_gpu_quota, " gpus"); + /* free chunks of RAM to comply with the request */ size_t released_quota = 0; while (released_quota < requested_ram_quota) { @@ -208,6 +218,10 @@ class Test::Parent unsigned _wait_cnt = 0; + unsigned long _start = 0; + + unsigned long _end = 0; + enum State { WAIT, YIELD_REQUESTED, YIELD_GOT_RESPONSE }; State _state = WAIT; @@ -232,7 +246,9 @@ class Test::Parent log("request yield (ram prior yield: ", _used_ram_prior_yield); /* issue yield request */ - Genode::Parent::Resource_args yield_args("ram_quota=5M"); + Genode::Parent::Resource_args yield_args("ram_quota=5M,cpu_quota=10,gpus=1"); + + _start = _timer.elapsed_us(); _child.yield(yield_args); _state = YIELD_REQUESTED; @@ -251,7 +267,9 @@ class Test::Parent void _yield_response() { - log("got yield response"); + _end = _timer.elapsed_us(); + log("got yield response after ", (_end-_start), "us"); + _state = YIELD_GOT_RESPONSE; _print_status(); @@ -281,7 +299,7 @@ class Test::Parent Parent &_parent; Static_parent_services + Log_session, Timer::Session, Topo_session> _parent_services { _env }; Cap_quota const _cap_quota { 50 }; From 1f7bfe14269fd1b82c1f7cb7189d8d836d51b5e0 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:46:55 +0200 Subject: [PATCH 06/40] Benchmarks to evaluate cost of resource yield requests and resource allocation notifications. --- repos/mml/run/bestow_resources.run | 94 +++++++ repos/mml/run/resource_yield.run | 94 +++++++ repos/mml/src/app/grant_bench/main.cc | 351 ++++++++++++++++++++++++ repos/mml/src/app/grant_bench/target.mk | 3 + repos/mml/src/app/yield_bench/main.cc | 346 +++++++++++++++++++++++ repos/mml/src/app/yield_bench/target.mk | 3 + 6 files changed, 891 insertions(+) create mode 100644 repos/mml/run/bestow_resources.run create mode 100644 repos/mml/run/resource_yield.run create mode 100644 repos/mml/src/app/grant_bench/main.cc create mode 100644 repos/mml/src/app/grant_bench/target.mk create mode 100644 repos/mml/src/app/yield_bench/main.cc create mode 100644 repos/mml/src/app/yield_bench/target.mk diff --git a/repos/mml/run/bestow_resources.run b/repos/mml/run/bestow_resources.run new file mode 100644 index 0000000000..3d0e0e20a4 --- /dev/null +++ b/repos/mml/run/bestow_resources.run @@ -0,0 +1,94 @@ +set build_components { + core init hoitaja timer app/grant_bench +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + install_config $config + + set boot_modules { + core init hoitaja timer vfs.lib.so ld.lib.so benchmark_resource_award +} + +append_platform_drv_boot_modules + +build_boot_image $boot_modules +append qemu_args "-nographic " + +run_genode_until forever \ No newline at end of file diff --git a/repos/mml/run/resource_yield.run b/repos/mml/run/resource_yield.run new file mode 100644 index 0000000000..b959ef9ada --- /dev/null +++ b/repos/mml/run/resource_yield.run @@ -0,0 +1,94 @@ +set build_components { + core init hoitaja timer app/yield_bench +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + install_config $config + + set boot_modules { + core init hoitaja timer vfs.lib.so ld.lib.so benchmark_resource_yield +} + +append_platform_drv_boot_modules + +build_boot_image $boot_modules +append qemu_args "-nographic " + +run_genode_until forever \ No newline at end of file diff --git a/repos/mml/src/app/grant_bench/main.cc b/repos/mml/src/app/grant_bench/main.cc new file mode 100644 index 0000000000..0b67b6ff82 --- /dev/null +++ b/repos/mml/src/app/grant_bench/main.cc @@ -0,0 +1,351 @@ +/* + * \brief Test for yielding resources + * \author Norman Feske + * \date 2013-10-05 + * + * This test exercises the protocol between a parent and child, which is used + * by the parent to regain resources from a child subsystem. + * + * The program acts in either one of two roles, the parent or the child. The + * role is determined by reading a config argument. + * + * The child periodically allocates chunks of RAM until its RAM quota is + * depleted. Once it observes a yield request from the parent, however, it + * cooperatively releases as much resources as requested by the parent. + * + * The parent wait a while to give the child the chance to allocate RAM. It + * then sends a yield request and waits for a response. When getting the + * response, it validates whether the child complied to the request or not. + */ + +/* + * Copyright (C) 2013-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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Test { + class Child; + class Parent; + using namespace Genode; +} + + +/**************** + ** Child role ** + ****************/ + +/** + * The child eats more and more RAM. However, when receiving a yield request, + * it releases the requested amount of resources. + */ +class Test::Child +{ + private: + + struct Ram_chunk : List::Element + { + Env &env; + + size_t const size; + + Ram_dataspace_capability ds_cap; + + Ram_chunk(Env &env, size_t size) + : + env(env),size(size), ds_cap(env.ram().alloc(size)) + { } + + ~Ram_chunk() { env.ram().free(ds_cap); } + }; + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + bool const _expand; + List _ram_chunks { }; + Timer::Connection _timer { _env }; + Signal_handler _grant_handler; + Genode::uint64_t const _period_ms; + + void _handle_grant(); + + + + public: + + Child(Env &, Xml_node); + void main(); +}; + + +void Test::Child::_handle_grant() +{ + /* request yield request arguments */ + unsigned long start = Genode::Trace::timestamp(); + [[maybe_unused]] Genode::Parent::Resource_args const args = _env.parent().gained_resources(); + unsigned long end = Genode::Trace::timestamp(); + // Genode::Parent::Resource_args const args = _env.parent().yield_request(); + + _env.parent().yield_response(); + log("{\"grant-handle-et\": ", (end - start)/2000, "}"); + // size_t const gained_ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + // log("Gained RAM quota: ", gained_ram_quota); + /* + log("yield request: ", args.string()); + + size_t const requested_ram_quota = + Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + log("got request to free ", requested_ram_quota, " MB of RAM"); + + size_t const requested_cpu_quota = + Arg_string::find_arg(args.string(), "cpu_quota").ulong_value(0); + + log("released ", requested_cpu_quota, " portions of cpu_quota"); + + size_t const requested_gpu_quota = + Arg_string::find_arg(args.string(), "gpus").ulong_value(0); + + log("got request to release ", requested_gpu_quota, " gpus");*/ +} + + +Test::Child::Child(Env &env, Xml_node config) +: + _env(env), + _expand(config.attribute_value("expand", false)), + _grant_handler(_env.ep(), *this, &Child::_handle_grant), + _period_ms(config.attribute_value("period_ms", (Genode::uint64_t)500)) +{ + /* register yield signal handler */ + _env.parent().resource_avail_sigh(_grant_handler); +} + + +/***************** + ** Parent role ** + *****************/ + +/** + * The parent grants resource requests as long as it has free resources. + * Once in a while, it politely requests the child to yield resources. + */ +class Test::Parent +{ + private: + + Env &_env; + + Timer::Connection _timer { _env }; + + void _print_status() + { + log("quota: ", _child.pd().ram_quota().value / 1024, " KiB " + "used: ", _child.pd().used_ram().value / 1024, " KiB"); + } + + size_t _used_ram_prior_yield = 0; + + /* perform the test three times */ + unsigned _cnt = 5000; + + unsigned long _start = 0; + + unsigned long _end = 0; + unsigned long _sent = 0; + + enum State { WAIT, YIELD_REQUESTED, YIELD_GOT_RESPONSE }; + State _state = WAIT; + + void _schedule_one_second_timeout() + { + //log("wait ", _wait_cnt, "/", _wait_secs); + _timer.trigger_once(10000); + } + + void _init() + { + _state = WAIT; + _schedule_one_second_timeout(); + } + + void _bestow_resources() + { + /* remember quantum of resources used by the child */ + //_used_ram_prior_yield = _child.pd().used_ram().value; + + //log("request yield (ram prior yield: ", _used_ram_prior_yield); + + /* issue yield request */ + Genode::Parent::Resource_args award("ram_quota=5M,cpu_quota=10,gpus=1"); + + _start = Genode::Trace::timestamp(); + _child.accept(award); + _sent = Genode::Trace::timestamp(); + + _state = YIELD_REQUESTED; + } + + void _handle_timeout() + { + //_print_status(); + _bestow_resources(); + _schedule_one_second_timeout(); + } + + void _yield_response() + { + _end = Genode::Trace::timestamp(); + log("{\"bestow-rtt\": ", (_end-_start)/2000, ", \"bestow-transmit\": ", (_sent-_start)/2000, ",\"bestow-acked\":", (_end-_sent)/2000,"}"); + + _state = YIELD_GOT_RESPONSE; + + //_print_status(); + + if (_cnt-- > 0) { + _init(); + } else { + log("--- test-resource_yield finished ---"); + _env.parent().exit(0); + } + } + + Signal_handler _timeout_handler { + _env.ep(), *this, &Parent::_handle_timeout }; + + struct Policy : public Genode::Child_policy + { + Env &_env; + + Parent &_parent; + + Static_parent_services + _parent_services { _env }; + + Cap_quota const _cap_quota { 50 }; + Ram_quota const _ram_quota { 10*1024*1024 }; + Binary_name const _binary_name { "benchmark_resource_award" }; + + /* + * Config ROM service + */ + + struct Config_producer : Dynamic_rom_session::Content_producer + { + void produce_content(char *dst, Genode::size_t dst_len) override + { + Xml_generator xml(dst, dst_len, "config", [&] () { + xml.attribute("child", "yes"); }); + } + } _config_producer { }; + + Dynamic_rom_session _config_session { _env.ep().rpc_ep(), + ref_pd(), _env.rm(), + _config_producer }; + + typedef Genode::Local_service Config_service; + + Config_service::Single_session_factory _config_factory { _config_session }; + Config_service _config_service { _config_factory }; + + void yield_response() override + { + _parent._yield_response(); + } + + Policy(Parent &parent, Env &env) : _env(env), _parent(parent) { } + + Name name() const override { return "child"; } + + Binary_name binary_name() const override { return _binary_name; } + + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + + void init(Pd_session &pd, Pd_session_capability pd_cap) override + { + pd.ref_account(ref_pd_cap()); + ref_pd().transfer_quota(pd_cap, _cap_quota); + ref_pd().transfer_quota(pd_cap, _ram_quota); + } + + Route resolve_session_request(Service::Name const &service_name, + Session_label const &label, + Session::Diag const diag) override + { + auto route = [&] (Service &service) { + return Route { .service = service, + .label = label, + .diag = diag }; }; + + if (service_name == "ROM" && label == "child -> config") + return route(_config_service); + + Service *service_ptr = nullptr; + _parent_services.for_each([&] (Service &s) { + if (!service_ptr && service_name == s.name()) + service_ptr = &s; }); + + if (!service_ptr) + throw Service_denied(); + + return route(*service_ptr); + } + }; + + Policy _policy { *this, _env }; + + Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), _policy }; + + public: + + class Insufficient_yield { }; + + /** + * Constructor + */ + Parent(Env &env) : _env(env) + { + _timer.sigh(_timeout_handler); + _init(); + } +}; + + +/*************** + ** Component ** + ***************/ + +void Component::construct(Genode::Env &env) +{ + using namespace Genode; + + /* + * Read value '' attribute to decide whether to perform + * the child or the parent role. + */ + static Attached_rom_dataspace config(env, "config"); + bool const is_child = config.xml().attribute_value("child", false); + + if (is_child) { + log("--- test-resource_yield child role started ---"); + static Test::Child child(env, config.xml()); + } else { + log("--- test-resource_yield parent role started ---"); + static Test::Parent parent(env); + } +} diff --git a/repos/mml/src/app/grant_bench/target.mk b/repos/mml/src/app/grant_bench/target.mk new file mode 100644 index 0000000000..5c5b4a3e48 --- /dev/null +++ b/repos/mml/src/app/grant_bench/target.mk @@ -0,0 +1,3 @@ +TARGET = benchmark_resource_award +SRC_CC = main.cc +LIBS = base diff --git a/repos/mml/src/app/yield_bench/main.cc b/repos/mml/src/app/yield_bench/main.cc new file mode 100644 index 0000000000..0d3ce13c99 --- /dev/null +++ b/repos/mml/src/app/yield_bench/main.cc @@ -0,0 +1,346 @@ +/* + * \brief Test for yielding resources + * \author Norman Feske + * \date 2013-10-05 + * + * This test exercises the protocol between a parent and child, which is used + * by the parent to regain resources from a child subsystem. + * + * The program acts in either one of two roles, the parent or the child. The + * role is determined by reading a config argument. + * + * The child periodically allocates chunks of RAM until its RAM quota is + * depleted. Once it observes a yield request from the parent, however, it + * cooperatively releases as much resources as requested by the parent. + * + * The parent wait a while to give the child the chance to allocate RAM. It + * then sends a yield request and waits for a response. When getting the + * response, it validates whether the child complied to the request or not. + */ + +/* + * Copyright (C) 2013-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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Test { + class Child; + class Parent; + using namespace Genode; +} + + +/**************** + ** Child role ** + ****************/ + +/** + * The child eats more and more RAM. However, when receiving a yield request, + * it releases the requested amount of resources. + */ +class Test::Child +{ + private: + + struct Ram_chunk : List::Element + { + Env &env; + + size_t const size; + + Ram_dataspace_capability ds_cap; + + Ram_chunk(Env &env, size_t size) + : + env(env),size(size), ds_cap(env.ram().alloc(size)) + { } + + ~Ram_chunk() { env.ram().free(ds_cap); } + }; + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + bool const _expand; + List _ram_chunks { }; + Timer::Connection _timer { _env }; + Signal_handler _yield_handler; + uint64_t const _period_ms; + + void _handle_yield(); + + + + public: + + Child(Env &, Xml_node); + void main(); +}; + + +void Test::Child::_handle_yield() +{ + /* request yield request arguments */ + //Genode::Parent::Resource_args const args = _env.parent().yield_request(); + _env.parent().yield_response(); +/* + log("yield request: ", args.string()); + + size_t const requested_ram_quota = + Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + log("got request to free ", requested_ram_quota, " MB of RAM"); + + size_t const requested_cpu_quota = + Arg_string::find_arg(args.string(), "cpu_quota").ulong_value(0); + + log("released ", requested_cpu_quota, " portions of cpu_quota"); + + size_t const requested_gpu_quota = + Arg_string::find_arg(args.string(), "gpus").ulong_value(0); + + log("got request to release ", requested_gpu_quota, " gpus");*/ + + + +} + + +Test::Child::Child(Env &env, Xml_node config) +: + _env(env), + _expand(config.attribute_value("expand", false)), + _yield_handler(_env.ep(), *this, &Child::_handle_yield), + _period_ms(config.attribute_value("period_ms", (uint64_t)500)) +{ + /* register yield signal handler */ + _env.parent().yield_sigh(_yield_handler); +} + + +/***************** + ** Parent role ** + *****************/ + +/** + * The parent grants resource requests as long as it has free resources. + * Once in a while, it politely requests the child to yield resources. + */ +class Test::Parent +{ + private: + + Env &_env; + + Timer::Connection _timer { _env }; + + void _print_status() + { + log("quota: ", _child.pd().ram_quota().value / 1024, " KiB " + "used: ", _child.pd().used_ram().value / 1024, " KiB"); + } + + size_t _used_ram_prior_yield = 0; + + /* perform the test three times */ + unsigned _cnt = 5000; + + unsigned long _start = 0; + + unsigned long _end = 0; + unsigned long _sent = 0; + + enum State { WAIT, YIELD_REQUESTED, YIELD_GOT_RESPONSE }; + State _state = WAIT; + + void _schedule_one_second_timeout() + { + //log("wait ", _wait_cnt, "/", _wait_secs); + _timer.trigger_once(10000); + } + + void _init() + { + _state = WAIT; + _schedule_one_second_timeout(); + } + + void _request_yield() + { + /* remember quantum of resources used by the child */ + _used_ram_prior_yield = _child.pd().used_ram().value; + + //log("request yield (ram prior yield: ", _used_ram_prior_yield); + + /* issue yield request */ + Genode::Parent::Resource_args yield_args("ram_quota=5M,cpu_quota=10,gpus=1"); + + _start = Genode::Trace::timestamp(); + _child.yield(yield_args); + _sent = Genode::Trace::timestamp(); + + _state = YIELD_REQUESTED; + } + + void _handle_timeout() + { + //_print_status(); + _request_yield(); + _schedule_one_second_timeout(); + } + + void _yield_response() + { + _end = Genode::Trace::timestamp(); + log("{\"yield-rtt\": ", (_end-_start)/2000, ", \"yield-request\": ", (_sent-_start)/2000, ",\"yield-response\":", (_end-_sent)/2000,"}"); + + _state = YIELD_GOT_RESPONSE; + + //_print_status(); + + if (_cnt-- > 0) { + _init(); + } else { + log("--- test-resource_yield finished ---"); + _env.parent().exit(0); + } + } + + Signal_handler _timeout_handler { + _env.ep(), *this, &Parent::_handle_timeout }; + + struct Policy : public Genode::Child_policy + { + Env &_env; + + Parent &_parent; + + Static_parent_services + _parent_services { _env }; + + Cap_quota const _cap_quota { 50 }; + Ram_quota const _ram_quota { 10*1024*1024 }; + Binary_name const _binary_name { "benchmark_resource_yield" }; + + /* + * Config ROM service + */ + + struct Config_producer : Dynamic_rom_session::Content_producer + { + void produce_content(char *dst, Genode::size_t dst_len) override + { + Xml_generator xml(dst, dst_len, "config", [&] () { + xml.attribute("child", "yes"); }); + } + } _config_producer { }; + + Dynamic_rom_session _config_session { _env.ep().rpc_ep(), + ref_pd(), _env.rm(), + _config_producer }; + + typedef Genode::Local_service Config_service; + + Config_service::Single_session_factory _config_factory { _config_session }; + Config_service _config_service { _config_factory }; + + void yield_response() override + { + _parent._yield_response(); + } + + Policy(Parent &parent, Env &env) : _env(env), _parent(parent) { } + + Name name() const override { return "child"; } + + Binary_name binary_name() const override { return _binary_name; } + + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + + void init(Pd_session &pd, Pd_session_capability pd_cap) override + { + pd.ref_account(ref_pd_cap()); + ref_pd().transfer_quota(pd_cap, _cap_quota); + ref_pd().transfer_quota(pd_cap, _ram_quota); + } + + Route resolve_session_request(Service::Name const &service_name, + Session_label const &label, + Session::Diag const diag) override + { + auto route = [&] (Service &service) { + return Route { .service = service, + .label = label, + .diag = diag }; }; + + if (service_name == "ROM" && label == "child -> config") + return route(_config_service); + + Service *service_ptr = nullptr; + _parent_services.for_each([&] (Service &s) { + if (!service_ptr && service_name == s.name()) + service_ptr = &s; }); + + if (!service_ptr) + throw Service_denied(); + + return route(*service_ptr); + } + }; + + Policy _policy { *this, _env }; + + Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), _policy }; + + public: + + class Insufficient_yield { }; + + /** + * Constructor + */ + Parent(Env &env) : _env(env) + { + _timer.sigh(_timeout_handler); + _init(); + } +}; + + +/*************** + ** Component ** + ***************/ + +void Component::construct(Genode::Env &env) +{ + using namespace Genode; + + /* + * Read value '' attribute to decide whether to perform + * the child or the parent role. + */ + static Attached_rom_dataspace config(env, "config"); + bool const is_child = config.xml().attribute_value("child", false); + + if (is_child) { + log("--- test-resource_yield child role started ---"); + static Test::Child child(env, config.xml()); + } else { + log("--- test-resource_yield parent role started ---"); + static Test::Parent parent(env); + } +} diff --git a/repos/mml/src/app/yield_bench/target.mk b/repos/mml/src/app/yield_bench/target.mk new file mode 100644 index 0000000000..74d1d88926 --- /dev/null +++ b/repos/mml/src/app/yield_bench/target.mk @@ -0,0 +1,3 @@ +TARGET = benchmark_resource_yield +SRC_CC = main.cc +LIBS = base From aa183af6a45da63894267c27597a3586cf6d9bd7 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:47:58 +0200 Subject: [PATCH 07/40] Added run script to produce dry-run output for VSCode. --- repos/mml/run/vscode.run | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 repos/mml/run/vscode.run diff --git a/repos/mml/run/vscode.run b/repos/mml/run/vscode.run new file mode 100644 index 0000000000..afe6b6293d --- /dev/null +++ b/repos/mml/run/vscode.run @@ -0,0 +1,12 @@ +set build_components { + core init timer hoitaja app/blinktree app/hello_mxtask app/hpc_test app/yield_bench test/resource_yield app/grant_bench app/top server/cpu_balancer app/cpu_burner +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components +build $build_components + +create_boot_directory + + + From a72727fd45e5fd9bf225731dc66f4e062a78735f Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:51:25 +0200 Subject: [PATCH 08/40] Defined method prototypes for the internal interfaces of Hoitaja. --- repos/os/src/hoitaja/cell_controller.h | 46 ++++ repos/os/src/hoitaja/config.xsd | 214 ++++++++++++++++++ repos/os/src/hoitaja/core_allocator.h | 40 ++++ repos/os/src/hoitaja/habitat.h | 46 ++++ repos/os/src/hoitaja/hyperthread_controller.h | 0 repos/os/src/hoitaja/load_controller.h | 27 +++ repos/os/src/hoitaja/main.cc | 144 ++++++++++++ repos/os/src/hoitaja/memory_controller.h | 0 repos/os/src/hoitaja/numa_controller.h | 0 9 files changed, 517 insertions(+) create mode 100644 repos/os/src/hoitaja/cell_controller.h create mode 100644 repos/os/src/hoitaja/config.xsd create mode 100644 repos/os/src/hoitaja/core_allocator.h create mode 100644 repos/os/src/hoitaja/habitat.h create mode 100644 repos/os/src/hoitaja/hyperthread_controller.h create mode 100644 repos/os/src/hoitaja/load_controller.h create mode 100644 repos/os/src/hoitaja/main.cc create mode 100644 repos/os/src/hoitaja/memory_controller.h create mode 100644 repos/os/src/hoitaja/numa_controller.h diff --git a/repos/os/src/hoitaja/cell_controller.h b/repos/os/src/hoitaja/cell_controller.h new file mode 100644 index 0000000000..94127a2d7d --- /dev/null +++ b/repos/os/src/hoitaja/cell_controller.h @@ -0,0 +1,46 @@ +/* + * \brief Hoitaja — Cell Controller + * \author Michael Müller, Norman Feske (Init) + * \date 2023-04-20 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * Copyright (C) 2023 Michael Müller, Osnabrück University + * + * This file is part of EalánOS, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include + +namespace Hoitaja +{ + class Cell_controller; +} + +class Hoitaja::Cell_controller +{ + public: + void create_cell(); + void destroy_cell(); + + /** + * @brief Determine which cells shall be shrinked down + * + * @return Sandbox::Child* List of cells to shrink + */ + Sandbox::Child *cells_to_shrink(); + /** + * @brief Determine which cell shall be grown up + * + * @return Sandbox::Child* List of cells to grow + */ + Sandbox::Child *cells_to_grow(); + + /** + * @brief Regather performance metrics for next adaptation cycle + * + */ + void update_metrics(); +}; \ No newline at end of file diff --git a/repos/os/src/hoitaja/config.xsd b/repos/os/src/hoitaja/config.xsd new file mode 100644 index 0000000000..727b5b91e6 --- /dev/null +++ b/repos/os/src/hoitaja/config.xsd @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/os/src/hoitaja/core_allocator.h b/repos/os/src/hoitaja/core_allocator.h new file mode 100644 index 0000000000..6b8dcb58c3 --- /dev/null +++ b/repos/os/src/hoitaja/core_allocator.h @@ -0,0 +1,40 @@ +/* + * \brief Hoitaja — Core Allocator + * \author Michael Müller, Norman Feske (Init) + * \date 2023-04-20 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * Copyright (C) 2023 Michael Müller, Osnabrück University + * + * This file is part of EalánOS, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include + +/** Hoitaja includes **/ +#include "load_controller.h" +#include "cell_controller.h" + +namespace Hoitaja +{ + class Core_allocator; +} + +class Hoitaja::Core_allocator +{ + private: + Sandbox::Child *_cells_to_grow; + Sandbox::Child *_cells_to_shrink; + + public: + /** + * @brief Update core allocations for cells reported by Cell controller + * + */ + void update(); +}; \ No newline at end of file diff --git a/repos/os/src/hoitaja/habitat.h b/repos/os/src/hoitaja/habitat.h new file mode 100644 index 0000000000..09f1996bae --- /dev/null +++ b/repos/os/src/hoitaja/habitat.h @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Hoitaja { + class Habitat; + using namespace Genode; +} +class Hoitaja::Habitat : Sandbox::Library +{ + + private: + friend class Genode::Sandbox::Local_service_base; + + Heap _heap; + + Registry + _local_services{}; + + public: + void apply_config(Xml_node const &config) override { + log("Hoitaja is applying new config."); + Sandbox::Library::apply_config(config); + } + + void generate_state_report(Xml_generator &xml) const override { + log("Generating new state report for Hoitaja."); + Sandbox::Library::generate_state_report(xml); + } + + void maintain_cells(); + + Habitat(Env &env, Genode::Sandbox::State_handler &handler) + : Sandbox::Library(env, _heap, _local_services, handler), _heap(env.ram(), env.rm()) + { + } +}; \ No newline at end of file diff --git a/repos/os/src/hoitaja/hyperthread_controller.h b/repos/os/src/hoitaja/hyperthread_controller.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/repos/os/src/hoitaja/load_controller.h b/repos/os/src/hoitaja/load_controller.h new file mode 100644 index 0000000000..801f29a26f --- /dev/null +++ b/repos/os/src/hoitaja/load_controller.h @@ -0,0 +1,27 @@ +/* + * \brief Hoitaja — Load Controller + * \author Michael Müller, Norman Feske (Init) + * \date 2023-04-20 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * Copyright (C) 2023 Michael Müller, Osnabrück University + * + * This file is part of EalánOS, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#include +#include + +namespace Hoitaja { + class Load_controller; +} + +class Hoitaja::Load_controller +{ + public: + unsigned short *cpu_loads(); + Genode::Affinity::Location *idle_cores(); +}; \ No newline at end of file diff --git a/repos/os/src/hoitaja/main.cc b/repos/os/src/hoitaja/main.cc new file mode 100644 index 0000000000..26839672d6 --- /dev/null +++ b/repos/os/src/hoitaja/main.cc @@ -0,0 +1,144 @@ +/* + * \brief Hoitaja — Cell Management Component based on Init + * \author Michael Müller, Norman Feske (Init) + * \date 2023-04-20 + */ + +/* + * Copyright (C) 2010-2017 Genode Labs GmbH + * Copyright (C) 2023 Michael Müller, Osnabrück University + * + * This file is part of EalánOS, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/** Hoitaja components **/ +/* Filtering components */ +#include "load_controller.h" +#include "cell_controller.h" +#include "hyperthread_controller.h" +#include "memory_controller.h" +#include "numa_controller.h" +/* Core Allocator */ +#include "core_allocator.h" + +namespace Hoitaja { + + using namespace Genode; + + struct Main; +} + + +struct Hoitaja::Main : Genode::Sandbox::State_handler +{ + Env &_env; + + Habitat _sandbox { _env, *this }; + + Timer::Connection _timer{_env}; + + Attached_rom_dataspace _config { _env, "config" }; + + void _handle_resource_avail() { } + + Signal_handler
_resource_avail_handler { + _env.ep(), *this, &Main::_handle_resource_avail }; + + Constructible _reporter { }; + + size_t _report_buffer_size = 0; + + void _handle_config() + { + _config.update(); + + Xml_node const config = _config.xml(); + + bool reporter_enabled = false; + config.with_optional_sub_node("report", [&] (Xml_node report) { + + reporter_enabled = true; + + /* (re-)construct reporter whenever the buffer size is changed */ + Number_of_bytes const buffer_size = + report.attribute_value("buffer", Number_of_bytes(4096)); + + if (buffer_size != _report_buffer_size || !_reporter.constructed()) { + _report_buffer_size = buffer_size; + _reporter.construct(_env, "state", "state", _report_buffer_size); + } + }); + + if (_reporter.constructed()) + _reporter->enabled(reporter_enabled); + + _sandbox.apply_config(config); + } + + Signal_handler
_config_handler { + _env.ep(), *this, &Main::_handle_config }; + + void _handle_timeout() + { + //log("Hoitaja's entering its maintance cycle"); + // For now just print all cells created by Hoitaja + //_sandbox.maintain_cells(); + _timer.trigger_once(1000 * 1000); + } + + Signal_handler
_timeout_handler{ + _env.ep(), *this, &Main::_handle_timeout}; + + /** + * Sandbox::State_handler interface + */ + void handle_sandbox_state() override + { + + try { + Reporter::Xml_generator xml(*_reporter, [&] () { + _sandbox.generate_state_report(xml); }); + } + catch (Xml_generator::Buffer_exceeded) { + + error("state report exceeds maximum size"); + + /* try to reflect the error condition as state report */ + try { + Reporter::Xml_generator xml(*_reporter, [&] () { + xml.attribute("error", "report buffer exceeded"); }); + } + catch (...) { } + } + } + + Main(Env &env) : _env(env) + { + _config.sigh(_config_handler); + _timer.sigh(_timeout_handler); + + /* prevent init to block for resource upgrades (never satisfied by core) */ + _env.parent().resource_avail_sigh(_resource_avail_handler); + + _timer.trigger_once(1000 * 1000); + _handle_config(); + } +}; + +void Hoitaja::Habitat::maintain_cells() +{ + log("My current children are:"); + _children.for_each_child([&](Child &child) + { log(child.name(), " ram: ", child.ram_quota()); }); +} + +void Component::construct(Genode::Env &env) { static Hoitaja::Main main(env); } + diff --git a/repos/os/src/hoitaja/memory_controller.h b/repos/os/src/hoitaja/memory_controller.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/repos/os/src/hoitaja/numa_controller.h b/repos/os/src/hoitaja/numa_controller.h new file mode 100644 index 0000000000..e69de29bb2 From 2c5ebb131c3058b6247d756c8bd1a6644ee86157 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:52:04 +0200 Subject: [PATCH 09/40] Makefile for Hoitaja. --- repos/os/src/hoitaja/target.mk | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 repos/os/src/hoitaja/target.mk diff --git a/repos/os/src/hoitaja/target.mk b/repos/os/src/hoitaja/target.mk new file mode 100644 index 0000000000..d27ce27095 --- /dev/null +++ b/repos/os/src/hoitaja/target.mk @@ -0,0 +1,11 @@ +TARGET = hoitaja +SRC_CC = main.cc +LIBS = base +INC_DIR += $(PRG_DIR) + +CONFIG_XSD = config.xsd + +# statically link sandbox library to avoid dependency from sandbox.lib.so +SRC_CC += library.cc child.cc server.cc config_model.cc +INC_DIR += $(REP_DIR)/src/lib/sandbox +vpath %.cc $(REP_DIR)/src/lib/sandbox From 005b3dfdf40e019dc1c95973cb3bcab5e9d6b90e Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Thu, 3 Aug 2023 13:03:11 +0200 Subject: [PATCH 10/40] base_nova: Include hotfix for performance counters. --- repos/base-nova/ports/nova.hash | 2 +- repos/base-nova/ports/nova.port | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/repos/base-nova/ports/nova.hash b/repos/base-nova/ports/nova.hash index 79c825a918..6f9d8194a4 100644 --- a/repos/base-nova/ports/nova.hash +++ b/repos/base-nova/ports/nova.hash @@ -1 +1 @@ -52fcb4b19aa032eaba5484a69c3c4c491c2a6915 +cda426e631c475c21b8ff443bfa01f90f38d95a3 diff --git a/repos/base-nova/ports/nova.port b/repos/base-nova/ports/nova.port index 736bd5faf0..0e45bfd9f9 100644 --- a/repos/base-nova/ports/nova.port +++ b/repos/base-nova/ports/nova.port @@ -4,7 +4,7 @@ DOWNLOADS := nova.git # feature/numa branch URL(nova) := https://github.com/mmueller41/NOVA.git -REV(nova) := 4707840843206d63f72ba9238756355d16b52be3 +REV(nova) := 8d024288da12cb3a0366641e62c53876b7d827dd DIR(nova) := src/kernel/nova PATCHES := $(sort $(wildcard $(REP_DIR)/patches/*.patch)) From e002117098a5a3ac58d3113100ac8613dc412e36 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:41:51 +0200 Subject: [PATCH 11/40] Implemented Genode::print for Affinities to make printing affinities to console more convenient. --- repos/base/include/base/affinity.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/repos/base/include/base/affinity.h b/repos/base/include/base/affinity.h index a41bcc4a24..7421d1d074 100644 --- a/repos/base/include/base/affinity.h +++ b/repos/base/include/base/affinity.h @@ -93,6 +93,7 @@ class Genode::Affinity return Affinity::Space(node.attribute_value("width", 0U), node.attribute_value("height", 0U)); } + }; @@ -236,6 +237,30 @@ class Genode::Affinity } }; +namespace Genode { + static inline void print(Output &out, const Affinity::Space &space) + { + Genode::print(out, "("); + Genode::print(out, space.width()); + Genode::print(out, ","); + Genode::print(out, space.height()); + Genode::print(out, ")"); + } + + static inline void print(Output &out, const Affinity::Location &loc) + { + Genode::print(out, "("); + Genode::print(out, loc.xpos()); + Genode::print(out, ","); + Genode::print(out, loc.ypos()); + Genode::print(out, ","); + Genode::print(out, loc.width()); + Genode::print(out, "×"); + Genode::print(out, loc.height()); + Genode::print(out, ")"); + } +} + Genode::Affinity::Location Genode::Affinity::Space::location_of_index(int index) const { From 316f12855afd0acd596fdae6afabdb3c6ea8f92d Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:45:54 +0200 Subject: [PATCH 12/40] base: Added RPC to allow to change affinity of CPU and TOPO session after creation. This is necessary, as a cell might be pushed out of its original CPU region by Hoitaja. --- repos/base/include/cpu_session/client.h | 4 ++++ repos/base/include/cpu_session/connection.h | 2 +- repos/base/include/cpu_session/cpu_session.h | 9 ++++++++- repos/base/include/topo_session/client.h | 5 +++++ repos/base/include/topo_session/topo_session.h | 4 +++- repos/base/src/core/cpu_session_component.cc | 4 ++++ .../src/core/include/cpu_session_component.h | 1 + repos/base/src/core/include/topo_root.h | 6 +++++- .../src/core/include/topo_session_component.h | 13 ++++++++++--- repos/base/src/core/topo_session_component.cc | 18 ++++++++++++------ 10 files changed, 53 insertions(+), 13 deletions(-) diff --git a/repos/base/include/cpu_session/client.h b/repos/base/include/cpu_session/client.h index 60e65f801b..50b929c580 100644 --- a/repos/base/include/cpu_session/client.h +++ b/repos/base/include/cpu_session/client.h @@ -42,6 +42,10 @@ struct Genode::Cpu_session_client : Rpc_client Affinity::Space affinity_space() const override { return call(); } + void move(const Affinity::Location loc) override { + call(loc); + } + Dataspace_capability trace_control() override { return call(); } diff --git a/repos/base/include/cpu_session/connection.h b/repos/base/include/cpu_session/connection.h index ac0368abfd..865eb5c973 100644 --- a/repos/base/include/cpu_session/connection.h +++ b/repos/base/include/cpu_session/connection.h @@ -22,7 +22,7 @@ namespace Genode { struct Cpu_connection; } struct Genode::Cpu_connection : Connection, Cpu_session_client { - enum { RAM_QUOTA = 36*1024 }; + enum { RAM_QUOTA = 72*1024 }; /** * Constructor diff --git a/repos/base/include/cpu_session/cpu_session.h b/repos/base/include/cpu_session/cpu_session.h index 6297d5eadf..6a5b11c016 100644 --- a/repos/base/include/cpu_session/cpu_session.h +++ b/repos/base/include/cpu_session/cpu_session.h @@ -138,6 +138,12 @@ struct Genode::Cpu_session : Session */ virtual Affinity::Space affinity_space() const = 0; + /** + * @brief Update affinity location of this CPU session + * + */ + virtual void move(const Genode::Affinity::Location ) = 0; + /** * Translate generic priority value to kernel-specific priority levels * @@ -249,6 +255,7 @@ struct Genode::Cpu_session : Session GENODE_RPC(Rpc_migrate_thread, void, migrate_thread, Thread_capability, Affinity::Location); GENODE_RPC(Rpc_exception_sigh, void, exception_sigh, Signal_context_capability); GENODE_RPC(Rpc_affinity_space, Affinity::Space, affinity_space); + GENODE_RPC(Rpc_move, void, move, Affinity::Location); GENODE_RPC(Rpc_trace_control, Dataspace_capability, trace_control); GENODE_RPC(Rpc_ref_account, int, ref_account, Cpu_session_capability); GENODE_RPC(Rpc_transfer_quota, int, transfer_quota, Cpu_session_capability, size_t); @@ -257,7 +264,7 @@ struct Genode::Cpu_session : Session GENODE_RPC_INTERFACE(Rpc_create_thread, Rpc_kill_thread, Rpc_exception_sigh, Rpc_affinity_space, Rpc_trace_control, Rpc_ref_account, - Rpc_transfer_quota, Rpc_quota, Rpc_native_cpu, Rpc_migrate_thread); + Rpc_transfer_quota, Rpc_quota, Rpc_native_cpu, Rpc_migrate_thread, Rpc_move); }; diff --git a/repos/base/include/topo_session/client.h b/repos/base/include/topo_session/client.h index 35d00fcbb8..a092597310 100644 --- a/repos/base/include/topo_session/client.h +++ b/repos/base/include/topo_session/client.h @@ -41,4 +41,9 @@ struct Genode::Topo_session_client : Rpc_client unsigned node_count() override { return call(); } + + void reconstruct(const Affinity affinity) override + { + call(affinity); + } }; \ No newline at end of file diff --git a/repos/base/include/topo_session/topo_session.h b/repos/base/include/topo_session/topo_session.h index d6f90b9d06..8030df45c1 100644 --- a/repos/base/include/topo_session/topo_session.h +++ b/repos/base/include/topo_session/topo_session.h @@ -49,10 +49,12 @@ struct Genode::Topo_session : Session virtual Topology::Numa_region node_affinity_of(Affinity::Location const &) = 0; virtual Topology::Numa_region node_at_id(unsigned node_id) = 0; virtual unsigned node_count() = 0; + virtual void reconstruct(const Affinity) = 0; GENODE_RPC(Rpc_node_affinity, Topology::Numa_region, node_affinity_of, Affinity::Location const &); GENODE_RPC(Rpc_node_id, Topology::Numa_region, node_at_id, unsigned); GENODE_RPC(Rpc_node_count, unsigned, node_count); + GENODE_RPC(Rpc_reconstruct, void, reconstruct, Affinity); - GENODE_RPC_INTERFACE(Rpc_node_affinity, Rpc_node_id, Rpc_node_count); + GENODE_RPC_INTERFACE(Rpc_node_affinity, Rpc_node_id, Rpc_node_count, Rpc_reconstruct); }; \ No newline at end of file diff --git a/repos/base/src/core/cpu_session_component.cc b/repos/base/src/core/cpu_session_component.cc index 760cacbb87..49e7177aec 100644 --- a/repos/base/src/core/cpu_session_component.cc +++ b/repos/base/src/core/cpu_session_component.cc @@ -171,6 +171,10 @@ Affinity::Space Cpu_session_component::affinity_space() const return Affinity::Space(_location.width(), _location.height()); } +void Cpu_session_component::move(const Affinity::Location destination) +{ + _location = destination; +} Dataspace_capability Cpu_session_component::trace_control() { diff --git a/repos/base/src/core/include/cpu_session_component.h b/repos/base/src/core/include/cpu_session_component.h index 1b904ab3bf..44cdd1421c 100644 --- a/repos/base/src/core/include/cpu_session_component.h +++ b/repos/base/src/core/include/cpu_session_component.h @@ -172,6 +172,7 @@ class Genode::Cpu_session_component : public Session_object, void migrate_thread(Thread_capability, Affinity::Location) override; void exception_sigh(Signal_context_capability) override; Affinity::Space affinity_space() const override; + void move(const Affinity::Location) override; Dataspace_capability trace_control() override; int ref_account(Cpu_session_capability c) override; int transfer_quota(Cpu_session_capability, size_t) override; diff --git a/repos/base/src/core/include/topo_root.h b/repos/base/src/core/include/topo_root.h index 9c7c5978b7..52e369d8eb 100644 --- a/repos/base/src/core/include/topo_root.h +++ b/repos/base/src/core/include/topo_root.h @@ -19,6 +19,8 @@ #include +#include + namespace Genode { class Topo_root : public Root_component @@ -35,8 +37,10 @@ namespace Genode { if (ram_quota < Trace::Control_area::SIZE) throw Insufficient_ram_quota(); - if (!affinity.valid()) + if (!affinity.valid()) { + log("Location ", affinity.location(), " not within space ", affinity.space()); throw Service_denied(); + } return new (md_alloc()) Topo_session_component(*this->ep(), diff --git a/repos/base/src/core/include/topo_session_component.h b/repos/base/src/core/include/topo_session_component.h index e86ffd7665..20699f7193 100644 --- a/repos/base/src/core/include/topo_session_component.h +++ b/repos/base/src/core/include/topo_session_component.h @@ -30,7 +30,7 @@ namespace Genode { class Genode::Topo_session_component : public Session_object { private: - Genode::Affinity &_affinity; + Genode::Affinity _affinity; Sliced_heap _md_alloc; Topology::Numa_region _node_affinities[Genode::Platform::MAX_SUPPORTED_CPUS][Genode::Platform::MAX_SUPPORTED_CPUS]; @@ -44,9 +44,10 @@ class Genode::Topo_session_component : public Session_object Diag const &diag, Ram_allocator &ram_alloc, Region_map &local_rm, - Affinity &affinity + Affinity affinity ); + void construct(); /** * @brief Topology session interface @@ -65,5 +66,11 @@ class Genode::Topo_session_component : public Session_object unsigned node_count() override { return _node_count; - } + } + + void reconstruct(Affinity affinity) override + { + _affinity = affinity; + construct(); + } }; diff --git a/repos/base/src/core/topo_session_component.cc b/repos/base/src/core/topo_session_component.cc index f4caaf3fd4..42102eb3bd 100644 --- a/repos/base/src/core/topo_session_component.cc +++ b/repos/base/src/core/topo_session_component.cc @@ -25,19 +25,24 @@ Topo_session_component::Topo_session_component(Rpc_entrypoint &session_ep, Diag const &diag, Ram_allocator &ram_alloc, Region_map &local_rm, - Affinity &affinity) + Affinity affinity) : Session_object(session_ep, resources, label, diag), _affinity(affinity), _md_alloc(ram_alloc, local_rm), _node_count(0) { - Affinity::Location location = affinity.location(); + construct(); +} + +void Topo_session_component::construct() +{ + Affinity::Location location = _affinity.location(); const unsigned height = location.height(); unsigned width = location.width(); unsigned curr_node_id = 0; Topology::Numa_region *node_created = new (_md_alloc) Topology::Numa_region[64](); - Genode::log("[", label, "] Creating new topology model of size ", width, "x", height); + Genode::log("[", label(), "] Creating new topology model of size ", width, "x", height); for (unsigned x = 0; x < width; x++) { @@ -53,14 +58,14 @@ Topo_session_component::Topo_session_component(Rpc_entrypoint &session_ep, unsigned cpu_id = platform_specific().kernel_cpu_id(loc); unsigned native_id = platform_specific().domain_of_cpu(cpu_id); - log("[", label, "] CPU (", x, "x", y, ") is native CPU ", cpu_id, " on node ", native_id); + log("[", label(), "] CPU (", x, "x", y, ") is native CPU ", cpu_id, " on node ", native_id); if (node_created[native_id].core_count() == 0) { _nodes[curr_node_id] = _node_affinities[x][y] = Topology::Numa_region(curr_node_id, native_id); _node_affinities[x][y].increment_core_count(); node_created[native_id] = _node_affinities[x][y]; - log("[", label, "] Found new native NUMA region ", native_id, " for CPU (", x, "x", y, ")"); + log("[", label(), "] Found new native NUMA region ", native_id, " for CPU (", x, "x", y, ")"); _node_count++; curr_node_id++; } @@ -71,4 +76,5 @@ Topo_session_component::Topo_session_component(Rpc_entrypoint &session_ep, } } } -} + +} \ No newline at end of file From 44166feb15b8b6d9448c080b980242f9290d1424 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:46:42 +0200 Subject: [PATCH 13/40] base: Updated hash. --- repos/base/recipes/api/base/hash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/base/recipes/api/base/hash b/repos/base/recipes/api/base/hash index 65319582e1..dc6c0162bf 100644 --- a/repos/base/recipes/api/base/hash +++ b/repos/base/recipes/api/base/hash @@ -1 +1 @@ -2022-10-11 1574044ae0ee33a9ad3bdadb3c487c47d4f45bff +2023-07-07-a b51a6785a1bcdaf85c2627190b7320fb52517138 From df48b8b963a85dd42e409425ad567cbfd71b1c09 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:47:58 +0200 Subject: [PATCH 14/40] hoitaja: Example scenario for long-term scheduling of cells. --- repos/mml/run/hoitaja_example.run | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 repos/mml/run/hoitaja_example.run diff --git a/repos/mml/run/hoitaja_example.run b/repos/mml/run/hoitaja_example.run new file mode 100644 index 0000000000..1b28a90a51 --- /dev/null +++ b/repos/mml/run/hoitaja_example.run @@ -0,0 +1,114 @@ +set build_components { + core init hoitaja timer app/persistent_cell app/volatile_cell +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + install_config $config + + set boot_modules { + core init hoitaja timer vfs.lib.so ld.lib.so persistent_cell volatile_cell +} + +append_platform_drv_boot_modules + +build_boot_image $boot_modules +append qemu_args "-nographic " + +run_genode_until forever \ No newline at end of file From fa1e794c4dac4ed4ed550deee384b57cb58a3e91 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:48:38 +0200 Subject: [PATCH 15/40] Example scenario for PMC usage. --- repos/mml/run/pfm_test.run | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 repos/mml/run/pfm_test.run diff --git a/repos/mml/run/pfm_test.run b/repos/mml/run/pfm_test.run new file mode 100644 index 0000000000..b82a4adedb --- /dev/null +++ b/repos/mml/run/pfm_test.run @@ -0,0 +1,68 @@ +set build_components { + core init timer app/pfm_test +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +append config { + + + + 2022-07-20 14:30 + + + + + + + + +} + +install_config $config + +set boot_modules { + core init timer vfs.lib.so ld.lib.so posix.lib.so libc.lib.so libm.lib.so stdcxx.lib.so pfm_test +} + +append_platform_drv_boot_modules + +build_boot_image $boot_modules +append qemu_args "-nographic " + +run_genode_until forever \ No newline at end of file From 1245236ee21c78707ec30cb06e711cebe1a9bc93 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:49:48 +0200 Subject: [PATCH 16/40] Convenience scenario creating build.log for vscode's Intellisense. --- repos/mml/run/vscode.run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/mml/run/vscode.run b/repos/mml/run/vscode.run index afe6b6293d..eab351d598 100644 --- a/repos/mml/run/vscode.run +++ b/repos/mml/run/vscode.run @@ -1,5 +1,5 @@ set build_components { - core init timer hoitaja app/blinktree app/hello_mxtask app/hpc_test app/yield_bench test/resource_yield app/grant_bench app/top server/cpu_balancer app/cpu_burner + core init timer hoitaja app/blinktree app/hello_mxtask app/hpc_test app/yield_bench app/persistent_cell app/volatile_cell test/resource_yield app/grant_bench app/top app/cpu_burner } source ${genode_dir}/repos/base/run/platform_drv.inc From 6d53c9c87487f124fd964b1be5dfd6a4c36ef073 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:51:37 +0200 Subject: [PATCH 17/40] blinktree: Updated PMC definitions for AMD Epyc. --- repos/mml/src/app/blinktree/benchmark/perf.cpp | 3 ++- repos/mml/src/app/blinktree/benchmark/perf.h | 5 +++-- .../app/blinktree/blinktree_benchmark/benchmark.cpp | 10 ++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/repos/mml/src/app/blinktree/benchmark/perf.cpp b/repos/mml/src/app/blinktree/benchmark/perf.cpp index f18167d0da..9e6c8be876 100644 --- a/repos/mml/src/app/blinktree/benchmark/perf.cpp +++ b/repos/mml/src/app/blinktree/benchmark/perf.cpp @@ -14,7 +14,8 @@ using namespace benchmark; /** */ -[[maybe_unused]] PerfCounter Perf::L1_MISSES = {"l1-miss", Genode::Trace::Performance_counter::Type::CORE, 0x43, 0x5b}; +[[maybe_unused]] PerfCounter Perf::L1_DTLB_MISSES = {"l1-dtlb-miss", Genode::Trace::Performance_counter::Type::CORE, 0x45, 0xff}; +[[maybe_unused]] PerfCounter Perf::L1_ITLB_MISSES = {"l1-itlb-miss", Genode::Trace::Performance_counter::Type::CORE, 0x85, 0x0}; /** * Counter "LLC Misses" diff --git a/repos/mml/src/app/blinktree/benchmark/perf.h b/repos/mml/src/app/blinktree/benchmark/perf.h index 2a2ae39c00..4b61020b74 100644 --- a/repos/mml/src/app/blinktree/benchmark/perf.h +++ b/repos/mml/src/app/blinktree/benchmark/perf.h @@ -59,7 +59,7 @@ public: } catch (Genode::Trace::Pfc_access_error &e) { - std::cerr << "Failed to start counter: " << e.error_code() << std::endl; + std::cerr << "Failed to start counter " << _counter << " " << _name << ": " << static_cast(e.error_code()) << std::endl; } return _prev.value >= 0; } @@ -115,7 +115,8 @@ class Perf public: [[maybe_unused]] static PerfCounter INSTRUCTIONS; [[maybe_unused]] static PerfCounter CYCLES; - [[maybe_unused]] static PerfCounter L1_MISSES; + [[maybe_unused]] static PerfCounter L1_DTLB_MISSES; + [[maybe_unused]] static PerfCounter L1_ITLB_MISSES; [[maybe_unused]] [[maybe_unused]] static PerfCounter LLC_MISSES; [[maybe_unused]] static PerfCounter LLC_REFERENCES; //[[maybe_unused]] static PerfCounter STALLED_CYCLES_BACKEND; diff --git a/repos/mml/src/app/blinktree/blinktree_benchmark/benchmark.cpp b/repos/mml/src/app/blinktree/blinktree_benchmark/benchmark.cpp index 80c4ef7c7e..8b2921aa30 100644 --- a/repos/mml/src/app/blinktree/blinktree_benchmark/benchmark.cpp +++ b/repos/mml/src/app/blinktree/blinktree_benchmark/benchmark.cpp @@ -26,9 +26,15 @@ Benchmark::Benchmark(Libc::Env &env, benchmark::Cores &&cores, const std::uint16 { this->_chronometer.add(benchmark::Perf::CYCLES); this->_chronometer.add(benchmark::Perf::INSTRUCTIONS); + this->_chronometer.add(benchmark::Perf::L1_ITLB_MISSES); + this->_chronometer.add(benchmark::Perf::L1_DTLB_MISSES); + //this->_chronometer.add(benchmark::Perf::LLC_MISSES); + + //this->_chronometer.add(benchmark::Perf::STALLS_MEM_ANY); - this->_chronometer.add(benchmark::Perf::SW_PREFETCH_ACCESS_NTA); - this->_chronometer.add(benchmark::Perf::SW_PREFETCH_ACCESS_WRITE); + + //this->_chronometer.add(benchmark::Perf::SW_PREFETCH_ACCESS_NTA); + //this->_chronometer.add(benchmark::Perf::SW_PREFETCH_ACCESS_WRITE); } std::cout << "core configuration: \n" << this->_cores.dump(2) << std::endl; From e58e46880824f30eb5fd016b1429cd5650364573 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:52:04 +0200 Subject: [PATCH 18/40] blinktree: Use PMCs by default. --- repos/mml/src/app/blinktree/blinktree_benchmark/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/mml/src/app/blinktree/blinktree_benchmark/main.cpp b/repos/mml/src/app/blinktree/blinktree_benchmark/main.cpp index de111a6bb6..fb7d1d2d3d 100644 --- a/repos/mml/src/app/blinktree/blinktree_benchmark/main.cpp +++ b/repos/mml/src/app/blinktree/blinktree_benchmark/main.cpp @@ -205,7 +205,7 @@ void Libc::Component::construct(Libc::Env &env) { char cores_arg[10]; sprintf(cores_arg, "%d", cores); - char *args[] = {"blinktree_benchmark", "-i", "4", "-pd", "3", "-p", cores_arg}; + char *args[] = {"blinktree_benchmark", "-i", "6", "-pd", "3", "-p", cores_arg}; Libc::with_libc([&]() { From 4ee20705733e74cf7332a448cf344c737b1f02fa Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:53:44 +0200 Subject: [PATCH 19/40] Example code for persistent cell. --- .../app/persistent_cell/persistent_cell.cc | 36 +++++++++++++++++++ repos/mml/src/app/persistent_cell/target.mk | 3 ++ 2 files changed, 39 insertions(+) create mode 100644 repos/mml/src/app/persistent_cell/persistent_cell.cc create mode 100644 repos/mml/src/app/persistent_cell/target.mk diff --git a/repos/mml/src/app/persistent_cell/persistent_cell.cc b/repos/mml/src/app/persistent_cell/persistent_cell.cc new file mode 100644 index 0000000000..bc3db06588 --- /dev/null +++ b/repos/mml/src/app/persistent_cell/persistent_cell.cc @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +namespace Hoitaja_test { + struct Persistent_cell; +} + + +struct Hoitaja_test::Persistent_cell +{ + Genode::Env &_env; + Timer::Connection _timer{_env}; + + void _handle_timeout() + { + Genode::log("My affinity is ", _env.cpu().affinity_space()); + Genode::log("My PD cap is ", _env.pd_session_cap()); + _timer.trigger_once(5 * 1000 * 1000); + } + + Genode::Signal_handler _timeout_handler{ + _env.ep(), *this, &Persistent_cell::_handle_timeout}; + + Persistent_cell(Genode::Env &env) : _env(env) + { + Genode::log("My affinity is ", _env.cpu().affinity_space()); + Genode::log("My PD cap is ", _env.pd().address_space()); + _timer.sigh(_timeout_handler); + + _timer.trigger_once(5 * 1000 * 1000); + } +}; + +void Component::construct(Genode::Env &env) { static Hoitaja_test::Persistent_cell cell(env); } \ No newline at end of file diff --git a/repos/mml/src/app/persistent_cell/target.mk b/repos/mml/src/app/persistent_cell/target.mk new file mode 100644 index 0000000000..a8ef285365 --- /dev/null +++ b/repos/mml/src/app/persistent_cell/target.mk @@ -0,0 +1,3 @@ +TARGET = persistent_cell +SRC_CC = persistent_cell.cc +LIBS += base \ No newline at end of file From b711e0d0919e288cd2ad4dddc9d32a55e40afdea Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:54:24 +0200 Subject: [PATCH 20/40] Example for a cell that voluntarily terminates after a period of time. --- repos/mml/src/app/volatile_cell/target.mk | 3 ++ .../src/app/volatile_cell/volatile_cell.cc | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 repos/mml/src/app/volatile_cell/target.mk create mode 100644 repos/mml/src/app/volatile_cell/volatile_cell.cc diff --git a/repos/mml/src/app/volatile_cell/target.mk b/repos/mml/src/app/volatile_cell/target.mk new file mode 100644 index 0000000000..ec6dfc3aae --- /dev/null +++ b/repos/mml/src/app/volatile_cell/target.mk @@ -0,0 +1,3 @@ +TARGET = volatile_cell +SRC_CC = volatile_cell.cc +LIBS += base \ No newline at end of file diff --git a/repos/mml/src/app/volatile_cell/volatile_cell.cc b/repos/mml/src/app/volatile_cell/volatile_cell.cc new file mode 100644 index 0000000000..5f866d1cb4 --- /dev/null +++ b/repos/mml/src/app/volatile_cell/volatile_cell.cc @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +namespace Hoitaja_test { + class Volatile_cell; +} + + +class Hoitaja_test::Volatile_cell +{ + private: + Genode::Env &_env; + Timer::Connection _timer{_env}; + + void _handle_timeout() + { + Genode::log("My time has come. Exiting ..."); + _env.parent().exit(0); + } + + Genode::Signal_handler _timeout_handler{ + _env.ep(), *this, &Volatile_cell::_handle_timeout}; + + public: + Volatile_cell(Genode::Env &env) : _env(env) + { + Genode::log("My affinity space is ", _env.cpu().affinity_space()); + _timer.sigh(_timeout_handler); + _timer.trigger_once(30 * 1000 * 1000); + } +}; + +void Component::construct(Genode::Env &env) { static Hoitaja_test::Volatile_cell cell(env); } \ No newline at end of file From 550c376e401af2879c5bffc6242ace28cf811848 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:56:28 +0200 Subject: [PATCH 21/40] sandbox: Refactored sandbox library to make it extensible via inheritance. --- repos/os/include/sandbox/child.h | 87 +-- repos/os/include/sandbox/library.h | 3 +- repos/os/src/lib/sandbox/alias.h | 47 -- repos/os/src/lib/sandbox/child.cc | 10 +- repos/os/src/lib/sandbox/child.h | 742 ---------------------- repos/os/src/lib/sandbox/child_registry.h | 117 ---- repos/os/src/lib/sandbox/config_model.h | 292 --------- repos/os/src/lib/sandbox/heartbeat.h | 89 --- repos/os/src/lib/sandbox/library.cc | 2 +- repos/os/src/lib/sandbox/name_registry.h | 39 -- repos/os/src/lib/sandbox/report.h | 91 --- repos/os/src/lib/sandbox/route_model.h | 280 -------- repos/os/src/lib/sandbox/server.h | 131 ---- repos/os/src/lib/sandbox/service.h | 151 ----- repos/os/src/lib/sandbox/state_reporter.h | 191 ------ repos/os/src/lib/sandbox/types.h | 100 --- repos/os/src/lib/sandbox/utils.h | 233 ------- repos/os/src/lib/sandbox/verbose.h | 43 -- 18 files changed, 58 insertions(+), 2590 deletions(-) delete mode 100644 repos/os/src/lib/sandbox/alias.h delete mode 100644 repos/os/src/lib/sandbox/child.h delete mode 100644 repos/os/src/lib/sandbox/child_registry.h delete mode 100644 repos/os/src/lib/sandbox/config_model.h delete mode 100644 repos/os/src/lib/sandbox/heartbeat.h delete mode 100644 repos/os/src/lib/sandbox/name_registry.h delete mode 100644 repos/os/src/lib/sandbox/report.h delete mode 100644 repos/os/src/lib/sandbox/route_model.h delete mode 100644 repos/os/src/lib/sandbox/server.h delete mode 100644 repos/os/src/lib/sandbox/service.h delete mode 100644 repos/os/src/lib/sandbox/state_reporter.h delete mode 100644 repos/os/src/lib/sandbox/types.h delete mode 100644 repos/os/src/lib/sandbox/utils.h delete mode 100644 repos/os/src/lib/sandbox/verbose.h diff --git a/repos/os/include/sandbox/child.h b/repos/os/include/sandbox/child.h index 890e0344b8..ad3d972278 100644 --- a/repos/os/include/sandbox/child.h +++ b/repos/os/include/sandbox/child.h @@ -36,6 +36,44 @@ 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; + + 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}; + } + }; typedef String<80> Version; @@ -73,7 +111,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup enum class Sample_state_result { CHANGED, UNCHANGED }; - private: + protected: friend class Child_registry; @@ -109,7 +147,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup * The child is no longer referenced by config model and can * safely be destructed. */ - ABANDONED + ABANDONED, + }; State _state = State::INITIAL; @@ -202,48 +241,11 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup 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, + Affinity::Location const &location, Cap_quota default_cap_quota) { unsigned cpu_percent = 0; @@ -269,7 +271,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup return Resources { log2(prio_levels.value), priority_from_xml(start_node, prio_levels), Affinity(affinity_space, - affinity_location_from_xml(affinity_space, start_node)), + (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 } }; @@ -555,6 +558,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup 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); @@ -674,6 +678,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup Sample_state_result sample_state(); + /**************************** ** Child-policy interface ** ****************************/ diff --git a/repos/os/include/sandbox/library.h b/repos/os/include/sandbox/library.h index 34992ff068..3efdf7483c 100644 --- a/repos/os/include/sandbox/library.h +++ b/repos/os/include/sandbox/library.h @@ -13,6 +13,7 @@ #include #include +#pragma once namespace Sandbox { class Library; } @@ -200,7 +201,7 @@ public: 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(); + virtual void _destroy_abandoned_children(); Server _server { _env, _heap, _child_services, _state_reporter }; diff --git a/repos/os/src/lib/sandbox/alias.h b/repos/os/src/lib/sandbox/alias.h deleted file mode 100644 index 7f05a5052d..0000000000 --- a/repos/os/src/lib/sandbox/alias.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * \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/src/lib/sandbox/child.cc b/repos/os/src/lib/sandbox/child.cc index cc13eac8ed..281b85368b 100644 --- a/repos/os/src/lib/sandbox/child.cc +++ b/repos/os/src/lib/sandbox/child.cc @@ -679,6 +679,7 @@ Genode::Affinity Sandbox::Child::filter_session_affinity(Affinity const &session Affinity::Space const &child_space = _resources.affinity.space(); Affinity::Location const &child_location = _resources.affinity.location(); + Genode::log("[Hoitaja->", this->name(),"] Using cell's affinity ", child_location, " in ", child_space, " for filtering session affinity."); /* check if no valid affinity space was specified */ if (session_affinity.space().total() == 0) return Affinity(child_space, child_location); @@ -686,18 +687,24 @@ Genode::Affinity Sandbox::Child::filter_session_affinity(Affinity const &session Affinity::Space const &session_space = session_affinity.space(); Affinity::Location const &session_location = session_affinity.location(); + Genode::log("Scaling to session affinity ", session_location, " in ", session_space); + /* 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()); + Genode::log("Scaled session affinity to ", child_session, " in ", space); + /* 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())); + Genode::log("Session affinity subordinated to ", location, " in ", space); + return Affinity(space, location); } @@ -743,6 +750,7 @@ Sandbox::Child::Child(Env &env, 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) @@ -759,7 +767,7 @@ Sandbox::Child::Child(Env &env, _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, + _resources(_resources_from_start_node(start_node, prio_levels, affinity_space, location, default_caps_accessor.default_caps())), _parent_services(parent_services), _child_services(child_services), diff --git a/repos/os/src/lib/sandbox/child.h b/repos/os/src/lib/sandbox/child.h deleted file mode 100644 index b4a612624a..0000000000 --- a/repos/os/src/lib/sandbox/child.h +++ /dev/null @@ -1,742 +0,0 @@ -/* - * \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/src/lib/sandbox/child_registry.h b/repos/os/src/lib/sandbox/child_registry.h deleted file mode 100644 index b13e7527fa..0000000000 --- a/repos/os/src/lib/sandbox/child_registry.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * \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/src/lib/sandbox/config_model.h b/repos/os/src/lib/sandbox/config_model.h deleted file mode 100644 index 5a007970fe..0000000000 --- a/repos/os/src/lib/sandbox/config_model.h +++ /dev/null @@ -1,292 +0,0 @@ -/* - * \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/src/lib/sandbox/heartbeat.h b/repos/os/src/lib/sandbox/heartbeat.h deleted file mode 100644 index d62679a83f..0000000000 --- a/repos/os/src/lib/sandbox/heartbeat.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * \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/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc index 84f632dce1..91f2847d8c 100644 --- a/repos/os/src/lib/sandbox/library.cc +++ b/repos/os/src/lib/sandbox/library.cc @@ -97,7 +97,7 @@ bool Sandbox::Library::ready_to_create_child(Start_model::Name const &name, Child(_env, _heap, *_verbose, Child::Id { ++_child_cnt }, _state_reporter, start_node, *this, *this, _children, *this, *this, *this, *this, - _prio_levels, _effective_affinity_space(), + _prio_levels, _effective_affinity_space(), Affinity::Location(-1, -1, 0, 0), _parent_services, _child_services, _local_services); _children.insert(&child); diff --git a/repos/os/src/lib/sandbox/name_registry.h b/repos/os/src/lib/sandbox/name_registry.h deleted file mode 100644 index f92f9e0ead..0000000000 --- a/repos/os/src/lib/sandbox/name_registry.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * \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/src/lib/sandbox/report.h b/repos/os/src/lib/sandbox/report.h deleted file mode 100644 index 7531f2a7ee..0000000000 --- a/repos/os/src/lib/sandbox/report.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * \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/src/lib/sandbox/route_model.h b/repos/os/src/lib/sandbox/route_model.h deleted file mode 100644 index f2a01ab7f1..0000000000 --- a/repos/os/src/lib/sandbox/route_model.h +++ /dev/null @@ -1,280 +0,0 @@ -/* - * \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/src/lib/sandbox/server.h b/repos/os/src/lib/sandbox/server.h deleted file mode 100644 index ce231ce28f..0000000000 --- a/repos/os/src/lib/sandbox/server.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * \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/src/lib/sandbox/service.h b/repos/os/src/lib/sandbox/service.h deleted file mode 100644 index 9589de91b5..0000000000 --- a/repos/os/src/lib/sandbox/service.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * \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/src/lib/sandbox/state_reporter.h b/repos/os/src/lib/sandbox/state_reporter.h deleted file mode 100644 index dc91498e2a..0000000000 --- a/repos/os/src/lib/sandbox/state_reporter.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * \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 "report.h" -#include "child.h" - -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/src/lib/sandbox/types.h b/repos/os/src/lib/sandbox/types.h deleted file mode 100644 index 56fd8ddd0d..0000000000 --- a/repos/os/src/lib/sandbox/types.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * \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/src/lib/sandbox/utils.h b/repos/os/src/lib/sandbox/utils.h deleted file mode 100644 index cba9ca5d2c..0000000000 --- a/repos/os/src/lib/sandbox/utils.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * \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/src/lib/sandbox/verbose.h b/repos/os/src/lib/sandbox/verbose.h deleted file mode 100644 index ab3e6050a6..0000000000 --- a/repos/os/src/lib/sandbox/verbose.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * \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_ */ From 260079b5c4fdb5b90dc2920c93d84e32a250f4ed Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:57:34 +0200 Subject: [PATCH 22/40] Print directory when VERBOSE= is given on make cmdline. --- repos/base/mk/global.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/base/mk/global.mk b/repos/base/mk/global.mk index 75cfaff415..3cc0575317 100644 --- a/repos/base/mk/global.mk +++ b/repos/base/mk/global.mk @@ -255,7 +255,7 @@ ALL_INC_DIR += $(LIBGCC_INC_DIR) ALL_INC_DIR += $(HOST_INC_DIR) VERBOSE ?= @ -VERBOSE_DIR ?= --no-print-directory +VERBOSE_DIR ?= --print-directory MSG_LINK ?= @$(ECHO) " LINK " MSG_COMP ?= @$(ECHO) " COMPILE " From 0f3368cbe48ad56e48b7cbde83effeba1057f6f1 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:58:03 +0200 Subject: [PATCH 23/40] base: Updated symbols table. --- repos/base/lib/symbols/ld | 2 ++ 1 file changed, 2 insertions(+) diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 4f47dff9a4..802b676d2d 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -212,6 +212,8 @@ _ZN6Genode18Signal_transmitterC1ENS_10CapabilityINS_14Signal_contextEEE T _ZN6Genode18Signal_transmitterC2ENS_10CapabilityINS_14Signal_contextEEE T _ZN6Genode20env_session_id_spaceEv T _ZN6Genode21cache_invalidate_dataEmm T +_ZN6Genode22Topo_session_component11reconstructENS_8AffinityE W +_ZThn136_N6Genode22Topo_session_component11reconstructENS_8AffinityE W _ZN6Genode25env_stack_area_region_mapE B 8 _ZN6Genode27cache_clean_invalidate_dataEmm T _ZN6Genode28env_stack_area_ram_allocatorE B 8 From c39a9201e74f25dba579ee1e31bf13a285c074d3 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 17:59:31 +0200 Subject: [PATCH 24/40] Added priorities to yield testing scenario. --- repos/mml/run/resource_yield.run | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/repos/mml/run/resource_yield.run b/repos/mml/run/resource_yield.run index b959ef9ada..f210d87341 100644 --- a/repos/mml/run/resource_yield.run +++ b/repos/mml/run/resource_yield.run @@ -11,7 +11,7 @@ create_boot_directory set config { - + @@ -39,8 +39,8 @@ set config { - - + + @@ -71,6 +71,26 @@ set config { + + + + + + + + + + + + + + + + + + + + From 2dd211a1b3f952b7c5862764b9a992e0c7ce8cec Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:00:09 +0200 Subject: [PATCH 25/40] Updated hash for shim. --- repos/os/recipes/src/shim/hash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/os/recipes/src/shim/hash b/repos/os/recipes/src/shim/hash index 7f7348d333..8a8053d346 100644 --- a/repos/os/recipes/src/shim/hash +++ b/repos/os/recipes/src/shim/hash @@ -1 +1 @@ -2022-10-11 c19f6134a15baf12d7c14f77ee8204a19bd1d2af +2023-07-07-a 36058ca42f3cc198dd7e776037ac196469423e4a From 6f3449f56845f5d6c92a904cde6a193ed64a87dc Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:02:07 +0200 Subject: [PATCH 26/40] hoitaja: Fixed include error in cell_controller.h. --- repos/os/src/hoitaja/cell_controller.h | 1 + 1 file changed, 1 insertion(+) diff --git a/repos/os/src/hoitaja/cell_controller.h b/repos/os/src/hoitaja/cell_controller.h index 94127a2d7d..f4d0a92c9b 100644 --- a/repos/os/src/hoitaja/cell_controller.h +++ b/repos/os/src/hoitaja/cell_controller.h @@ -12,6 +12,7 @@ * under the terms of the GNU Affero General Public License version 3. */ +#pragma once #include namespace Hoitaja From d603ea90c0631152335a0e84f08f940bc2f35ed7 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:03:11 +0200 Subject: [PATCH 27/40] hoitaja: Added class representing Cells. --- repos/os/src/hoitaja/cell.h | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 repos/os/src/hoitaja/cell.h diff --git a/repos/os/src/hoitaja/cell.h b/repos/os/src/hoitaja/cell.h new file mode 100644 index 0000000000..1019207932 --- /dev/null +++ b/repos/os/src/hoitaja/cell.h @@ -0,0 +1,80 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Hoitaja { + class Cell; +} + +class Hoitaja::Cell : public ::Sandbox::Child +{ + private: + State_handler &_state_handler; + + public: + friend class Habitat; + + Cell(Genode::Env &env, + Genode::Allocator &alloc, + ::Sandbox::Verbose const &verbose, + ::Sandbox::Child::Id id, + ::Sandbox::Report_update_trigger &report_update_trigger, + Genode::Xml_node start_node, + ::Sandbox::Child::Default_route_accessor &default_route_accessor, + ::Sandbox::Child::Default_caps_accessor &default_caps_accessor, + ::Sandbox::Name_registry &name_registry, + ::Sandbox::Child::Ram_limit_accessor &ram_limit_accessor, + ::Sandbox::Child::Cap_limit_accessor &cap_limit_accessor, + ::Sandbox::Child::Cpu_limit_accessor &cpu_limit_accessor, + ::Sandbox::Child::Cpu_quota_transfer &cpu_quota_transfer, + ::Sandbox::Prio_levels prio_levels, + Genode::Affinity::Space const &affinity_space, + Genode::Affinity::Location const &location, + Genode::Registry<::Sandbox::Parent_service> &parent_services, + Genode::Registry<::Sandbox::Routed_service> &child_services, + Genode::Registry<::Sandbox::Child::Local_service> &local_services, + State_handler &state_handler) + : ::Sandbox::Child(env, alloc, verbose, id, report_update_trigger, start_node, default_route_accessor, default_caps_accessor, name_registry, ram_limit_accessor, cap_limit_accessor, cpu_limit_accessor, cpu_quota_transfer, prio_levels, affinity_space, location, parent_services, child_services, local_services), _state_handler(state_handler) + { } + + virtual ~Cell() { }; + + struct Resources &resources() { return _resources; } + + void update_affinity(Genode::Affinity affinity) { + Genode::log("Updating affinity to ", affinity.location(), " in space ", affinity.space()); + _resources.affinity = affinity; + Genode::log("Moving CPU session ", _env.cpu_session_cap()); + _env.cpu().move(affinity.location()); + if (_child.active()) { + _child.cpu().move(affinity.location()); + _child.topo().reconstruct(affinity); + } + } + + void exit(int exit_value) override + { + ::Sandbox::Child::exit(exit_value); + _state_handler.handle_habitat_state(*this); + } + + void yield(Genode::Parent::Resource_args &args) { + _child.yield(args); + } +}; \ No newline at end of file From 01cf6978756f58476c8a48377cd12fd4eff240f0 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:03:45 +0200 Subject: [PATCH 28/40] hoitaja: First version of static core allocator. --- repos/os/src/hoitaja/core_allocator.h | 75 +++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/repos/os/src/hoitaja/core_allocator.h b/repos/os/src/hoitaja/core_allocator.h index 6b8dcb58c3..6fb60d9306 100644 --- a/repos/os/src/hoitaja/core_allocator.h +++ b/repos/os/src/hoitaja/core_allocator.h @@ -12,13 +12,18 @@ * under the terms of the GNU Affero General Public License version 3. */ +#pragma once /* Genode includes */ #include #include +#include +#include +#include /** Hoitaja includes **/ #include "load_controller.h" #include "cell_controller.h" +#include namespace Hoitaja { @@ -28,13 +33,77 @@ namespace Hoitaja class Hoitaja::Core_allocator { private: - Sandbox::Child *_cells_to_grow; - Sandbox::Child *_cells_to_shrink; + Genode::Affinity::Space &_affinity_space; + + ::Sandbox::Prio_levels &_prio_levels; + + double _resource_coeff; // Coefficient used for calculating resource shares + + void _shrink(Cell &cell, unsigned int cores) + { + char yield_args_str[10]; + Genode::snprintf(yield_args_str, 7, "cores=%d", cores); + Genode::Parent::Resource_args yield_args(yield_args_str); + + cell.yield(yield_args); + } public: + inline unsigned int _calculate_resource_share(long priority) { + double ref_share = static_cast(_affinity_space.total()) / _resource_coeff; + return static_cast((1.0 / static_cast(priority)) * ref_share); + } + + Core_allocator(Genode::Affinity::Space &affinity_space, ::Sandbox::Prio_levels prio_levels) : _affinity_space(affinity_space), _prio_levels(prio_levels), _resource_coeff(0.0) + { + Genode::log("Created core allocator for ", affinity_space.total(), " cores and ", prio_levels.value, " priorities."); + } + + Genode::Affinity::Location allocate_cores_for_cell(Genode::Xml_node const &start_node) + { + // Calculate affinity from global affinity space and priority + long priority = ::Sandbox::priority_from_xml(start_node, _prio_levels); + priority = (priority == 0) ? 1 : priority; + _resource_coeff += (1.0/static_cast(priority)); // treat priority 0 same as 1, to avoid division by zero here + + unsigned int cores_share = _calculate_resource_share(priority); + + Genode::log("Child ", start_node.attribute_value("name", Genode::String<8>("unknown")), "'s share is ", cores_share, " of ", _affinity_space.total(), " cores, coeff=", _resource_coeff, " priority=", priority); + + return Genode::Affinity::Location( _affinity_space.total()-cores_share, 0, cores_share, 1 ); /* always use the core_share last cores, for now */ + } + + void free_cores_from_cell(Cell &cell) + { + /* Remove cell's coefficient from the global resource coefficient. + * This is necessary in order to be able to redistribute the freed resources correctly. We do not trigger the redistribution itself here, because the child has not been fully destroyed yet, thus its resources might still be occupied at this point. */ + _resource_coeff -= 1.0 / static_cast(cell.resources().priority); + } + /** * @brief Update core allocations for cells reported by Cell controller * */ - void update(); + void update(Hoitaja::Cell &cell, int *xpos) { + Cell::Resources resources = cell.resources(); + long priority = (resources.priority == 0)? 1 : resources.priority; + + unsigned int cores_share = _calculate_resource_share(priority); + unsigned int cores_to_reclaim = resources.affinity.location().width() * resources.affinity.location().height() - cores_share; + + cores_to_reclaim = (static_cast(cores_to_reclaim) < 0) ? 0 : cores_to_reclaim; + + Genode::Affinity::Location location(*xpos - cores_share, resources.affinity.location().ypos(), cores_share, resources.affinity.location().height()); + cell.update_affinity(Genode::Affinity(resources.affinity.space(), location)); + + *xpos = location.xpos(); + // TODO: Update affinity of existing sessions for cell + // TODO: Send yield request to cell + log("Need to reclaim ", cores_to_reclaim, " cores from ", cell.name()); + + if (cores_to_reclaim > 0) { + _shrink(cell, cores_to_reclaim); + } + } + }; \ No newline at end of file From fd216a561340cc7aebbf4e0ecd6dec7fd4d6670e Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:05:14 +0200 Subject: [PATCH 29/40] hoitaja: Implemented core (re-)allocation upon Cell construction/destruction. --- repos/os/src/hoitaja/habitat.cc | 119 ++++++++++++++++++++++++++++++++ repos/os/src/hoitaja/habitat.h | 35 ++++++++-- 2 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 repos/os/src/hoitaja/habitat.cc diff --git a/repos/os/src/hoitaja/habitat.cc b/repos/os/src/hoitaja/habitat.cc new file mode 100644 index 0000000000..d82e2576e0 --- /dev/null +++ b/repos/os/src/hoitaja/habitat.cc @@ -0,0 +1,119 @@ +#include "habitat.h" +#include + +::Sandbox::Child &Hoitaja::Habitat::create_child(Genode::Xml_node const &start_node) +{ + if (_affinity_space.constructed() && !_core_allocator.constructed()) + _core_allocator.construct(*_affinity_space, _prio_levels); + + Genode::Affinity::Location allocation = _core_allocator->allocate_cores_for_cell(start_node); + + + + if (allocation.width() < 1) { + Genode::error("failed to create child ", start_node.attribute_value("name", Child_policy::Name()), ": not enough CPU cores left."); + throw ::Sandbox::Start_model::Factory::Creation_failed(); + } + + // Allocate `cores_share` cores from the Core Allocator and set the childs affinity accordingly + // TODO: Implement core allocation + + try { + Hoitaja::Cell &child = *new (_heap) + Hoitaja::Cell(_env, _heap, *_verbose, + Child::Id { ++_child_cnt }, _state_reporter, + start_node, *this, *this, _children, *this, *this, *this, *this, + _prio_levels, _effective_affinity_space(), allocation, + _parent_services, _child_services, _local_services, _habitat_handler); + _children.insert(static_cast<::Sandbox::Child *>(&child)); + + maintain_cells(); + + _avail_cpu.percent -= min(_avail_cpu.percent, child.cpu_quota().percent); + + if (start_node.has_sub_node("provides")) + _server_appeared_or_disappeared = true; + + _state_report_outdated = true; + + return static_cast<::Sandbox::Child&>(child); + } + catch (Rom_connection::Rom_connection_failed) { + /* + * The binary does not exist. An error message is printed + * by the Rom_connection constructor. + */ + } + catch (Out_of_ram) { + warning("memory exhausted during child creation"); } + catch (Out_of_caps) { + warning("local capabilities exhausted during child creation"); } + catch (Child::Missing_name_attribute) { + warning("skipped startup of nameless child"); } + catch (Region_map::Region_conflict) { + warning("failed to attach dataspace to local address space " + "during child construction"); } + catch (Region_map::Invalid_dataspace) { + warning("attempt to attach invalid dataspace to local address space " + "during child construction"); } + catch (Service_denied) { + warning("failed to create session during child construction"); } + + throw ::Sandbox::Start_model::Factory::Creation_failed(); +} + +void Hoitaja::Habitat::_destroy_abandoned_children() +{ + _children.for_each_child([&] (Child &child) { + + if (!child.abandoned()) + return; + + /* make the child's services unavailable */ + child.destroy_services(); + child.close_all_sessions(); + _state_report_outdated = true; + + /* destroy child once all environment sessions are gone */ + if (child.env_sessions_closed()) { + _core_allocator->free_cores_from_cell(static_cast(child)); + _children.remove(&child); + + Cpu_quota const child_cpu_quota = child.cpu_quota(); + + destroy(_heap, &child); + + /* replenish available CPU quota */ + _avail_cpu.percent += child_cpu_quota.percent; + _transferred_cpu.percent -= min(_transferred_cpu.percent, + child_cpu_quota.percent); + } + }); + + /* We might have formerly occupied resources again now, so redistribute them */ + maintain_cells(); +} + +void Hoitaja::Habitat::maintain_cells() +{ + int xpos = _affinity_space->total(); + _children.for_each_child([&](Child &child) + { + log(child.name(), " ram: ", child.ram_quota()); + Cell &cell = static_cast(child); + _core_allocator->update(cell, &xpos); }); +} + +void Hoitaja::Habitat::update(Cell &cell) +{ + if (cell._exited) { + if (cell._exit_value != 0) + Genode::error(cell.name(), " exited with exit status ", cell._exit_value); + + _children.remove(static_cast(&cell)); + _core_allocator->free_cores_from_cell(cell); + + /* Update resource allocations, as there are new resources available */ + maintain_cells(); + } +} \ No newline at end of file diff --git a/repos/os/src/hoitaja/habitat.h b/repos/os/src/hoitaja/habitat.h index 09f1996bae..7a58b658fb 100644 --- a/repos/os/src/hoitaja/habitat.h +++ b/repos/os/src/hoitaja/habitat.h @@ -11,24 +11,36 @@ #include #include +#include + +/* Hoitaja includes */ +#include +#include +#include + +#pragma once namespace Hoitaja { class Habitat; using namespace Genode; } -class Hoitaja::Habitat : Sandbox::Library +struct Hoitaja::Habitat : public Sandbox::Library { - - private: + public: + friend class Genode::Sandbox::Local_service_base; + State_handler &_habitat_handler; + Heap _heap; + Genode::Constructible _core_allocator; + Registry _local_services{}; - public: void apply_config(Xml_node const &config) override { log("Hoitaja is applying new config."); + Sandbox::Library::apply_config(config); } @@ -39,8 +51,19 @@ class Hoitaja::Habitat : Sandbox::Library void maintain_cells(); - Habitat(Env &env, Genode::Sandbox::State_handler &handler) - : Sandbox::Library(env, _heap, _local_services, handler), _heap(env.ram(), env.rm()) + /** + * @brief Update cell's resource allocations + * + * @param cell whose resource allocations needs updating + */ + void update(Cell &cell); + + Habitat(Env &env, State_handler &habitat_handler, Genode::Sandbox::State_handler &handler) + : Sandbox::Library(env, _heap, _local_services, handler), _habitat_handler(habitat_handler), _heap(env.ram(), env.rm()), _core_allocator() { } + + Sandbox::Child &create_child(Xml_node const &) override; + + void _destroy_abandoned_children() override; }; \ No newline at end of file From 025a7ce66772fadbe222968db63e213da183f40c Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:06:26 +0200 Subject: [PATCH 30/40] hoitaja: Dummy classes for functions to be implemented later. --- repos/os/src/hoitaja/hyperthread_controller.h | 2 ++ repos/os/src/hoitaja/load_controller.h | 1 + repos/os/src/hoitaja/memory_controller.h | 2 ++ repos/os/src/hoitaja/numa_controller.h | 2 ++ 4 files changed, 7 insertions(+) diff --git a/repos/os/src/hoitaja/hyperthread_controller.h b/repos/os/src/hoitaja/hyperthread_controller.h index e69de29bb2..b6393fb363 100644 --- a/repos/os/src/hoitaja/hyperthread_controller.h +++ b/repos/os/src/hoitaja/hyperthread_controller.h @@ -0,0 +1,2 @@ + +#pragma once \ No newline at end of file diff --git a/repos/os/src/hoitaja/load_controller.h b/repos/os/src/hoitaja/load_controller.h index 801f29a26f..09cb7a62fa 100644 --- a/repos/os/src/hoitaja/load_controller.h +++ b/repos/os/src/hoitaja/load_controller.h @@ -11,6 +11,7 @@ * This file is part of EalánOS, which is distributed * under the terms of the GNU Affero General Public License version 3. */ +#pragma once #include #include diff --git a/repos/os/src/hoitaja/memory_controller.h b/repos/os/src/hoitaja/memory_controller.h index e69de29bb2..b6393fb363 100644 --- a/repos/os/src/hoitaja/memory_controller.h +++ b/repos/os/src/hoitaja/memory_controller.h @@ -0,0 +1,2 @@ + +#pragma once \ No newline at end of file diff --git a/repos/os/src/hoitaja/numa_controller.h b/repos/os/src/hoitaja/numa_controller.h index e69de29bb2..b6393fb363 100644 --- a/repos/os/src/hoitaja/numa_controller.h +++ b/repos/os/src/hoitaja/numa_controller.h @@ -0,0 +1,2 @@ + +#pragma once \ No newline at end of file From 278fbb2281c59517155e1e604c626a082a674833 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:07:49 +0200 Subject: [PATCH 31/40] hoitaja: Added state_handler callback for notifying hoitaja about a cells construction or termination. --- repos/os/src/hoitaja/main.cc | 30 +++++++++++++++------------- repos/os/src/hoitaja/state_handler.h | 13 ++++++++++++ 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 repos/os/src/hoitaja/state_handler.h diff --git a/repos/os/src/hoitaja/main.cc b/repos/os/src/hoitaja/main.cc index 26839672d6..22e06e8d00 100644 --- a/repos/os/src/hoitaja/main.cc +++ b/repos/os/src/hoitaja/main.cc @@ -28,6 +28,8 @@ #include "numa_controller.h" /* Core Allocator */ #include "core_allocator.h" +/* State Handler */ +#include "state_handler.h" namespace Hoitaja { @@ -37,11 +39,11 @@ namespace Hoitaja { } -struct Hoitaja::Main : Genode::Sandbox::State_handler +struct Hoitaja::Main : Genode::Sandbox::State_handler, Hoitaja::State_handler { Env &_env; - Habitat _sandbox { _env, *this }; + Habitat _sandbox { _env, *this, *this }; Timer::Connection _timer{_env}; @@ -90,8 +92,8 @@ struct Hoitaja::Main : Genode::Sandbox::State_handler { //log("Hoitaja's entering its maintance cycle"); // For now just print all cells created by Hoitaja - //_sandbox.maintain_cells(); - _timer.trigger_once(1000 * 1000); + //_handle_config(); + //_timer.trigger_once(1000 * 1000); } Signal_handler
_timeout_handler{ @@ -102,7 +104,8 @@ struct Hoitaja::Main : Genode::Sandbox::State_handler */ void handle_sandbox_state() override { - + Genode::log("Habitat state changed"); + /* try { Reporter::Xml_generator xml(*_reporter, [&] () { _sandbox.generate_state_report(xml); }); @@ -111,13 +114,19 @@ struct Hoitaja::Main : Genode::Sandbox::State_handler error("state report exceeds maximum size"); - /* try to reflect the error condition as state report */ + try to reflect the error condition as state report try { Reporter::Xml_generator xml(*_reporter, [&] () { xml.attribute("error", "report buffer exceeded"); }); } catch (...) { } - } + }*/ + } + + void handle_habitat_state(Cell &cell) override + { + Genode::log("Habitat changed"); + _sandbox.update(cell); } Main(Env &env) : _env(env) @@ -133,12 +142,5 @@ struct Hoitaja::Main : Genode::Sandbox::State_handler } }; -void Hoitaja::Habitat::maintain_cells() -{ - log("My current children are:"); - _children.for_each_child([&](Child &child) - { log(child.name(), " ram: ", child.ram_quota()); }); -} - void Component::construct(Genode::Env &env) { static Hoitaja::Main main(env); } diff --git a/repos/os/src/hoitaja/state_handler.h b/repos/os/src/hoitaja/state_handler.h new file mode 100644 index 0000000000..e25a24d8b9 --- /dev/null +++ b/repos/os/src/hoitaja/state_handler.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace Hoitaja { + struct State_handler; + class Cell; +} + +struct Hoitaja::State_handler : Genode::Interface +{ + virtual void handle_habitat_state(Cell &cell) = 0; +}; \ No newline at end of file From e4f43c1edd09976d09c54ce8e31fb5159501a004 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:09:58 +0200 Subject: [PATCH 32/40] hoitaja: Added new CPU interface providing abstraction for task-parallelism. --- repos/os/include/suoritin/client.h | 38 ++++++ repos/os/include/suoritin/connection.h | 46 ++++++++ repos/os/include/suoritin/session.h | 101 ++++++++++++++++ repos/os/src/hoitaja/suoritin/component.cc | 131 +++++++++++++++++++++ 4 files changed, 316 insertions(+) create mode 100644 repos/os/include/suoritin/client.h create mode 100644 repos/os/include/suoritin/connection.h create mode 100644 repos/os/include/suoritin/session.h create mode 100644 repos/os/src/hoitaja/suoritin/component.cc diff --git a/repos/os/include/suoritin/client.h b/repos/os/include/suoritin/client.h new file mode 100644 index 0000000000..1acd0e93c7 --- /dev/null +++ b/repos/os/include/suoritin/client.h @@ -0,0 +1,38 @@ +/* + * \brief Suoritin - Task-based CPU Client Interface + * \author Michael Müller + * \date 2023-07-12 + */ + +/* + * Copyright (C) 2010-2020 Genode Labs GmbH + * Copyright (C) 2023 Michael Müller, Osnabrück University + * + * This file is part of EalánOS, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ +#pragma once + +#include +#include +#include + +struct Tukija::Suoritin::Client : Genode::Rpc_client +{ + explicit Client(Genode::Capability session) + : Rpc_client(session) { } + + void create_channel() override + { + call(); + } + + void register_worker(Genode::Thread::Name const &name, Genode::Thread_capability cap) override + { + call(name, cap); + } + + Capability interface_cap() override { + return call(); + } +}; \ No newline at end of file diff --git a/repos/os/include/suoritin/connection.h b/repos/os/include/suoritin/connection.h new file mode 100644 index 0000000000..48eefff1d6 --- /dev/null +++ b/repos/os/include/suoritin/connection.h @@ -0,0 +1,46 @@ +/* + * \brief Suoritin - Task-based CPU Connection + * \author Michael Müller + * \date 2023-07-12 + */ + +/* + * Copyright (C) 2010-2020 Genode Labs GmbH + * Copyright (C) 2023 Michael Müller, Osnabrück University + * + * This file is part of EalánOS, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ +#pragma once + +#include +#include + +namespace Tukija { + namespace Suoritin { + struct Connection; + } +} + +struct Tukija::Suoritin::Connection : Genode::Connection, Tukija::Suoritin::Client +{ + enum + { + RAM_QUOTA = 32768UL + }; + + Connection(Genode::Env &env, const char *label="", Genode::Affinity const &affinity = Genode::Affinity()) + : Genode::Connection(env, session(env.parent(), affinity, "ram_quota=%u, cap_quota=%u, label=\"%s\"", RAM_QUOTA, CAP_QUOTA, label)), Tukija::Suoritin::Client(cap()) {} + + void create_channel() override { + Tukija::Suoritin::Client::create_channel(); + } + + void register_worker(Genode::Thread::Name const &name, Genode::Thread_capability cap) override { + Tukija::Suoritin::Client::register_worker(name, cap); + } + + Tukija::Suoritin::Capability interface_cap() override { + return Tukija::Suoritin::Client::interface_cap(); + } +}; \ No newline at end of file diff --git a/repos/os/include/suoritin/session.h b/repos/os/include/suoritin/session.h new file mode 100644 index 0000000000..4a20c9b9e8 --- /dev/null +++ b/repos/os/include/suoritin/session.h @@ -0,0 +1,101 @@ +/* + * \brief Suoritin - Task-based CPU Service + * \author Michael Müller + * \date 2023-07-12 + */ + +/* + * Copyright (C) 2010-2020 Genode Labs GmbH + * Copyright (C) 2023 Michael Müller, Osnabrück University + * + * This file is part of EalánOS, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ +#pragma once + +#include +#include +#include +#include + +namespace Tukija { + namespace Suoritin { + struct Session; + struct Client; + class Capability; + + struct Channel; + struct Worker; + typedef Genode::Registry> Channel_list; + typedef Genode::Registry> Worker_registry; + } +} + +class Tukija::Suoritin::Capability +{ + private: + Genode::Ram_dataspace_capability _worker_space; + Genode::Ram_dataspace_capability _channel_space; + + public: + Capability(Genode::Ram_dataspace_capability worker_space, Genode::Ram_dataspace_capability channel_space) : _worker_space(worker_space), _channel_space(channel_space) {} + + Genode::Ram_dataspace_capability worker_interface() { return _worker_space; } + Genode::Ram_dataspace_capability channel_space() { return _channel_space; } +}; + +struct Tukija::Suoritin::Worker : Genode::Interface +{ + Genode::Thread_capability _cap{}; + Genode::Thread::Name _name{}; +}; + +struct Tukija::Suoritin::Channel : Genode::Interface +{ + unsigned long _id{0}; + unsigned long _length{0}; + unsigned long _occupancy{0}; +}; + +struct Tukija::Suoritin::Session : Genode::Session +{ + static const char *service_name() { return "TASKING"; } + + enum + { + CAP_QUOTA = 6 + }; + + /** + * @brief List of all channels, i.e. worker queues, of the client cell + * + */ + Channel_list _channels{}; + + /** + * @brief List of worker threads for this client + * + */ + Worker_registry _workers{}; + + virtual ~Session() { } + + /************************ + ** internal interface ** + ************************/ + Channel_list &channels() { return _channels; } + Worker_registry &workers() { return _workers; } + + /******************************** + ** Suoritin session interface ** + ********************************/ + virtual void create_channel() = 0; + virtual void register_worker(Genode::Thread::Name const &name, Genode::Thread_capability cap) = 0; + virtual Capability interface_cap() = 0; + + GENODE_RPC(Rpc_create_channel, void, create_channel); + GENODE_RPC(Rpc_register_worker, void, register_worker, Genode::Thread::Name const&, Genode::Thread_capability); + GENODE_RPC(Rpc_suoritin_cap, Tukija::Suoritin::Capability, interface_cap); + + GENODE_RPC_INTERFACE(Rpc_create_channel, Rpc_register_worker, Rpc_suoritin_cap); +}; \ No newline at end of file diff --git a/repos/os/src/hoitaja/suoritin/component.cc b/repos/os/src/hoitaja/suoritin/component.cc new file mode 100644 index 0000000000..0c2096776d --- /dev/null +++ b/repos/os/src/hoitaja/suoritin/component.cc @@ -0,0 +1,131 @@ +/* Genode includes */ +#include +#include +#include +#include +#include + +#include +#include +#include + + + +namespace Tukija { + namespace Suoritin { + class Session_component; + template class Allocator; + } +} + +template +class Tukija::Suoritin::Allocator : public Genode::Allocator +{ + using size_t = Genode::size_t; + +private: + Genode::Region_map::Local_addr _dataspace{}; + size_t _interface_size; + Genode::Region_map::Local_addr _pos {_dataspace}; + +public: + Allocator(Genode::Env &env, Genode::Ram_dataspace_capability *_interface_cap, size_t interface_size) : _interface_size(interface_size) + { + *_interface_cap = env.ram().alloc(interface_size); + _dataspace = static_cast(env.rm().attach(*_interface_cap)); + } + + Alloc_result try_alloc(size_t) override + { + T *pos = _pos; + if (pos >= static_cast(_dataspace) + _interface_size) + return Alloc_result(Genode::Ram_allocator::Alloc_error::OUT_OF_RAM); + + pos++; + return Alloc_result(static_cast(pos)); + } + + void free(void *, size_t) override + { } + + size_t overhead(size_t) const override { return 0; } + + bool need_size_for_free() const override { return false; } + + T *interface() { return _dataspace; } +}; + +class Tukija::Suoritin::Session_component : public Genode::Session_object +{ + private: + Genode::Affinity _affinity; + Genode::Env &_env; + Genode::Ram_dataspace_capability _workers_interface_cap{}; + Genode::Ram_dataspace_capability _channels_interface_cap{}; + + Allocator> _worker_allocator; + Allocator> _channel_allocator; + + unsigned long no_channels{0}; + unsigned long no_workers{0}; + + template + void construct(FUNC const &fn, Allocator> &alloc, Genode::Registry> ®istry) { + T* object = nullptr; + + try { + try { + object = new (alloc) Genode::Registered(registry); + fn(object); + } catch (Genode::Allocator::Out_of_memory) { + Genode::error("Out of RAM on registering worker."); + throw; + } + } catch (...) { + if (object) + destroy(alloc, object); + Genode::error("Exception caught registering worker"); + throw; + } + } + + + public: + Session_component(Genode::Rpc_entrypoint &session_ep, + Genode::Session::Resources const &resources, + Genode::Session::Label const &label, + Genode::Session::Diag const &diag, + Genode::Env &env, + Genode::Affinity &affinity) + : Session_object(session_ep, resources, label, diag), _affinity(affinity.space().total() ? affinity : Genode::Affinity(Genode::Affinity::Space(1,1), Genode::Affinity::Location(0,0,1,1))), + _env(env), _worker_allocator(env, &_workers_interface_cap, _affinity.space().total()*sizeof(Genode::Registered)), + _channel_allocator(env, &_channels_interface_cap, _affinity.space().total()*sizeof(Genode::Registered)) + { + } + + void create_channel() override + { + try { + construct([&](Channel *) {}, _channel_allocator, _channels); + } + catch (...) + { + Genode::error("Faild to create channel"); + } + } + void register_worker(Genode::Thread::Name const &name, Genode::Thread_capability cap) override { + try { + construct([&](Worker *worker) + { worker->_cap = cap; + worker->_name = name; }, _worker_allocator, _workers); + } + catch (...) + { + Genode::error("Failed to register worker"); + } + } + + Capability interface_cap() override { + return Capability{_workers_interface_cap, _channels_interface_cap}; + } +}; \ No newline at end of file From ecf760f4b5ef1a78a1142909227dd0f254f01787 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:11:32 +0200 Subject: [PATCH 33/40] Print directories for verbose build to create build.log for vscode. --- tool/builddir/build.mk | 2 +- tool/depot/mk/common.inc | 2 +- tool/ports/prepare_port | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tool/builddir/build.mk b/tool/builddir/build.mk index ed31c74654..9e53c3a9cc 100644 --- a/tool/builddir/build.mk +++ b/tool/builddir/build.mk @@ -64,7 +64,7 @@ INSTALL_DIR := $(CURDIR)/bin export BASE_DIR ?= ../base export REPOSITORIES ?= $(BASE_DIR:%base=%base-linux) $(BASE_DIR) export VERBOSE ?= @ -export VERBOSE_DIR ?= --no-print-directory +export VERBOSE_DIR ?= --print-directory export VERBOSE_MK ?= @ export LIB_CACHE_DIR ?= $(BUILD_BASE_DIR)/var/libcache export LIB_PROGRESS_LOG ?= $(BUILD_BASE_DIR)/progress.log diff --git a/tool/depot/mk/common.inc b/tool/depot/mk/common.inc index 29606ee4d8..4385bdbc93 100644 --- a/tool/depot/mk/common.inc +++ b/tool/depot/mk/common.inc @@ -9,7 +9,7 @@ VERBOSE ?= @ ECHO := echo -e HASHSUM := sha1sum -MAKEFLAGS += --no-print-directory +MAKEFLAGS += --print-directory BRIGHT_COL ?= \x1b[01;33m DARK_COL ?= \x1b[00;33m diff --git a/tool/ports/prepare_port b/tool/ports/prepare_port index d95f49d9b0..88e403bc7e 100755 --- a/tool/ports/prepare_port +++ b/tool/ports/prepare_port @@ -7,7 +7,7 @@ # ifndef VERBOSE -MAKEFLAGS += --no-print-directory +MAKEFLAGS += --print-directory endif export GENODE_DIR := $(realpath $(dir $(MAKEFILE_LIST))/../..) From 6b31730301afbfb8adcae9add4beb13444f9d027 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:12:51 +0200 Subject: [PATCH 34/40] Removed default memory configuration for qemu. --- repos/ports/run/tool_chain_auto.run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/ports/run/tool_chain_auto.run b/repos/ports/run/tool_chain_auto.run index 47d55537ac..81e9730cd9 100644 --- a/repos/ports/run/tool_chain_auto.run +++ b/repos/ports/run/tool_chain_auto.run @@ -254,7 +254,7 @@ lappend boot_modules libc.lib.so vfs.lib.so vfs_pipe.lib.so vfs build_boot_image $boot_modules -append qemu_args " -nographic -m 800 " +append qemu_args " -nographic " # wait until Noux started run_genode_until {.*\[init -> vfs\] creating build directory\.\.\..*\n} $boot_timeout From 667e272a8a16c008d63046ed2f7024c0163c70f9 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:13:34 +0200 Subject: [PATCH 35/40] hoitaja: Added Suoritin as service to Hoitaja. --- repos/os/src/hoitaja/target.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/os/src/hoitaja/target.mk b/repos/os/src/hoitaja/target.mk index d27ce27095..782f7bbc15 100644 --- a/repos/os/src/hoitaja/target.mk +++ b/repos/os/src/hoitaja/target.mk @@ -1,5 +1,5 @@ TARGET = hoitaja -SRC_CC = main.cc +SRC_CC = main.cc habitat.cc suoritin/component.cc LIBS = base INC_DIR += $(PRG_DIR) From d1ee1e82a4ebc83fa7f4bf9b281683816f0fc54f Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:14:40 +0200 Subject: [PATCH 36/40] top: Changed CPU configuration to work with topology used by blinktree. --- repos/os/src/app/top/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/os/src/app/top/main.cc b/repos/os/src/app/top/main.cc index ebdd3aae8b..2516db03d1 100644 --- a/repos/os/src/app/top/main.cc +++ b/repos/os/src/app/top/main.cc @@ -68,7 +68,7 @@ struct Trace_subject_registry return nullptr; } - enum { MAX_CPUS_X = 16, MAX_CPUS_Y = 4, MAX_ELEMENTS_PER_CPU = 6}; + enum { MAX_CPUS_X = 64, MAX_CPUS_Y = 1, MAX_ELEMENTS_PER_CPU = 6}; /* accumulated execution time on all CPUs */ unsigned long long total_first [MAX_CPUS_X][MAX_CPUS_Y]; From 1b3eef72f71bbef70916f74dec6dceca252b4e51 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:18:58 +0200 Subject: [PATCH 37/40] Example for using the low-level interface for PMCs. --- repos/mml/src/app/pfm_test/main.cc | 109 +++++++++++++++++++++++++++ repos/mml/src/app/pfm_test/target.mk | 4 + 2 files changed, 113 insertions(+) create mode 100644 repos/mml/src/app/pfm_test/main.cc create mode 100644 repos/mml/src/app/pfm_test/target.mk diff --git a/repos/mml/src/app/pfm_test/main.cc b/repos/mml/src/app/pfm_test/main.cc new file mode 100644 index 0000000000..98953cf817 --- /dev/null +++ b/repos/mml/src/app/pfm_test/main.cc @@ -0,0 +1,109 @@ +/** + * @file main.cc + * @author Michael Müller (michael.mueller@uos.de) + * @brief Some Tests for using Performance Counters with libpfm and the NOVA syscalls + * @version 0.1 + * @date 2022-12-14 + * + * @copyright Copyright (c) 2022 + * + */ +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include +#include +} + +int main(void) +{ + pfm_pmu_info_t pinfo; + pfm_pmu_encode_arg_t e; + pfm_event_info_t info; + int ret; + + ret = pfm_initialize(); + if (ret != PFM_SUCCESS) { + std::cerr << "cannot initialize libpfm: " << pfm_strerror(ret) << std::endl; + return EXIT_FAILURE; + } + + memset(&pinfo, 0, sizeof(pfm_pmu_info_t)); + + ret = pfm_get_pmu_info(PFM_PMU_AMD64_FAM17H_ZEN1, &pinfo); + if (ret != PFM_SUCCESS) + { + std::cerr << "Failed to find PMU" << std::endl; + return -EXIT_FAILURE; + } + + if (!pinfo.is_present) { + std::cerr << "No AMD PMU present" << std::endl; + return -EXIT_FAILURE; + } + + memset(&e, 0, sizeof(e)); + + char *fqstr = nullptr; + e.fstr = &fqstr; + + do + { + ret = pfm_get_os_event_encoding("ITLB_RELOADS", PFM_PLM0 | PFM_PLM3, PFM_OS_NONE, &e); + if (ret == PFM_ERR_TOOSMALL) { + free(e.codes); + e.codes = NULL; + e.count = 0; + continue; + } else { + std::cerr << "No such event" << std::endl; + return EXIT_FAILURE; + } + } while (ret != PFM_SUCCESS); + + memset(&info, 0, sizeof(info)); + + ret = pfm_get_event_info(e.idx, PFM_OS_NONE, &info); + if (ret) { + std::cerr << "Failed to get event info" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Event found : " << fqstr << std::endl; + std::cout << "Code : " << info.code << std::endl; + + Nova::uint8_t rc = 0; + Nova::mword_t umask = 0x6; + Nova::mword_t flags = 0x0; + + if ((rc = Nova::hpc_ctrl(Nova::HPC_SETUP, 0, 1, info.code, umask, flags)) != Nova::NOVA_OK) { + std::cerr << "Failed to setup HPC 0 for event" << std::endl; + return EXIT_FAILURE; + } + + if ((rc = Nova::hpc_start(0, 1))) { + std::cerr << "Failed to start counter" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Successfully set up hardware performance counter 0" << std::endl; + + for (;;) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + Nova::mword_t value; + if ((rc = Nova::hpc_read(0, 1, value)) != Nova::NOVA_OK) { + std::cerr << "Failed to read HPC" << std::endl; + return EXIT_FAILURE; + } + std::cout << "Counter value: " << value << std::endl; + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/repos/mml/src/app/pfm_test/target.mk b/repos/mml/src/app/pfm_test/target.mk new file mode 100644 index 0000000000..5f61b6d5ed --- /dev/null +++ b/repos/mml/src/app/pfm_test/target.mk @@ -0,0 +1,4 @@ +TARGET = pfm_test +SRC_CC = main.cc +LIBS += base posix libm libc stdcxx libpfm4 +CC_OPT += -Wno-error -fpermissive -Wno-error=conversion \ No newline at end of file From 02dd7fdb78baec27489d8a5cc497eb9532655f03 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:23:21 +0200 Subject: [PATCH 38/40] Updated vscode configuration. --- .vscode/c_cpp_properties.json | 49 ++++++++++---- .vscode/settings.json | 124 +++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 17 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 366b9d892f..0b1754143a 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,11 +3,25 @@ { "name": "EalánOS", "includePath": [ - "${workspaceFolder}/depot/genodelabs/api/libc/**", - "${workspaceFolder}/depot/genodelabs/api/stdcxx/**", - "${workspaceFolder}/repos/base/**", - "${workspaceFolder}/repos/base-nova/**", - "${workspaceFolder}/repos/**" + "${workspaceFolder}/repos/**", + "${workspaceFolder}/repos/mml/**", + "${workspaceFolder}/repos/libports/include/**", + "${workspaceFolder}/contrib/mxtasking-07a3844690ae8eb15832d93e29567a5a8e6e45af/include/**", + "${workspaceFolder}/contrib/libpfm4-b0ec09148c2be9f4a96203a3d2de4ebed6ce2da0/include/**", + "${workspaceFolder}/contrib/libc-c7cd230b11ca71979f32950803bc78b45adfa0ce/include/libc/**", + "${workspaceFolder}/contrib/libc-c7cd230b11ca71979f32950803bc78b45adfa0ce/include/spec/x86_64/libc", + "${workspaceFolder}/contrib/libc-c7cd230b11ca71979f32950803bc78b45adfa0ce/include/libc/sys/**", + "${workspaceFolder}/contrib/stdcxx-d2865c41fafbbf66051d38e7b742c4d5bc2f05a3/include/stdcxx/", + "${workspaceFolder}/contrib/stdcxx-d2865c41fafbbf66051d38e7b742c4d5bc2f05a3/include/stdcxx/std", + "${workspaceFolder}/contrib/stdcxx-d2865c41fafbbf66051d38e7b742c4d5bc2f05a3/include/stdcxx/c_std", + "${workspaceFolder}/repos/libports/include/spec/x86_64/stdcxx", + "${workspaceFolder}/repos/base-nova/src/core/include/**", + "${workspaceFolder}/repos/base-nova/src/include/**", + "${workspaceFolder}/repos/base-nova/include/**", + "${workspaceFolder}/repos/base/src/core/include/**", + "${workspaceFolder}/repos/base/src/include/**", + "${workspaceFolder}/repos/base/include/**", + "/usr/local/genode/tool/21.05/lib/gcc/x86_64-pc-elf/10.3.0/include" ], "defines": [ "__GENODE__", @@ -16,30 +30,35 @@ "_GLIBCXX_ATOMIC_BUILTINS_4", "_GLIBCXX_NO_OBSOLETE_ISINF_ISNAN_DYNAMIC" ], - "compilerPath": "/usr/local/genode/tool/21.05/bin/genode-x86-g++", + "compilerPath": "/usr/local/genode/tool/21.05/bin/genode-x86-gcc", "cStandard": "gnu17", "cppStandard": "gnu++17", - "intelliSenseMode": "${default}", + "intelliSenseMode": "linux-gcc-x64", "compilerArgs": [ "-nostdinc", - "-m64", - "-mcmodel=large", - "-MMD", - "-MP", - "-MT" + "-m64" ], - "configurationProvider": "ms-vscode.cmake-tools" + "configurationProvider": "ms-vscode.makefile-tools", + "forcedInclude": [ + "${workspaceFolder}/contrib/libc-c7cd230b11ca71979f32950803bc78b45adfa0ce/include/libc/stdint.h" + ], + "mergeConfigurations": false, + "browse": { + "limitSymbolsToIncludedHeaders": true + } }, { "name": "Genode", "includePath": [ - "${workspaceFolder}/**" + "${workspaceFolder}/**", + "${workspaceFolder}/repos/base/**" ], "defines": [], "compilerPath": "/usr/bin/clang", "cStandard": "c17", "cppStandard": "c++14", - "intelliSenseMode": "linux-clang-x64" + "intelliSenseMode": "linux-clang-x64", + "configurationProvider": "ms-vscode.makefile-tools" } ], "version": 4 diff --git a/.vscode/settings.json b/.vscode/settings.json index dc2cb39413..3654d91693 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,8 +5,128 @@ "initializer_list": "cpp", "streambuf": "cpp", "tuple": "cpp", - "memory": "cpp" + "memory": "cpp", + "*.def": "cpp", + "array": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "string": "cpp", + "vector": "cpp", + "any": "cpp", + "executor": "cpp", + "internet": "cpp", + "io_context": "cpp", + "memory_resource": "cpp", + "socket": "cpp", + "string_view": "cpp", + "timer": "cpp", + "functional": "cpp", + "rope": "cpp", + "slist": "cpp", + "coroutine": "cpp", + "future": "cpp", + "scoped_allocator": "cpp", + "valarray": "cpp", + "regex": "cpp", + "cstdint": "cpp", + "bitset": "cpp", + "random": "cpp", + "optional": "cpp", + "dynamic_bitset": "cpp", + "mutex": "cpp", + "shared_mutex": "cpp", + "algorithm": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cassert": "cpp", + "cctype": "cpp", + "cerrno": "cpp", + "chrono": "cpp", + "ciso646": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "map": "cpp", + "unordered_map": "cpp", + "exception": "cpp", + "fstream": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "new": "cpp", + "numeric": "cpp", + "ostream": "cpp", + "queue": "cpp", + "ranges": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "system_error": "cpp", + "thread": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "utility": "cpp", + "variant": "cpp", + "charconv": "cpp", + "cfenv": "cpp", + "cinttypes": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cuchar": "cpp", + "set": "cpp", + "unordered_set": "cpp", + "codecvt": "cpp", + "condition_variable": "cpp", + "iomanip": "cpp", + "*.run": "xml", + "span": "cpp" }, "vscode-as-git-mergetool.settingsAssistantOnStartup": false, - "makefile.makeDirectory": "build/x86_64" + "makefile.makeDirectory": "build/x86_64", + "C_Cpp.errorSquiggles": "enabledIfIncludesResolve", + "C_Cpp.default.cppStandard": "gnu++17", + "C_Cpp.default.cStandard": "gnu17", + "C_Cpp.workspaceSymbols": "Just My Code", + "C_Cpp.inlayHints.parameterNames.enabled": true, + "C_Cpp.inlayHints.autoDeclarationTypes.showOnLeft": true, + "C_Cpp.intelliSenseMemoryLimit": 16384, + "makefile.makefilePath": "", + "makefile.dryrunSwitches": [ + "--keep-going", + "--print-directory", + "KERNEL=nova", + "BOARD=pc", + "run/vscode", + "VERBOSE=" + ], + "C_Cpp.default.intelliSenseMode": "linux-gcc-x64", + "C_Cpp.default.mergeConfigurations": true, + "C_Cpp.autocompleteAddParentheses": true, + "C_Cpp.intelliSenseCacheSize": 20480, + "makefile.buildBeforeLaunch": false, + "makefile.extensionOutputFolder": ".vscode", + "makefile.configurationCachePath": ".vscode/configurationCache.log", + "explorer.excludeGitIgnore": true, + "makefile.buildLog": ".vscode/build.log", + "definition-autocompletion.update_index_on_change": true, + "definition-autocompletion.update_index_interval": 5, + "C_Cpp.intelliSenseEngineFallback": "disabled", + "makefile.extensionLog": ".vscode/extension.log", + "makefile.ignoreDirectoryCommands": false, + "html.format.wrapLineLength": 80, + "editor.wordWrap": "bounded", + "editor.wordWrapColumn": 90 } \ No newline at end of file From 6f60db5209b107e9367a39e5893502eec5aca8b4 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:31:51 +0200 Subject: [PATCH 39/40] blinktree: More complex scenario using TRACE service and top. --- repos/mml/run/blinktree.run | 159 ++++++++++++++++++++++++++++++++++-- 1 file changed, 153 insertions(+), 6 deletions(-) diff --git a/repos/mml/run/blinktree.run b/repos/mml/run/blinktree.run index 0da72928ee..e211ca01b4 100644 --- a/repos/mml/run/blinktree.run +++ b/repos/mml/run/blinktree.run @@ -1,5 +1,5 @@ set build_components { - core init timer app/blinktree + core init timer app/blinktree app/top } source ${genode_dir}/repos/base/run/platform_drv.inc @@ -22,8 +22,9 @@ set config { + - + @@ -40,9 +41,10 @@ set config { } append config { - - - + + + + @@ -62,13 +64,158 @@ append config { + + } install_config $config set boot_modules { - core init timer vfs.lib.so ld.lib.so libm.lib.so libc.lib.so stdcxx.lib.so posix.lib.so blinktree fill_randint_workloada mixed_randint_workloada + core init timer vfs.lib.so ld.lib.so libm.lib.so libc.lib.so stdcxx.lib.so posix.lib.so blinktree top fill_randint_workloada mixed_randint_workloada } append_platform_drv_boot_modules From 1205032bd6c2980fd50bbcaa142c886f2f971869 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Mon, 7 Aug 2023 18:33:24 +0200 Subject: [PATCH 40/40] Use absolute path to resize2fs as on Debian /sbin is not in PATH by default. --- tool/run/image/disk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/run/image/disk b/tool/run/image/disk index 59245d05be..9ef15f62c5 100644 --- a/tool/run/image/disk +++ b/tool/run/image/disk @@ -54,7 +54,7 @@ proc run_image { {unused ""} } { if {[image_disk_size] == 0} { # resize image to only needed size and get size of resized image - exec resize2fs -M [run_dir].partition 2>/dev/null + exec /sbin/resize2fs -M [run_dir].partition 2>/dev/null set disk_size_b [expr [regsub {\s.*} [exec wc -c [run_dir].partition] {}]] set disk_size_kb [expr $disk_size_b / 1024] }