diff --git a/repos/os/include/init/child.h b/repos/os/include/init/child.h index 124631ef9f..d10d599dff 100644 --- a/repos/os/include/init/child.h +++ b/repos/os/include/init/child.h @@ -38,6 +38,8 @@ namespace Init { using namespace Genode; using Genode::size_t; using Genode::strlen; + + struct Ram_quota { size_t value; }; } @@ -111,18 +113,6 @@ namespace Init { catch (...) { return Location(0, 0, space.width(), space.height()); } } - - /** - * Return amount of RAM that is currently unused - */ - static inline size_t avail_slack_ram_quota(size_t ram_avail) - { - size_t const preserve = 148*1024; - - return ram_avail > preserve ? ram_avail - preserve : 0; - } - - /** * Return sub string of label with the leading child name stripped out * @@ -407,10 +397,9 @@ class Init::Child : Child_policy, Child_service::Wakeup */ struct Id { unsigned value; }; - struct Default_route_accessor - { - virtual Xml_node default_route() = 0; - }; + struct Default_route_accessor { virtual Xml_node default_route() = 0; }; + + struct Ram_limit_accessor { virtual Ram_quota ram_limit() = 0; }; private: @@ -437,6 +426,8 @@ class Init::Child : Child_policy, Child_service::Wakeup Default_route_accessor &_default_route_accessor; + Ram_limit_accessor &_ram_limit_accessor; + Name_registry &_name_registry; typedef String<64> Name; @@ -491,12 +482,12 @@ class Init::Child : Child_policy, Child_service::Wakeup struct Read_quota { - Read_quota(Xml_node start_node, - size_t &ram_quota, - size_t &cpu_quota_pc, - bool &constrain_phys, - size_t const ram_avail, - Verbose const &verbose) + Read_quota(Xml_node start_node, + size_t &ram_quota, + size_t &cpu_quota_pc, + bool &constrain_phys, + Ram_quota const ram_limit, + Verbose const &verbose) { cpu_quota_pc = 0; constrain_phys = false; @@ -521,11 +512,11 @@ class Init::Child : Child_policy, Child_service::Wakeup * If the configured RAM quota exceeds our own quota, we donate * all remaining quota to the child. */ - if (ram_quota > ram_avail) { - ram_quota = ram_avail; + if (ram_quota > ram_limit.value) { + ram_quota = ram_limit.value; if (verbose.enabled()) - warn_insuff_quota(ram_avail); + warn_insuff_quota(ram_limit.value); } } }; @@ -538,23 +529,23 @@ class Init::Child : Child_policy, Child_service::Wakeup long prio_levels_log2; long priority; Affinity affinity; - size_t ram_quota; + size_t assigned_ram_quota; + size_t effective_ram_quota; size_t cpu_quota_pc; bool constrain_phys; Resources(Xml_node start_node, long prio_levels, - Affinity::Space const &affinity_space, size_t ram_avail, + Affinity::Space const &affinity_space, Ram_quota ram_limit, Verbose const &verbose) : - Read_quota(start_node, ram_quota, cpu_quota_pc, - constrain_phys, ram_avail, verbose), + Read_quota(start_node, assigned_ram_quota, cpu_quota_pc, + constrain_phys, ram_limit, verbose), prio_levels_log2(log2(prio_levels)), priority(read_priority(start_node, prio_levels)), affinity(affinity_space, read_affinity_location(affinity_space, start_node)) { - /* deduce session costs from usable ram quota */ - ram_quota = Genode::Child::effective_ram_quota(ram_quota); + effective_ram_quota = Genode::Child::effective_ram_quota(assigned_ram_quota); } } _resources; @@ -684,6 +675,7 @@ class Init::Child : Child_policy, Child_service::Wakeup Xml_node start_node, Default_route_accessor &default_route_accessor, Name_registry &name_registry, + Ram_limit_accessor &ram_limit_accessor, long prio_levels, Affinity::Space const &affinity_space, Registry &parent_services, @@ -694,24 +686,25 @@ class Init::Child : Child_policy, Child_service::Wakeup _list_element(this), _start_node(_alloc, start_node), _default_route_accessor(default_route_accessor), + _ram_limit_accessor(ram_limit_accessor), _name_registry(name_registry), _unique_name(start_node, name_registry), _binary_name(_binary_name_from_xml(start_node, _unique_name)), _resources(start_node, prio_levels, affinity_space, - avail_slack_ram_quota(_env.ram().avail()), _verbose), + ram_limit_accessor.ram_limit(), _verbose), _parent_services(parent_services), _child_services(child_services), _session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm()), _priority_policy(_resources.prio_levels_log2, _resources.priority), _ram_session_policy(_resources.constrain_phys) { - if (_resources.ram_quota == 0) + if (_resources.effective_ram_quota == 0) warning("no valid RAM resource for child " "\"", _unique_name, "\""); if (_verbose.enabled()) { log("child \"", _unique_name, "\""); - log(" RAM quota: ", _resources.ram_quota); + log(" RAM quota: ", _resources.effective_ram_quota); log(" ELF binary: ", _binary_name); log(" priority: ", _resources.priority); } @@ -758,6 +751,8 @@ class Init::Child : Child_policy, Child_service::Wakeup */ bool has_name(Child_policy::Name const &str) const { return str == name(); } + Ram_quota ram_quota() const { return Ram_quota { _resources.assigned_ram_quota }; } + void initiate_env_ram_session() { if (_state == STATE_INITIAL) { @@ -775,7 +770,7 @@ class Init::Child : Child_policy, Child_service::Wakeup /* check for completeness of the child's environment */ if (_verbose.enabled()) _child.for_each_session([&] (Session_state const &session) { - if (session.phase != Session_state::AVAILABLE) + if (!session.alive()) warning(name(), ": incomplete environment ", session.service().name(), " session " "(", session.label(), ")"); }); @@ -937,7 +932,15 @@ class Init::Child : Child_policy, Child_service::Wakeup void init(Ram_session &session, Ram_session_capability cap) override { session.ref_account(_env.ram_session_cap()); - _env.ram().transfer_quota(cap, _resources.ram_quota); + + size_t const initial_session_costs = + session_alloc_batch_size()*_child.session_factory().session_costs(); + + size_t const transfer_ram = _resources.effective_ram_quota > initial_session_costs + ? _resources.effective_ram_quota - initial_session_costs + : 0; + if (transfer_ram) + _env.ram().transfer_quota(cap, transfer_ram); } void init(Cpu_session &session, Cpu_session_capability cap) override @@ -1161,7 +1164,7 @@ class Init::Child : Child_policy, Child_service::Wakeup Arg_string::find_arg(args.string(), "ram_quota") .ulong_value(0); - if (avail_slack_ram_quota(_env.ram().avail()) < requested_ram_quota) { + if (_ram_limit_accessor.ram_limit().value < requested_ram_quota) { warning("cannot respond to resource request - out of memory"); return; } diff --git a/repos/os/run/init.run b/repos/os/run/init.run index 2cab3b803f..7fe3e06962 100644 --- a/repos/os/run/init.run +++ b/repos/os/run/init.run @@ -234,6 +234,40 @@ append config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc index 195c9b4e4f..9c9764d8b6 100644 --- a/repos/os/src/init/main.cc +++ b/repos/os/src/init/main.cc @@ -377,7 +377,8 @@ class Init::State_reporter : public Report_update_trigger }; -struct Init::Main : State_reporter::Producer, Child::Default_route_accessor +struct Init::Main : State_reporter::Producer, Child::Default_route_accessor, + Child::Ram_limit_accessor { Env &_env; @@ -395,6 +396,24 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor unsigned _child_cnt = 0; + static Ram_quota _preserved_ram_from_config(Xml_node config) + { + Number_of_bytes preserve { 40*sizeof(long)*1024 }; + + config.for_each_sub_node("resource", [&] (Xml_node node) { + if (node.attribute_value("name", String<16>()) == "RAM") + preserve = node.attribute_value("preserve", preserve); }); + + return Ram_quota { preserve }; + } + + Ram_quota _ram_limit { 0 }; + + /** + * Child::Ram_limit_accessor interface + */ + Ram_quota ram_limit() override { return _ram_limit; } + void _handle_resource_avail() { } void produce_state_report(Xml_generator &xml, Report_detail const &detail) const @@ -591,6 +610,24 @@ void Init::Main::_handle_config() _destroy_abandoned_parent_services(); + Ram_quota const preserved_ram = _preserved_ram_from_config(_config.xml()); + + Ram_quota avail_ram { _env.ram().avail() }; + + if (preserved_ram.value > avail_ram.value) { + error("RAM preservation exceeds available memory"); + return; + } + + /* deduce preserved quota from available quota */ + avail_ram = Ram_quota { avail_ram.value - preserved_ram.value }; + + /* initial RAM limit before starting new children */ + _ram_limit = Ram_quota { avail_ram.value }; + + /* variable used to track the RAM taken by new started children */ + Ram_quota used_ram { 0 }; + /* create new children */ try { _config.xml().for_each_sub_node("start", [&] (Xml_node start_node) { @@ -604,15 +641,29 @@ void Init::Main::_handle_config() return; } + if (used_ram.value > avail_ram.value) { + error("RAM exhausted while starting childen"); + throw Ram_session::Alloc_failed(); + } + try { - _children.insert(new (_heap) - Init::Child(_env, _heap, *_verbose, - Init::Child::Id { ++_child_cnt }, - _state_reporter, - start_node, *this, - _children, read_prio_levels(_config.xml()), - read_affinity_space(_config.xml()), - _parent_services, _child_services)); + Init::Child &child = *new (_heap) + Init::Child(_env, _heap, *_verbose, + Init::Child::Id { ++_child_cnt }, _state_reporter, + start_node, *this, _children, *this, + read_prio_levels(_config.xml()), + read_affinity_space(_config.xml()), + _parent_services, _child_services); + _children.insert(&child); + + /* account for the start XML node buffered in the child */ + size_t const metadata_overhead = start_node.size() + + sizeof(Init::Child); + /* track used memory and RAM limit */ + used_ram = Ram_quota { used_ram.value + + child.ram_quota().value + + metadata_overhead }; + _ram_limit = Ram_quota { avail_ram.value - used_ram.value }; } catch (Rom_connection::Rom_connection_failed) { /* diff --git a/repos/os/src/test/init/main.cc b/repos/os/src/test/init/main.cc index b6bf9382b6..4bccff519d 100644 --- a/repos/os/src/test/init/main.cc +++ b/repos/os/src/test/init/main.cc @@ -44,10 +44,20 @@ static inline bool Test::xml_attribute_matches(Xml_node condition, Xml_node node typedef String<32> Name; typedef String<64> Value; - Name const name = condition.attribute_value("name", Name()); - Value const value = condition.attribute_value("value", Value()); + Name const name = condition.attribute_value("name", Name()); - return node.attribute_value(name.string(), Value()) == value; + if (condition.has_attribute("value")) { + Value const value = condition.attribute_value("value", Value()); + return node.attribute_value(name.string(), Value()) == value; + } + + if (condition.has_attribute("higher")) { + size_t const value = condition.attribute_value("higher", Number_of_bytes()); + return (size_t)node.attribute_value(name.string(), Number_of_bytes()) > value; + } + + error("missing condition in node"); + return false; }