diff --git a/repos/os/run/init.run b/repos/os/run/init.run
index b022f21297..18c7d29274 100644
--- a/repos/os/run/init.run
+++ b/repos/os/run/init.run
@@ -456,7 +456,7 @@ append config {
-
+
@@ -467,6 +467,236 @@ append config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/repos/os/src/app/dummy/main.cc b/repos/os/src/app/dummy/main.cc
index 7526ad3f16..b010eab292 100644
--- a/repos/os/src/app/dummy/main.cc
+++ b/repos/os/src/app/dummy/main.cc
@@ -24,6 +24,8 @@ namespace Dummy {
struct Log_service;
struct Log_connections;
+ struct Ram_consumer;
+ struct Resource_yield_handler;
struct Main;
using namespace Genode;
}
@@ -109,6 +111,64 @@ struct Dummy::Log_connections
};
+struct Dummy::Ram_consumer
+{
+ size_t _amount = 0;
+
+ Ram_dataspace_capability _ds_cap;
+
+ Ram_session &_ram;
+
+ Ram_consumer(Ram_session &ram) : _ram(ram) { }
+
+ void release()
+ {
+ if (!_amount)
+ return;
+
+ log("release ", Number_of_bytes(_amount), " bytes of memory");
+ _ram.free(_ds_cap);
+
+ _ds_cap = Ram_dataspace_capability();
+ _amount = 0;
+ }
+
+ void consume(size_t amount)
+ {
+ if (_amount)
+ release();
+
+ log("consume ", Number_of_bytes(amount), " bytes of memory");
+ _ds_cap = _ram.alloc(amount);
+ _amount = amount;
+ }
+};
+
+
+struct Dummy::Resource_yield_handler
+{
+ Env &_env;
+
+ Ram_consumer &_ram_consumer;
+
+ void _handle_yield()
+ {
+ log("got yield request");
+ _ram_consumer.release();
+ _env.parent().yield_response();
+ }
+
+ Signal_handler _yield_handler {
+ _env.ep(), *this, &Resource_yield_handler::_handle_yield };
+
+ Resource_yield_handler(Env &env, Ram_consumer &ram_consumer)
+ : _env(env), _ram_consumer(ram_consumer)
+ {
+ _env.parent().yield_sigh(_yield_handler);
+ }
+};
+
+
struct Dummy::Main
{
Env &_env;
@@ -125,6 +185,10 @@ struct Dummy::Main
Signal_handler _config_handler { _env.ep(), *this, &Main::_handle_config };
+ Ram_consumer _ram_consumer { _env.ram() };
+
+ Constructible _resource_yield_handler;
+
void _handle_config()
{
_config.update();
@@ -150,6 +214,12 @@ struct Dummy::Main
if (node.type() == "log_service")
_log_service.construct(_env);
+ if (node.type() == "consume_ram")
+ _ram_consumer.consume(node.attribute_value("amount", Number_of_bytes()));
+
+ if (node.type() == "handle_yield_requests")
+ _resource_yield_handler.construct(_env, _ram_consumer);
+
if (node.type() == "sleep") {
if (!_timer.constructed())
diff --git a/repos/os/src/init/child.cc b/repos/os/src/init/child.cc
index a9faa91e7b..90680b6333 100644
--- a/repos/os/src/init/child.cc
+++ b/repos/os/src/init/child.cc
@@ -15,7 +15,8 @@
#include
-Init::Child::Apply_config_result Init::Child::apply_config(Xml_node start_node)
+Init::Child::Apply_config_result
+Init::Child::apply_config(Xml_node start_node)
{
Child_policy &policy = *this;
@@ -156,6 +157,109 @@ Init::Child::Apply_config_result Init::Child::apply_config(Xml_node start_node)
}
+static Init::Ram_quota assigned_ram_from_start_node(Genode::Xml_node start_node)
+{
+ using namespace Init;
+
+ size_t assigned = 0;
+
+ start_node.for_each_sub_node("resource", [&] (Xml_node resource) {
+ if (resource.attribute_value("name", String<8>()) == "RAM")
+ assigned = resource.attribute_value("quantum", Number_of_bytes()); });
+
+ return Ram_quota { assigned };
+}
+
+
+void Init::Child::apply_ram_upgrade()
+{
+ Ram_quota const assigned_ram_quota = assigned_ram_from_start_node(_start_node->xml());
+
+ if (assigned_ram_quota.value <= _resources.assigned_ram_quota.value)
+ return;
+
+ size_t const increase = assigned_ram_quota.value
+ - _resources.assigned_ram_quota.value;
+ size_t const limit = _ram_limit_accessor.ram_limit().value;
+ size_t const transfer = min(increase, limit);
+
+ if (increase > limit)
+ warning(name(), ": assigned RAM exceeds available RAM");
+
+ /*
+ * Remember assignment and apply RAM upgrade to child
+ *
+ * Note that we remember the actually transferred amount as the
+ * assigned amount. In the case where the value is clamped to to
+ * the limit, the value as given in the config remains diverged
+ * from the assigned value. This way, a future config update will
+ * attempt the completion of the upgrade if memory become
+ * available.
+ */
+ if (transfer) {
+ _resources.assigned_ram_quota =
+ Ram_quota { _resources.assigned_ram_quota.value + transfer };
+
+ _check_resource_constraints(_ram_limit_accessor.ram_limit());
+
+ ref_ram().transfer_quota(_child.ram_session_cap(), transfer);
+ }
+}
+
+
+void Init::Child::apply_ram_downgrade()
+{
+ Ram_quota const assigned_ram_quota = assigned_ram_from_start_node(_start_node->xml());
+
+ if (assigned_ram_quota.value >= _resources.assigned_ram_quota.value)
+ return;
+
+ size_t const decrease = _resources.assigned_ram_quota.value
+ - assigned_ram_quota.value;
+
+ /*
+ * The child may concurrently consume quota from its RAM session,
+ * causing the 'transfer_quota' to fail. For this reason, we repeatedly
+ * attempt the transfer.
+ */
+ unsigned max_attempts = 4, attempts = 0;
+ for (; attempts < max_attempts; attempts++) {
+
+ /* give up if the child's available RAM is exhausted */
+ size_t const preserved = 16*1024;
+ size_t const avail = _child.ram().avail();
+
+ if (avail < preserved)
+ break;
+
+ size_t const transfer = min(avail - preserved, decrease);
+
+ if (_child.ram().transfer_quota(ref_ram_cap(), transfer) == 0) {
+ _resources.assigned_ram_quota =
+ Ram_quota { _resources.assigned_ram_quota.value - transfer };
+ break;
+ }
+ }
+
+ if (attempts == max_attempts)
+ warning(name(), ": RAM downgrade failed after ", max_attempts, " attempts");
+
+ /*
+ * If designated RAM quota is lower than the child's consumed RAM, issue
+ * a yield request to the child.
+ */
+ if (assigned_ram_quota.value < _resources.assigned_ram_quota.value) {
+
+ size_t const demanded = _resources.assigned_ram_quota.value
+ - assigned_ram_quota.value;
+
+ Parent::Resource_args const args { "ram_quota=", Number_of_bytes(demanded) };
+ _child.yield(args);
+ }
+
+}
+
+
void Init::Child::report_state(Xml_generator &xml, Report_detail const &detail) const
{
xml.node("child", [&] () {
@@ -174,6 +278,10 @@ void Init::Child::report_state(Xml_generator &xml, Report_detail const &detail)
if (detail.child_ram() && _child.ram_session_cap().valid()) {
xml.node("ram", [&] () {
+
+ xml.attribute("assigned", String<32> {
+ Number_of_bytes(_resources.assigned_ram_quota.value) });
+
/*
* The const cast is needed because there is no const
* accessor for the RAM session of the child.
@@ -215,8 +323,8 @@ void Init::Child::init(Ram_session &session, Ram_session_capability cap)
size_t const initial_session_costs =
session_alloc_batch_size()*_child.session_factory().session_costs();
- size_t const transfer_ram = _resources.effective_ram_quota.value > initial_session_costs
- ? _resources.effective_ram_quota.value - initial_session_costs
+ size_t const transfer_ram = _resources.effective_ram_quota().value > initial_session_costs
+ ? _resources.effective_ram_quota().value - initial_session_costs
: 0;
if (transfer_ram)
_env.ram().transfer_quota(cap, transfer_ram);
@@ -511,6 +619,7 @@ Init::Child::Child(Env &env,
Xml_node start_node,
Default_route_accessor &default_route_accessor,
Name_registry &name_registry,
+ Ram_quota ram_limit,
Ram_limit_accessor &ram_limit_accessor,
long prio_levels,
Affinity::Space const &affinity_space,
@@ -525,13 +634,14 @@ Init::Child::Child(Env &env,
_ram_limit_accessor(ram_limit_accessor),
_name_registry(name_registry),
_resources(_resources_from_start_node(start_node, prio_levels, affinity_space)),
+ _resources_checked((_check_resource_constraints(ram_limit), true)),
_parent_services(parent_services),
_child_services(child_services),
_session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm())
{
if (_verbose.enabled()) {
log("child \"", _unique_name, "\"");
- log(" RAM quota: ", Number_of_bytes(_resources.effective_ram_quota.value));
+ log(" RAM quota: ", Number_of_bytes(_resources.effective_ram_quota().value));
log(" ELF binary: ", _binary_name);
log(" priority: ", _resources.priority);
}
diff --git a/repos/os/src/init/child.h b/repos/os/src/init/child.h
index 73183798b0..558e9988c0 100644
--- a/repos/os/src/init/child.h
+++ b/repos/os/src/init/child.h
@@ -125,9 +125,13 @@ class Init::Child : Child_policy, Child_service::Wakeup
long priority;
Affinity affinity;
Ram_quota assigned_ram_quota;
- Ram_quota effective_ram_quota;
size_t cpu_quota_pc;
bool constrain_phys;
+
+ Ram_quota effective_ram_quota() const
+ {
+ return Ram_quota { Genode::Child::effective_ram_quota(assigned_ram_quota.value) };
+ }
};
Resources _resources_from_start_node(Xml_node start_node, long prio_levels,
@@ -157,20 +161,17 @@ class Init::Child : Child_policy, Child_service::Wakeup
Affinity(affinity_space,
affinity_location_from_xml(affinity_space, start_node)),
Ram_quota { ram_bytes },
- Ram_quota { Genode::Child::effective_ram_quota(ram_bytes) },
cpu_quota_pc,
constrain_phys };
}
Resources _resources;
- void _check_resource_constraints()
+ void _check_resource_constraints(Ram_quota ram_limit)
{
- if (_resources.effective_ram_quota.value == 0)
+ if (_resources.effective_ram_quota().value == 0)
warning("no valid RAM RESOURCE for child \"", _unique_name, "\"");
- Ram_quota const ram_limit = _ram_limit_accessor.ram_limit();
-
/*
* If the configured RAM quota exceeds our own quota, we donate
* all remaining quota to the child.
@@ -183,7 +184,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
}
}
- bool const _resources_checked = (_check_resource_constraints(), true);
+ bool const _resources_checked;
Registry &_parent_services;
Registry &_child_services;
@@ -335,11 +336,19 @@ class Init::Child : Child_policy, Child_service::Wakeup
/**
* Constructor
*
- * \param alloc allocator solely used for configuration-dependent
- * allocations. It is not used for allocations on behalf
- * of the child's behavior.
+ * \param alloc allocator solely used for configuration-
+ * dependent allocations. It is not used for
+ * allocations on behalf of the child's
+ * behavior.
*
- * \throw Allocator::Out_of_memory could not buffer the XML start node
+ * \param ram_limit maximum amount of RAM to be consumed at
+ * creation time.
+ *
+ * \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,
@@ -349,6 +358,7 @@ class Init::Child : Child_policy, Child_service::Wakeup
Xml_node start_node,
Default_route_accessor &default_route_accessor,
Name_registry &name_registry,
+ Ram_quota ram_limit,
Ram_limit_accessor &ram_limit_accessor,
long prio_levels,
Affinity::Space const &affinity_space,
@@ -411,6 +421,9 @@ class Init::Child : Child_policy, Child_service::Wakeup
*/
Apply_config_result apply_config(Xml_node start_node);
+ void apply_ram_upgrade();
+ void apply_ram_downgrade();
+
void report_state(Xml_generator &xml, Report_detail const &detail) const;
@@ -460,6 +473,12 @@ class Init::Child : Child_policy, Child_service::Wakeup
}
bool initiate_env_sessions() const override { return false; }
+
+ void yield_response() override
+ {
+ apply_ram_downgrade();
+ _report_update_trigger.trigger_report_update();
+ }
};
#endif /* _SRC__INIT__CHILD_H_ */
diff --git a/repos/os/src/init/main.cc b/repos/os/src/init/main.cc
index 6dca3e2114..415ae8f651 100644
--- a/repos/os/src/init/main.cc
+++ b/repos/os/src/init/main.cc
@@ -54,12 +54,25 @@ struct Init::Main : State_reporter::Producer, Child::Default_route_accessor,
return Ram_quota { preserve };
}
- Ram_quota _ram_limit { 0 };
+ Ram_quota _avail_ram()
+ {
+ 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 Ram_quota { 0 };
+ }
+
+ /* deduce preserved quota from available quota */
+ return Ram_quota { avail_ram.value - preserved_ram.value };
+ }
/**
* Child::Ram_limit_accessor interface
*/
- Ram_quota ram_limit() override { return _ram_limit; }
+ Ram_quota ram_limit() override { return _avail_ram(); }
void _handle_resource_avail() { }
@@ -242,6 +255,9 @@ void Init::Main::_handle_config()
_default_route.construct(_heap, _config.xml().sub_node("default-route")); }
catch (...) { }
+ long const prio_levels = prio_levels_from_xml(_config.xml());
+ Affinity::Space const affinity_space = affinity_space_from_xml(_config.xml());
+
_update_aliases_from_config();
_update_parent_services_from_config();
_abandon_obsolete_children();
@@ -257,20 +273,8 @@ 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 };
+ Ram_quota const avail_ram = _avail_ram();
/* variable used to track the RAM taken by new started children */
Ram_quota used_ram { 0 };
@@ -297,9 +301,9 @@ void Init::Main::_handle_config()
Init::Child &child = *new (_heap)
Init::Child(_env, _heap, *_verbose,
Init::Child::Id { ++_child_cnt }, _state_reporter,
- start_node, *this, _children, *this,
- prio_levels_from_xml(_config.xml()),
- affinity_space_from_xml(_config.xml()),
+ start_node, *this, _children,
+ Ram_quota { avail_ram.value - used_ram.value },
+ *this, prio_levels, affinity_space,
_parent_services, _child_services);
_children.insert(&child);
@@ -310,7 +314,6 @@ void Init::Main::_handle_config()
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) {
/*
@@ -346,6 +349,15 @@ void Init::Main::_handle_config()
*/
_children.for_each_child([&] (Child &child) {
child.initiate_env_sessions(); });
+
+ /*
+ * (Re-)distribute RAM among the childen, given their resource assignments
+ * and the available slack memory. We first apply possible downgrades to
+ * free as much memory as we can. This memory is then incorporated in the
+ * subsequent upgrade step.
+ */
+ _children.for_each_child([&] (Child &child) { child.apply_ram_downgrade(); });
+ _children.for_each_child([&] (Child &child) { child.apply_ram_upgrade(); });
}
diff --git a/repos/os/src/test/init/main.cc b/repos/os/src/test/init/main.cc
index 55d2ff29a7..83f6c1319e 100644
--- a/repos/os/src/test/init/main.cc
+++ b/repos/os/src/test/init/main.cc
@@ -56,6 +56,11 @@ static inline bool Test::xml_attribute_matches(Xml_node condition, Xml_node node
return (size_t)node.attribute_value(name.string(), Number_of_bytes()) > value;
}
+ if (condition.has_attribute("lower")) {
+ size_t const value = condition.attribute_value("lower", Number_of_bytes());
+ return (size_t)node.attribute_value(name.string(), Number_of_bytes()) < value;
+ }
+
error("missing condition in node");
return false;
}