diff --git a/repos/os/lib/mk/sandbox.mk b/repos/os/lib/mk/sandbox.mk
index 9fe8dfab35..9e30fd1828 100644
--- a/repos/os/lib/mk/sandbox.mk
+++ b/repos/os/lib/mk/sandbox.mk
@@ -1,4 +1,4 @@
-SRC_CC = library.cc child.cc server.cc
+SRC_CC = library.cc child.cc server.cc config_model.cc
INC_DIR += $(REP_DIR)/src/lib/sandbox
LIBS += base
diff --git a/repos/os/recipes/raw/test-init/test-init.config b/repos/os/recipes/raw/test-init/test-init.config
index 98e6ebf156..aa2ac2fe23 100644
--- a/repos/os/recipes/raw/test-init/test-init.config
+++ b/repos/os/recipes/raw/test-init/test-init.config
@@ -64,7 +64,7 @@
-
+
@@ -261,7 +261,7 @@
-
+
@@ -306,7 +306,7 @@
-
+
@@ -524,6 +524,7 @@
+
@@ -551,7 +552,7 @@
-
+
@@ -759,7 +760,7 @@
-
+
@@ -838,7 +839,7 @@
-
+
@@ -1078,7 +1079,7 @@
-
+
@@ -1155,7 +1156,7 @@
-
+
@@ -1246,6 +1247,7 @@
+
@@ -1266,6 +1268,7 @@
good-case tests. -->
+
@@ -1478,11 +1481,11 @@
-
+
-
+
@@ -1513,7 +1516,7 @@
-
+
@@ -1543,7 +1546,7 @@
-
+
@@ -1657,8 +1660,8 @@
-
-
+
+
@@ -1682,7 +1685,7 @@
-
+
@@ -1701,7 +1704,7 @@
-
+
@@ -1730,7 +1733,7 @@
-
+
diff --git a/repos/os/src/init/target.mk b/repos/os/src/init/target.mk
index 279736b2d6..bc74bb5f0f 100644
--- a/repos/os/src/init/target.mk
+++ b/repos/os/src/init/target.mk
@@ -6,6 +6,6 @@ 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
+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
diff --git a/repos/os/src/lib/sandbox/alias.h b/repos/os/src/lib/sandbox/alias.h
index d723365c69..7f05a5052d 100644
--- a/repos/os/src/lib/sandbox/alias.h
+++ b/repos/os/src/lib/sandbox/alias.h
@@ -19,34 +19,28 @@
namespace Sandbox { struct Alias; }
-struct Sandbox::Alias : List::Element
+struct Sandbox::Alias : List::Element, Noncopyable
{
- typedef String<128> Name;
- typedef String<128> Child;
+ typedef Child_policy::Name Name;
+ typedef Child_policy::Name Child;
- Name name;
- Child child;
+ Name const name;
- /**
- * Exception types
+ Child child { }; /* defined by 'update' */
+
+ Alias(Name const &name) : name(name) { }
+
+ class Child_attribute_missing : Exception { };
+
+ /*
+ * \throw Child_attribute_missing
*/
- class Name_is_missing : Exception { };
- class Child_is_missing : Exception { };
-
- /**
- * Constructor
- *
- * \throw Name_is_missing
- * \throw Child_is_missing
- */
- Alias(Genode::Xml_node alias)
- :
- name (alias.attribute_value("name", Name())),
- child(alias.attribute_value("child", Child()))
+ void update(Xml_node const &alias)
{
+ if (!alias.has_attribute("child"))
+ warning("alias node \"", name, "\" lacks child attribute");
- if (!name.valid()) throw Name_is_missing();
- if (!child.valid()) throw Child_is_missing();
+ child = alias.attribute_value("child", Child());
}
};
diff --git a/repos/os/src/lib/sandbox/child.cc b/repos/os/src/lib/sandbox/child.cc
index 0a39c8cda9..6fc765dd5f 100644
--- a/repos/os/src/lib/sandbox/child.cc
+++ b/repos/os/src/lib/sandbox/child.cc
@@ -11,6 +11,7 @@
* under the terms of the GNU Affero General Public License version 3.
*/
+/* Genode includes */
#include
/* local includes */
@@ -28,14 +29,14 @@ void Sandbox::Child::destroy_services()
Sandbox::Child::Apply_config_result
Sandbox::Child::apply_config(Xml_node start_node)
{
- if (_state == STATE_ABANDONED || _exited)
+ if (abandoned() || stuck() || restart_scheduled() || _exited)
return NO_SIDE_EFFECTS;
/*
- * If the child's environment is incomplete, restart it to attempt
- * the re-routing of its environment sessions.
+ * If the child was started but its environment is incomplete, mark it as
+ * being stuck in order to restart it once the environment changes.
*/
- {
+ if (_state != State::INITIAL) {
bool env_log_exists = false, env_binary_exists = false;
_child.for_each_session([&] (Session_state const &session) {
Parent::Client::Id const id = session.id_at_client();
@@ -44,8 +45,8 @@ Sandbox::Child::apply_config(Xml_node start_node)
});
if (!env_binary_exists || !env_log_exists) {
- abandon();
- return MAY_HAVE_SIDE_EFFECTS;
+ _state = State::STUCK;
+ return NO_SIDE_EFFECTS;
}
}
@@ -62,14 +63,24 @@ Sandbox::Child::apply_config(Xml_node start_node)
if (start_node.differs_from(_start_node->xml())) {
/*
- * Start node changed
- *
+ * The node may affect the availability or unavailability
+ * of dependencies.
+ */
+ start_node.with_sub_node("route", [&] (Xml_node const &route) {
+ _start_node->xml().with_sub_node("route", [&] (Xml_node const &orig) {
+ if (route.differs_from(orig))
+ _uncertain_dependencies = true; }); });
+
+ /*
* Determine how the inline config is affected.
*/
char const * const tag = "config";
bool const config_was_present = _start_node->xml().has_sub_node(tag);
bool const config_is_present = start_node.has_sub_node(tag);
+ if (config_was_present != config_is_present)
+ _uncertain_dependencies = true;
+
if (config_was_present && !config_is_present)
config_update = CONFIG_VANISHED;
@@ -125,7 +136,10 @@ Sandbox::Child::apply_config(Xml_node start_node)
* the binary's ROM session, triggering the restart of the
* child.
*/
+ Binary_name const orig_binary_name = _binary_name;
_binary_name = _binary_from_xml(start_node, _unique_name);
+ if (orig_binary_name != _binary_name)
+ _uncertain_dependencies = true;
_heartbeat_enabled = start_node.has_sub_node("heartbeat");
@@ -136,8 +150,8 @@ Sandbox::Child::apply_config(Xml_node start_node)
/*
* Apply change to '_config_rom_service'. This will
* potentially result in a change of the "config" ROM route, which
- * may in turn prompt the routing-check below to abandon (restart)
- * the child.
+ * may in turn prompt the routing-check by 'evaluate_dependencies'
+ * to restart the child.
*/
switch (config_update) {
case CONFIG_UNCHANGED: break;
@@ -146,26 +160,39 @@ Sandbox::Child::apply_config(Xml_node start_node)
case CONFIG_VANISHED: _config_rom_service->abandon(); break;
}
- /* validate that the routes of all existing sessions remain intact */
- {
- bool routing_changed = false;
- _child.for_each_session([&] (Session_state const &session) {
- if (!_route_valid(session))
- routing_changed = true; });
-
- if (routing_changed) {
- abandon();
- return MAY_HAVE_SIDE_EFFECTS;
- }
- }
-
if (provided_services_changed)
- return MAY_HAVE_SIDE_EFFECTS;
+ return PROVIDED_SERVICES_CHANGED;
return NO_SIDE_EFFECTS;
}
+void Sandbox::Child::evaluate_dependencies()
+{
+ bool any_route_changed = false;
+ bool any_route_unavailable = false;
+
+ _child.for_each_session([&] (Session_state const &session) {
+
+ switch (_route_valid(session)) {
+ case Route_state::VALID: break;
+ case Route_state::UNAVAILABLE: any_route_unavailable = true; break;
+ case Route_state::MISMATCH: any_route_changed = true; break;
+ }
+ });
+
+ _uncertain_dependencies = false;
+
+ if (any_route_unavailable) {
+ _state = State::STUCK;
+ return;
+ }
+
+ if (any_route_changed || stuck())
+ _schedule_restart();
+}
+
+
Sandbox::Ram_quota Sandbox::Child::_configured_ram_quota() const
{
size_t assigned = 0;
@@ -338,7 +365,7 @@ void Sandbox::Child::report_state(Xml_generator &xml, Report_detail const &detai
if (detail.ids())
xml.attribute("id", _id.value);
- if (!_child.active())
+ if (stuck() || _state == State::RAM_INITIALIZED)
xml.attribute("state", "incomplete");
if (_exited)
@@ -420,11 +447,29 @@ void Sandbox::Child::init(Pd_session &session, Pd_session_capability cap)
size_t const initial_session_costs =
session_alloc_batch_size()*_child.session_factory().session_costs();
- Ram_quota const ram_quota { _resources.effective_ram_quota().value > initial_session_costs
- ? _resources.effective_ram_quota().value - initial_session_costs
- : 0 };
+ Ram_quota ram_quota { _resources.effective_ram_quota().value > initial_session_costs
+ ? _resources.effective_ram_quota().value - initial_session_costs
+ : 0 };
- Cap_quota const cap_quota { _resources.effective_cap_quota().value };
+ Ram_quota avail_ram = _ram_limit_accessor.resource_limit(Ram_quota());
+
+ avail_ram = Genode::Child::effective_quota(avail_ram);
+
+ if (ram_quota.value > avail_ram.value) {
+ warning(name(), ": configured RAM exceeds available RAM, proceed with ", avail_ram);
+ ram_quota = avail_ram;
+ }
+
+ Cap_quota cap_quota { _resources.effective_cap_quota().value };
+
+ Cap_quota avail_caps = _cap_limit_accessor.resource_limit(avail_caps);
+
+ avail_caps = Genode::Child::effective_quota(avail_caps);
+
+ if (cap_quota.value > avail_caps.value) {
+ warning(name(), ": configured caps exceed available caps, proceed with ", avail_caps);
+ cap_quota = avail_caps;
+ }
try { _env.pd().transfer_quota(cap, cap_quota); }
catch (Out_of_caps) {
@@ -718,8 +763,6 @@ Sandbox::Child::Child(Env &env,
Default_route_accessor &default_route_accessor,
Default_caps_accessor &default_caps_accessor,
Name_registry &name_registry,
- Ram_quota ram_limit,
- Cap_quota cap_limit,
Ram_limit_accessor &ram_limit_accessor,
Cap_limit_accessor &cap_limit_accessor,
Prio_levels prio_levels,
@@ -739,8 +782,7 @@ Sandbox::Child::Child(Env &env,
_name_registry(name_registry),
_heartbeat_enabled(start_node.has_sub_node("heartbeat")),
_resources(_resources_from_start_node(start_node, prio_levels, affinity_space,
- default_caps_accessor.default_caps(), cap_limit)),
- _resources_clamped_to_limit((_clamp_resources(ram_limit, cap_limit), true)),
+ default_caps_accessor.default_caps())),
_parent_services(parent_services),
_child_services(child_services),
_local_services(local_services),
diff --git a/repos/os/src/lib/sandbox/child.h b/repos/os/src/lib/sandbox/child.h
index e0385616f6..11fca7ca63 100644
--- a/repos/os/src/lib/sandbox/child.h
+++ b/repos/os/src/lib/sandbox/child.h
@@ -78,10 +78,34 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Id const _id;
- enum State { STATE_INITIAL, STATE_RAM_INITIALIZED, STATE_ALIVE,
- STATE_ABANDONED };
+ enum class State {
- State _state = STATE_INITIAL;
+ /*
+ * 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;
@@ -94,6 +118,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
*/
Version _version { _start_node->xml().attribute_value("version", Version()) };
+ bool _uncertain_dependencies = false;
+
/*
* True if the binary is loaded with ld.lib.so
*/
@@ -117,7 +143,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
if (name.valid())
return name;
- warning("missint 'name' attribute in '' entry");
+ warning("missing 'name' attribute in '' entry");
throw Missing_name_attribute();
}
@@ -151,7 +177,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
bool _heartbeat_expected() const
{
/* don't expect heartbeats from a child that is not yet complete */
- return _heartbeat_enabled && (_state == STATE_ALIVE);
+ return _heartbeat_enabled && (_state == State::ALIVE);
}
/**
@@ -193,9 +219,10 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
}
};
+ static
Resources _resources_from_start_node(Xml_node start_node, Prio_levels prio_levels,
Affinity::Space const &affinity_space,
- Cap_quota default_cap_quota, Cap_quota)
+ Cap_quota default_cap_quota)
{
size_t cpu_quota_pc = 0;
Number_of_bytes ram_bytes = 0;
@@ -208,7 +235,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Name const name = rsc.attribute_value("name", Name());
if (name == "RAM") {
- ram_bytes = rsc.attribute_value("quantum", ram_bytes);
+ ram_bytes = rsc.attribute_value("quantum", ram_bytes);
}
if (name == "CPU") {
@@ -231,29 +258,9 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Resources _resources;
- /**
- * Print diagnostic information on misconfiguration
- */
- void _clamp_resources(Ram_quota ram_limit, Cap_quota cap_limit)
- {
- if (_resources.assigned_ram_quota.value > ram_limit.value) {
- warning(name(), " assigned RAM (", _resources.assigned_ram_quota, ") "
- "exceeds available RAM (", ram_limit, ")");
- _resources.assigned_ram_quota = ram_limit;
- }
-
- if (_resources.assigned_cap_quota.value > cap_limit.value) {
- warning(name(), " assigned caps (", _resources.assigned_cap_quota, ") "
- "exceed available caps (", cap_limit, ")");
- _resources.assigned_cap_quota = cap_limit;
- }
- }
-
Ram_quota _configured_ram_quota() const;
Cap_quota _configured_cap_quota() const;
- bool const _resources_clamped_to_limit;
-
using Local_service = Genode::Sandbox::Local_service_base;
Registry &_parent_services;
@@ -375,6 +382,8 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
_session_requester.trigger_update();
}
+ enum class Route_state { VALID, MISMATCH, UNAVAILABLE };
+
/**
* Return true if the policy results in the current route of the session
*
@@ -382,7 +391,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
* client session of a child, i.e., to determine whether the child must
* be restarted.
*/
- bool _route_valid(Session_state const &session)
+ Route_state _route_valid(Session_state const &session)
{
try {
Route const route =
@@ -390,10 +399,12 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
session.client_label(),
session.diag());
- return (session.service() == route.service)
- && (route.label == session.label());
+ bool const valid = (session.service() == route.service)
+ && (route.label == session.label());
+
+ return valid ? Route_state::VALID : Route_state::MISMATCH;
}
- catch (Service_denied) { return false; }
+ catch (Service_denied) { return Route_state::UNAVAILABLE; }
}
static Xml_node _provides_sub_node(Xml_node start_node)
@@ -421,7 +432,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
service.name() == node.attribute_value("name", Service::Name()))
exists = true; });
- return exists && !abandoned();
+ return exists && !abandoned() && !restart_scheduled();
}
void _add_service(Xml_node service)
@@ -454,7 +465,7 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
*/
bool _pd_alive() const
{
- return !abandoned() && !_exited;
+ return !abandoned() && !restart_scheduled() && !_exited;
}
void _destroy_services();
@@ -477,6 +488,19 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
} _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:
/**
@@ -505,8 +529,6 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Default_route_accessor &default_route_accessor,
Default_caps_accessor &default_caps_accessor,
Name_registry &name_registry,
- Ram_quota ram_limit,
- Cap_quota cap_limit,
Ram_limit_accessor &ram_limit_accessor,
Cap_limit_accessor &cap_limit_accessor,
Prio_levels prio_levels,
@@ -527,50 +549,55 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
Ram_quota ram_quota() const { return _resources.assigned_ram_quota; }
Cap_quota cap_quota() const { return _resources.assigned_cap_quota; }
- void initiate_env_pd_session()
+ void try_start()
{
- if (_state == STATE_INITIAL) {
+ if (_state == State::INITIAL) {
_child.initiate_env_pd_session();
- _state = STATE_RAM_INITIALIZED;
+ _state = State::RAM_INITIALIZED;
}
- }
- void initiate_env_sessions()
- {
- if (_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();
- /* check for completeness of the child's environment */
- if (_verbose.enabled())
- _child.for_each_session([&] (Session_state const &session) {
- if (!session.alive())
- warning(name(), ": incomplete environment ",
- session.service().name(), " session "
- "(", session.label(), ")"); });
-
- _state = STATE_ALIVE;
+ 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;
-
- _child_services.for_each([&] (Routed_service &service) {
- if (service.has_id_space(_session_requester.id_space()))
- service.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 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 { MAY_HAVE_SIDE_EFFECTS, NO_SIDE_EFFECTS };
+ enum Apply_config_result { PROVIDED_SERVICES_CHANGED, NO_SIDE_EFFECTS };
/**
* Apply new configuration to child
@@ -580,6 +607,15 @@ class Sandbox::Child : Child_policy, Routed_service::Wakeup
*/
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 &);
diff --git a/repos/os/src/lib/sandbox/child_registry.h b/repos/os/src/lib/sandbox/child_registry.h
index 9b0cbfc03c..b13e7527fa 100644
--- a/repos/os/src/lib/sandbox/child_registry.h
+++ b/repos/os/src/lib/sandbox/child_registry.h
@@ -29,30 +29,8 @@ class Sandbox::Child_registry : public Name_registry, Child_list
List _aliases { };
- bool _unique(const char *name) const
- {
- /* check for name clash with an existing child */
- List_element const *curr = first();
- for (; curr; curr = curr->next())
- if (curr->object()->has_name(name))
- return false;
-
- /* check for name clash with an existing alias */
- for (Alias const *a = _aliases.first(); a; a = a->next()) {
- if (Alias::Name(name) == a->name)
- return false;
- }
-
- return true;
- }
-
public:
- /**
- * Exception type
- */
- class Alias_name_is_not_unique { };
-
/**
* Register child
*/
@@ -74,10 +52,6 @@ class Sandbox::Child_registry : public Name_registry, Child_list
*/
void insert_alias(Alias *alias)
{
- if (!_unique(alias->name.string())) {
- error("alias name ", alias->name, " is not unique");
- throw Alias_name_is_not_unique();
- }
_aliases.insert(alias);
}
@@ -89,22 +63,6 @@ class Sandbox::Child_registry : public Name_registry, Child_list
_aliases.remove(alias);
}
- /**
- * Return any of the registered children, or 0 if no child exists
- */
- Child *any()
- {
- return first() ? first()->object() : 0;
- }
-
- /**
- * Return any of the registered aliases, or 0 if no alias exists
- */
- Alias *any_alias()
- {
- return _aliases.first() ? _aliases.first() : 0;
- }
-
template
void for_each_child(FN const &fn) const
{
@@ -127,7 +85,6 @@ class Sandbox::Child_registry : public Name_registry, Child_list
{
for_each_child([&] (Child &child) { child.report_state(xml, detail); });
- /* check for name clash with an existing alias */
for (Alias const *a = _aliases.first(); a; a = a->next()) {
xml.node("alias", [&] () {
xml.attribute("name", a->name);
diff --git a/repos/os/src/lib/sandbox/config_model.cc b/repos/os/src/lib/sandbox/config_model.cc
new file mode 100644
index 0000000000..0c48d2e977
--- /dev/null
+++ b/repos/os/src/lib/sandbox/config_model.cc
@@ -0,0 +1,402 @@
+/*
+ * \brief Internal model of the XML configuration
+ * \author Norman Feske
+ * \date 2021-04-02
+ */
+
+/*
+ * 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.
+ */
+
+#include
+
+using namespace Sandbox;
+
+
+struct Config_model::Node : Noncopyable, Interface, private List_model::Element
+{
+ friend class List_model;
+ friend class List;
+
+ virtual bool matches(Xml_node const &) const = 0;
+
+ virtual void update(Xml_node const &) = 0;
+
+ virtual void apply_child_restart(Xml_node const &) { /* only implemented by 'Start_node' */ }
+
+ virtual void trigger_start_child() { /* only implemented by 'Start_node' */ }
+};
+
+
+struct Config_model::Parent_provides_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("parent-provides");
+ }
+
+ Parent_provides_model _model;
+
+ Parent_provides_node(Allocator &alloc, Verbose const &verbose,
+ Parent_provides_model::Factory &factory)
+ :
+ _model(alloc, verbose, factory)
+ { }
+
+ bool matches(Xml_node const &xml) const override { return type_matches(xml); }
+
+ void update(Xml_node const &xml) override { _model.update_from_xml(xml); }
+};
+
+
+struct Config_model::Default_route_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("default-route");
+ }
+
+ Allocator &_alloc;
+ Constructible &_default_route;
+
+ Default_route_node(Allocator &alloc, Constructible &default_route)
+ : _alloc(alloc), _default_route(default_route) { }
+
+ ~Default_route_node() { _default_route.destruct(); }
+
+ bool matches(Xml_node const &xml) const override { return type_matches(xml); }
+
+ void update(Xml_node const &xml) override
+ {
+ if (!_default_route.constructed() || _default_route->xml().differs_from(xml))
+ _default_route.construct(_alloc, xml);
+ }
+};
+
+
+struct Config_model::Default_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("default");
+ }
+
+ Cap_quota &_default_caps;
+
+ Default_node(Cap_quota &default_caps) : _default_caps(default_caps) { }
+
+ bool matches(Xml_node const &xml) const override { return type_matches(xml); }
+
+ void update(Xml_node const &xml) override
+ {
+ _default_caps = Cap_quota { xml.attribute_value("caps", 0UL) };
+ }
+};
+
+
+struct Config_model::Affinity_space_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("affinity-space");
+ }
+
+ Constructible &_affinity_space;
+
+ Affinity_space_node(Constructible &affinity_space)
+ : _affinity_space(affinity_space) { }
+
+ ~Affinity_space_node() { _affinity_space.destruct(); }
+
+ bool matches(Xml_node const &xml) const override { return type_matches(xml); }
+
+ void update(Xml_node const &xml) override
+ {
+ _affinity_space.construct(xml.attribute_value("width", 1u),
+ xml.attribute_value("height", 1u));
+ }
+};
+
+
+struct Config_model::Start_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("start") || xml.has_type("alias");
+ }
+
+ Start_model _model;
+
+ /*
+ * \throw Start_model::Factory::Creation_failed
+ */
+ Start_node(Start_model::Factory &factory, Xml_node const &xml)
+ : _model(factory, xml) { }
+
+ bool matches(Xml_node const &xml) const override
+ {
+ return type_matches(xml) && _model.matches(xml);
+ }
+
+ void update(Xml_node const &xml) override
+ {
+ _model.update_from_xml(xml);
+ }
+
+ void apply_child_restart(Xml_node const &xml) override
+ {
+ _model.apply_child_restart(xml);
+ }
+
+ void trigger_start_child() override
+ {
+ _model.trigger_start_child();
+ }
+};
+
+
+struct Config_model::Report_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("report");
+ }
+
+ Version const &_version;
+ State_reporter &_state_reporter;
+
+ Report_node(Version const &version, State_reporter &state_reporter)
+ : _version(version), _state_reporter(state_reporter) { }
+
+ ~Report_node()
+ {
+ _state_reporter.apply_config(_version, Xml_node(""));
+ }
+
+ bool matches(Xml_node const &xml) const override { return type_matches(xml); }
+
+ void update(Xml_node const &xml) override
+ {
+ _state_reporter.apply_config(_version, xml);
+ }
+};
+
+
+struct Config_model::Resource_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("resource");
+ }
+
+ enum class Category { RAM, CAP } const _category;
+
+ class Unknown_resource_name : Exception { };
+
+ static Category _category_from_xml(Xml_node const &xml)
+ {
+ typedef String<16> Name;
+ Name const name = xml.attribute_value("name", Name());
+
+ if (name == "RAM") return Category::RAM;
+ if (name == "CAP") return Category::CAP;
+
+ throw Unknown_resource_name();
+ }
+
+ Preservation &_keep;
+
+ /*
+ * \throw Unknown_resource_name
+ */
+ Resource_node(Preservation &keep, Xml_node xml)
+ :
+ _category(_category_from_xml(xml)), _keep(keep)
+ { }
+
+ ~Resource_node()
+ {
+ switch (_category) {
+ case Category::RAM: _keep.ram = Preservation::default_ram(); break;
+ case Category::CAP: _keep.caps = Preservation::default_caps(); break;
+ }
+ }
+
+ bool matches(Xml_node const &xml) const override
+ {
+ return type_matches(xml) && _category == _category_from_xml(xml);
+ }
+
+ void update(Xml_node const &xml) override
+ {
+ switch (_category) {
+
+ case Category::RAM:
+ {
+ Number_of_bytes keep { Preservation::default_ram().value };
+ _keep.ram = { xml.attribute_value("preserve", keep) };
+ break;
+ }
+
+ case Category::CAP:
+ {
+ size_t keep = Preservation::default_caps().value;
+ _keep.caps = { xml.attribute_value("preserve", keep) };
+ break;
+ }
+ }
+ }
+};
+
+
+struct Config_model::Heartbeat_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("heartbeat");
+ }
+
+ Heartbeat &_heartbeat;
+
+ Heartbeat_node(Heartbeat &heartbeat) : _heartbeat(heartbeat) { }
+
+ ~Heartbeat_node()
+ {
+ _heartbeat.disable();
+ }
+
+ bool matches(Xml_node const &xml) const override { return type_matches(xml); }
+
+ void update(Xml_node const &xml) override
+ {
+ _heartbeat.apply_config(xml);
+ }
+};
+
+
+struct Config_model::Service_node : Node
+{
+ static bool type_matches(Xml_node const &xml)
+ {
+ return xml.has_type("service");
+ }
+
+ Service_model::Factory &_factory;
+
+ Service_model &_model;
+
+ Service_node(Service_model::Factory &factory, Xml_node const &xml)
+ : _factory(factory), _model(factory.create_service(xml)) { }
+
+ ~Service_node() { _factory.destroy_service(_model); }
+
+ bool matches(Xml_node const &xml) const override
+ {
+ return type_matches(xml) && _model.matches(xml);
+ }
+
+ void update(Xml_node const &xml) override { _model.update_from_xml(xml); }
+};
+
+
+void Config_model::update_from_xml(Xml_node const &xml,
+ Allocator &alloc,
+ Reconstructible &verbose,
+ Version &version,
+ Preservation &preservation,
+ Constructible &default_route,
+ Cap_quota &default_caps,
+ Prio_levels &prio_levels,
+ Constructible &affinity_space,
+ Start_model::Factory &child_factory,
+ Parent_provides_model::Factory &parent_service_factory,
+ Service_model::Factory &service_factory,
+ State_reporter &state_reporter,
+ Heartbeat &heartbeat)
+{
+ /* config version to be reflected in state reports */
+ version = xml.attribute_value("version", Version());
+
+ preservation.reset();
+
+ prio_levels = ::Sandbox::prio_levels_from_xml(xml);
+
+ affinity_space.destruct();
+
+ verbose.construct(xml);
+
+ class Unknown_element_type : Exception { };
+
+ auto destroy = [&] (Node &node) { Genode::destroy(alloc, &node); };
+
+ auto create = [&] (Xml_node const &xml) -> Node &
+ {
+ if (Parent_provides_node::type_matches(xml))
+ return *new (alloc)
+ Parent_provides_node(alloc, *verbose, parent_service_factory);
+
+ if (Default_route_node::type_matches(xml))
+ return *new (alloc) Default_route_node(alloc, default_route);
+
+ if (Default_node::type_matches(xml))
+ return *new (alloc) Default_node(default_caps);
+
+ if (Start_node::type_matches(xml))
+ return *new (alloc) Start_node(child_factory, xml);
+
+ if (Affinity_space_node::type_matches(xml))
+ return *new (alloc) Affinity_space_node(affinity_space);
+
+ if (Report_node::type_matches(xml))
+ return *new (alloc) Report_node(version, state_reporter);
+
+ if (Resource_node::type_matches(xml))
+ return *new (alloc) Resource_node(preservation, xml);
+
+ if (Heartbeat_node::type_matches(xml))
+ return *new (alloc) Heartbeat_node(heartbeat);
+
+ if (Service_node::type_matches(xml))
+ return *new (alloc) Service_node(service_factory, xml);
+
+ error("unknown config element type <", xml.type(), ">");
+ throw Unknown_element_type();
+ };
+
+ auto update = [&] (Node &node, Xml_node const &xml) { node.update(xml); };
+
+ try {
+ update_list_model_from_xml(_model, xml, create, destroy, update);
+ }
+ catch (Unknown_element_type) {
+ error("unable to apply complete configuration"); }
+ catch (Start_model::Factory::Creation_failed) {
+ error("child creation failed"); }
+}
+
+
+void Config_model::apply_children_restart(Xml_node const &xml)
+{
+ class Unexpected : Exception { };
+ auto destroy = [&] (Node &) { };
+ auto create = [&] (Xml_node const &) -> Node & { throw Unexpected(); };
+ auto update = [&] (Node &node, Xml_node const &xml)
+ {
+ node.apply_child_restart(xml);
+ };
+
+ try {
+ update_list_model_from_xml(_model, xml, create, destroy, update);
+ }
+ catch (...) { };
+}
+
+
+void Config_model::trigger_start_children()
+{
+ _model.for_each([&] (Node &node) {
+ node.trigger_start_child(); });
+}
diff --git a/repos/os/src/lib/sandbox/config_model.h b/repos/os/src/lib/sandbox/config_model.h
new file mode 100644
index 0000000000..ea19a5c165
--- /dev/null
+++ b/repos/os/src/lib/sandbox/config_model.h
@@ -0,0 +1,283 @@
+/*
+ * \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_
+
+/* local includes */
+#include
+#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();
+ }
+
+ 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)
+ { }
+
+ 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
index 64ab915ca6..4f445508d0 100644
--- a/repos/os/src/lib/sandbox/heartbeat.h
+++ b/repos/os/src/lib/sandbox/heartbeat.h
@@ -64,23 +64,23 @@ class Sandbox::Heartbeat : Noncopyable
_timer_handler(_env.ep(), *this, &Heartbeat::_handle_timer)
{ }
- void apply_config(Xml_node config)
+ void disable()
{
- bool const enabled = config.has_sub_node("heartbeat");
+ _timer.destruct();
+ _rate_ms = 0;
+ }
- _timer.conditional(enabled, _env);
-
- if (!enabled) {
- _rate_ms = 0;
- return;
+ void apply_config(Xml_node heartbeat)
+ {
+ if (!_timer.constructed()) {
+ _timer.construct(_env);
+ _timer->sigh(_timer_handler);
}
- unsigned const rate_ms =
- config.sub_node("heartbeat").attribute_value("rate_ms", 1000UL);
+ unsigned const rate_ms = heartbeat.attribute_value("rate_ms", 1000UL);
if (rate_ms != _rate_ms) {
_rate_ms = rate_ms;
- _timer->sigh(_timer_handler);
_timer->trigger_periodic(_rate_ms*1000);
}
}
diff --git a/repos/os/src/lib/sandbox/library.cc b/repos/os/src/lib/sandbox/library.cc
index 5023c06ece..21e1245f08 100644
--- a/repos/os/src/lib/sandbox/library.cc
+++ b/repos/os/src/lib/sandbox/library.cc
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2010-2020 Genode Labs GmbH
+ * Copyright (C) 2010-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.
@@ -20,12 +20,15 @@
#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::Cap_limit_accessor,
+ ::Sandbox::Start_model::Factory,
+ ::Sandbox::Parent_provides_model::Factory
{
using Routed_service = ::Sandbox::Routed_service;
using Parent_service = ::Sandbox::Parent_service;
@@ -41,6 +44,9 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
using Prio_levels = ::Sandbox::Prio_levels;
using Ram_info = ::Sandbox::Ram_info;
using Cap_info = ::Sandbox::Cap_info;
+ using Config_model = ::Sandbox::Config_model;
+ using Start_model = ::Sandbox::Start_model;
+ using Preservation = ::Sandbox::Preservation;
Env &_env;
Heap &_heap;
@@ -50,77 +56,82 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
Registry &_local_services;
Child_registry _children { };
- Reconstructible _verbose { };
+ /*
+ * 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 { };
- Constructible _default_route { };
-
- Cap_quota _default_caps { 0 };
-
- unsigned _child_cnt = 0;
-
- static Ram_quota _preserved_ram_from_config(Xml_node config)
+ Affinity::Space _effective_affinity_space() const
{
- 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 };
+ return _affinity_space.constructed() ? *_affinity_space
+ : Affinity::Space { 1, 1 };
}
- Ram_quota _preserved_ram { 0 };
- Cap_quota _preserved_caps { 0 };
+ 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;
Ram_quota _avail_ram() const
{
Ram_quota avail_ram = _env.pd().avail_ram();
- if (_preserved_ram.value > avail_ram.value) {
+ 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 - _preserved_ram.value };
- }
-
- static Cap_quota _preserved_caps_from_config(Xml_node config)
- {
- size_t preserve = 20;
-
- config.for_each_sub_node("resource", [&] (Xml_node node) {
- if (node.attribute_value("name", String<16>()) == "CAP")
- preserve = node.attribute_value("preserve", preserve); });
-
- return Cap_quota { preserve };
+ return Ram_quota { avail_ram.value - _preservation.ram.value };
}
Cap_quota _avail_caps() const
{
Cap_quota avail_caps { _env.pd().avail_caps().value };
- if (_preserved_caps.value > 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 - _preserved_caps.value };
+ 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(); }
+ 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(); }
- void _handle_resource_avail() { }
-
+ /**
+ * State_reporter::Producer interface
+ */
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const override
{
if (detail.init_ram())
@@ -133,6 +144,9 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
_children.report_state(xml, detail);
}
+ /**
+ * State_reporter::Producer interface
+ */
Child::Sample_state_result sample_children_state() override
{
return _children.sample_state();
@@ -152,18 +166,57 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
*/
Cap_quota default_caps() override { return _default_caps; }
- State_reporter _state_reporter;
-
- Heartbeat _heartbeat { _env, _children, _state_reporter };
-
void _update_aliases_from_config(Xml_node const &);
void _update_parent_services_from_config(Xml_node const &);
- void _abandon_obsolete_children(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)
:
@@ -180,52 +233,6 @@ struct Genode::Sandbox::Library : ::Sandbox::State_reporter::Producer,
};
-void Genode::Sandbox::Library::_update_parent_services_from_config(Xml_node const &config)
-{
- Xml_node const node = config.has_sub_node("parent-provides")
- ? config.sub_node("parent-provides")
- : Xml_node("");
-
- /* remove services that are no longer present in config */
- _parent_services.for_each([&] (Parent_service &service) {
-
- Service::Name const name = service.name();
-
- bool obsolete = true;
- node.for_each_sub_node("service", [&] (Xml_node service) {
- if (name == service.attribute_value("name", Service::Name())) {
- obsolete = false; }});
-
- if (obsolete)
- service.abandon();
- });
-
- /* used to prepend the list of new parent services with title */
- bool first_log = true;
-
- /* register new services */
- node.for_each_sub_node("service", [&] (Xml_node service) {
-
- Service::Name const name = service.attribute_value("name", Service::Name());
-
- bool registered = false;
- _parent_services.for_each([&] (Parent_service const &service) {
- if (service.name() == name)
- registered = true; });
-
- if (!registered) {
- new (_heap) ::Sandbox::Parent_service(_parent_services, _env, name);
- if (_verbose->enabled()) {
- if (first_log)
- log("parent provides");
- log(" service \"", name, "\"");
- first_log = false;
- }
- }
- });
-}
-
-
void Genode::Sandbox::Library::_destroy_abandoned_parent_services()
{
_parent_services.for_each([&] (Parent_service &service) {
@@ -234,109 +241,8 @@ void Genode::Sandbox::Library::_destroy_abandoned_parent_services()
}
-void Genode::Sandbox::Library::_update_aliases_from_config(Xml_node const &config)
+void Genode::Sandbox::Library::_destroy_abandoned_children()
{
- /* remove all known aliases */
- while (_children.any_alias()) {
- ::Sandbox::Alias *alias = _children.any_alias();
- _children.remove_alias(alias);
- destroy(_heap, alias);
- }
-
- /* create aliases */
- config.for_each_sub_node("alias", [&] (Xml_node alias_node) {
-
- try {
- _children.insert_alias(new (_heap) Alias(alias_node)); }
- catch (Alias::Name_is_missing) {
- warning("missing 'name' attribute in '' entry"); }
- catch (Alias::Child_is_missing) {
- warning("missing 'child' attribute in '' entry"); }
- });
-}
-
-
-void Genode::Sandbox::Library::_abandon_obsolete_children(Xml_node const &config)
-{
- _children.for_each_child([&] (Child &child) {
-
- bool obsolete = true;
- config.for_each_sub_node("start", [&] (Xml_node node) {
- if (child.has_name (node.attribute_value("name", Child_policy::Name()))
- && child.has_version(node.attribute_value("version", Child::Version())))
- obsolete = false; });
-
- if (obsolete)
- child.abandon();
- });
-}
-
-
-void Genode::Sandbox::Library::_update_children_config(Xml_node const &config)
-{
- for (;;) {
-
- /*
- * Children are abandoned if any of their client sessions can no longer
- * be routed or result in a different route. As each child may be a
- * service, an avalanche effect may occur. It stops if no update causes
- * a potential side effect in one iteration over all chilren.
- */
- bool side_effects = false;
-
- config.for_each_sub_node("start", [&] (Xml_node node) {
-
- Child_policy::Name const start_node_name =
- node.attribute_value("name", Child_policy::Name());
-
- _children.for_each_child([&] (Child &child) {
- if (!child.abandoned() && child.name() == start_node_name) {
- switch (child.apply_config(node)) {
- case Child::NO_SIDE_EFFECTS: break;
- case Child::MAY_HAVE_SIDE_EFFECTS: side_effects = true; break;
- };
- }
- });
- });
-
- if (!side_effects)
- break;
- }
-}
-
-
-void Genode::Sandbox::Library::apply_config(Xml_node const &config)
-{
- bool update_state_report = false;
-
- _preserved_ram = _preserved_ram_from_config(config);
- _preserved_caps = _preserved_caps_from_config(config);
-
- _verbose.construct(config);
- _state_reporter.apply_config(config);
- _heartbeat.apply_config(config);
-
- /* determine default route for resolving service requests */
- try {
- _default_route.construct(_heap, config.sub_node("default-route")); }
- catch (...) { }
-
- _default_caps = Cap_quota { 0 };
- try {
- _default_caps = Cap_quota { config.sub_node("default")
- .attribute_value("caps", 0UL) }; }
- catch (...) { }
-
- Prio_levels const prio_levels = ::Sandbox::prio_levels_from_xml(config);
- Affinity::Space const affinity_space = ::Sandbox::affinity_space_from_xml(config);
- bool const space_defined = config.has_sub_node("affinity-space");
-
- _update_aliases_from_config(config);
- _update_parent_services_from_config(config);
- _abandon_obsolete_children(config);
- _update_children_config(config);
-
- /* kill abandoned children */
_children.for_each_child([&] (Child &child) {
if (!child.abandoned())
@@ -345,7 +251,7 @@ void Genode::Sandbox::Library::apply_config(Xml_node const &config)
/* make the child's services unavailable */
child.destroy_services();
child.close_all_sessions();
- update_state_report = true;
+ _state_report_outdated = true;
/* destroy child once all environment sessions are gone */
if (child.env_sessions_closed()) {
@@ -353,123 +259,174 @@ void Genode::Sandbox::Library::apply_config(Xml_node const &config)
destroy(_heap, &child);
}
});
+}
- _destroy_abandoned_parent_services();
- /* initial RAM and caps limit before starting new children */
- Ram_quota const avail_ram = _avail_ram();
- Cap_quota const avail_caps = _avail_caps();
+bool Genode::Sandbox::Library::ready_to_create_child(Start_model::Name const &name,
+ Start_model::Version const &version) const
+{
+ bool exists = false;
- /* variable used to track the RAM and caps taken by new started children */
- Ram_quota used_ram { 0 };
- Cap_quota used_caps { 0 };
+ unsigned num_abandoned = 0;
+
+ _children.for_each_child([&] (Child const &child) {
+ if (child.name() == name && child.has_version(version)) {
+ if (child.abandoned())
+ num_abandoned++;
+ else
+ exists = true;
+ }
+ });
+
+ /* defer child creation if corresponding child already exists */
+ if (exists)
+ return false;
+
+ /* prevent queuing up abandoned children with the same name */
+ if (num_abandoned > 1)
+ return false;
+
+ return true;
+}
+
+
+::Sandbox::Child &Genode::Sandbox::Library::create_child(Xml_node const &start_node)
+{
+ if (!_affinity_space.constructed() && start_node.has_sub_node("affinity"))
+ warning("affinity-space configuration missing, "
+ "but affinity defined for child ",
+ start_node.attribute_value("name", Child_policy::Name()));
- /* create new children */
try {
- config.for_each_sub_node("start", [&] (Xml_node start_node) {
+ Child &child = *new (_heap)
+ Child(_env, _heap, *_verbose,
+ Child::Id { ++_child_cnt }, _state_reporter,
+ start_node, *this, *this, _children,
+ *this, *this, _prio_levels, _effective_affinity_space(),
+ _parent_services, _child_services, _local_services);
+ _children.insert(&child);
- bool exists = false;
+ if (start_node.has_sub_node("provides"))
+ _server_appeared_or_disappeared = true;
- unsigned num_abandoned = 0;
+ _state_report_outdated = true;
- typedef Child_policy::Name Name;
- Name const child_name = start_node.attribute_value("name", Name());
-
- _children.for_each_child([&] (Child const &child) {
- if (child.name() == child_name) {
- if (child.abandoned())
- num_abandoned++;
- else
- exists = true;
- }
- });
-
- /* skip start node if corresponding child already exists */
- if (exists)
- return;
-
- /* prevent queuing up abandoned children with the same name */
- if (num_abandoned > 1)
- return;
-
- if (used_ram.value > avail_ram.value) {
- error("RAM exhausted while starting child: ", child_name);
- return;
- }
-
- if (used_caps.value > avail_caps.value) {
- error("capabilities exhausted while starting child: ", child_name);
- return;
- }
-
- if (!space_defined && start_node.has_sub_node("affinity")) {
- warning("affinity-space configuration missing, "
- "but affinity defined for child: ", child_name);
- }
-
- try {
- Child &child = *new (_heap)
- Child(_env, _heap, *_verbose,
- Child::Id { ++_child_cnt }, _state_reporter,
- start_node, *this, *this, _children,
- Ram_quota { avail_ram.value - used_ram.value },
- Cap_quota { avail_caps.value - used_caps.value },
- *this, *this, prio_levels, affinity_space,
- _parent_services, _child_services, _local_services);
- _children.insert(&child);
-
- update_state_report = true;
-
- /* account for the start XML node buffered in the child */
- size_t const metadata_overhead = start_node.size() + sizeof(Child);
-
- /* track used memory and RAM limit */
- used_ram = Ram_quota { used_ram.value
- + child.ram_quota().value
- + metadata_overhead };
-
- used_caps = Cap_quota { used_caps.value
- + child.cap_quota().value };
- }
- 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"); }
- });
+ return child;
}
- catch (Xml_node::Nonexistent_sub_node) { error("no children to start"); }
- catch (Xml_node::Invalid_syntax) { error("config has invalid syntax"); }
- catch (Child_registry::Alias_name_is_not_unique) { }
+ 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 Genode::Sandbox::Library::update_child(Child &child, Xml_node const &start)
+{
+ if (child.abandoned())
+ return;
+
+ switch (child.apply_config(start)) {
+
+ case Child::NO_SIDE_EFFECTS: break;
+
+ case Child::PROVIDED_SERVICES_CHANGED:
+ _server_appeared_or_disappeared = true;
+ _state_report_outdated = true;
+ break;
+ };
+}
+
+
+void Genode::Sandbox::Library::apply_config(Xml_node const &config)
+{
+ _server_appeared_or_disappeared = false;
+ _state_report_outdated = false;
+
+ _config_model.update_from_xml(config,
+ _heap,
+ _verbose,
+ _version,
+ _preservation,
+ _default_route,
+ _default_caps,
+ _prio_levels,
+ _affinity_space,
+ *this, *this, _server,
+ _state_reporter,
+ _heartbeat);
/*
- * Initiate RAM sessions of all new children
+ * After importing the new configuration, servers may have disappeared
+ * (STATE_ABANDONED) or become new available.
+ *
+ * Re-evaluate the dependencies of the existing children.
+ *
+ * - Stuck children (STATE_STUCK) may become alive.
+ * - Children with broken dependencies may have become stuck.
+ * - Children with changed dependencies need a restart.
+ *
+ * Children are restarted if any of their client sessions can no longer be
+ * routed or result in a different route. As each child may be a service,
+ * an avalanche effect may occur. It stops if no child gets scheduled to be
+ * restarted in one iteration over all children.
*/
- _children.for_each_child([&] (Child &child) {
- if (!child.abandoned())
- child.initiate_env_pd_session(); });
+ while (true) {
- /*
- * Initiate remaining environment sessions of all new children
- */
- _children.for_each_child([&] (Child &child) {
- if (!child.abandoned())
- child.initiate_env_sessions(); });
+ bool any_restart_scheduled = false;
+
+ _children.for_each_child([&] (Child &child) {
+
+ if (child.abandoned())
+ return;
+
+ if (child.restart_scheduled()) {
+ any_restart_scheduled = true;
+ return;
+ }
+
+ if (_server_appeared_or_disappeared || child.uncertain_dependencies())
+ child.evaluate_dependencies();
+
+ if (child.restart_scheduled())
+ any_restart_scheduled = true;
+ });
+
+ /*
+ * Release resources captured by abandoned children before starting
+ * new children. The children must be started in the order of their
+ * start nodes for the assignment of slack RAM.
+ */
+ _destroy_abandoned_parent_services();
+ _destroy_abandoned_children();
+
+ _config_model.trigger_start_children();
+
+ if (any_restart_scheduled)
+ _config_model.apply_children_restart(config);
+
+ if (!any_restart_scheduled)
+ break;
+ }
+
+ _server.apply_updated_policy();
/*
* (Re-)distribute RAM and capability quota among the children, given their
@@ -480,9 +437,7 @@ void Genode::Sandbox::Library::apply_config(Xml_node const &config)
_children.for_each_child([&] (Child &child) { child.apply_downgrade(); });
_children.for_each_child([&] (Child &child) { child.apply_upgrade(); });
- _server.apply_config(config);
-
- if (update_state_report)
+ if (_state_report_outdated)
_state_reporter.trigger_immediate_report_update();
}
diff --git a/repos/os/src/lib/sandbox/server.cc b/repos/os/src/lib/sandbox/server.cc
index 39ea53a5f1..f2fbc3c625 100644
--- a/repos/os/src/lib/sandbox/server.cc
+++ b/repos/os/src/lib/sandbox/server.cc
@@ -21,19 +21,21 @@
/******************************
** Sandbox::Server::Service **
- **********...*****************/
+ ******************************/
-struct Sandbox::Server::Service
+struct Sandbox::Server::Service : Service_model
{
+ typedef Genode::Service::Name Name;
+
+ Name const _name;
+
Registry::Element _registry_element;
- Buffered_xml _service_node;
-
- typedef Genode::Service::Name Name;
+ Allocator &_alloc;
Registry &_child_services;
- Name const _name { _service_node.xml().attribute_value("name", Name()) };
+ Constructible _service_node { };
/**
* Constructor
@@ -45,11 +47,28 @@ struct Sandbox::Server::Service
Xml_node service_node,
Registry &child_services)
:
+ _name(service_node.attribute_value("name", Name())),
_registry_element(services, *this),
- _service_node(alloc, service_node),
+ _alloc(alloc),
_child_services(child_services)
{ }
+ /**
+ * Service_model interface
+ */
+ void update_from_xml(Xml_node const &service_node) override
+ {
+ _service_node.construct(_alloc, service_node);
+ }
+
+ /**
+ * Service_model interface
+ */
+ bool matches(Xml_node const &service_node) override
+ {
+ return _name == service_node.attribute_value("name", Name());
+ }
+
/**
* Determine route to child service for a given label according
* to the node policy
@@ -65,8 +84,11 @@ struct Sandbox::Server::Service
Sandbox::Server::Route
Sandbox::Server::Service::resolve_session_request(Session_label const &label)
{
+ if (!_service_node.constructed())
+ throw Service_denied();
+
try {
- Session_policy policy(label, _service_node.xml());
+ Session_policy policy(label, _service_node->xml());
if (!policy.has_sub_node("child"))
throw Service_denied();
@@ -365,13 +387,20 @@ void Sandbox::Server::_handle_session_requests()
}
-void Sandbox::Server::apply_config(Xml_node config)
+Sandbox::Service_model &Sandbox::Server::create_service(Xml_node const &node)
{
- _services.for_each([&] (Service &service) { destroy(_alloc, &service); });
+ return *new (_alloc) Service(_services, _alloc, node, _child_services);
+}
- config.for_each_sub_node("service", [&] (Xml_node node) {
- new (_alloc) Service(_services, _alloc, node, _child_services); });
+void Sandbox::Server::destroy_service(Service_model &service)
+{
+ destroy(_alloc, &service);
+}
+
+
+void Sandbox::Server::apply_updated_policy()
+{
/*
* Construct mechanics for responding to our parent's session requests
* on demand.
diff --git a/repos/os/src/lib/sandbox/server.h b/repos/os/src/lib/sandbox/server.h
index 44777c7cee..ce231ce28f 100644
--- a/repos/os/src/lib/sandbox/server.h
+++ b/repos/os/src/lib/sandbox/server.h
@@ -19,15 +19,17 @@
#include
/* local includes */
-#include "types.h"
-#include "service.h"
-#include "state_reporter.h"
+#include
+#include
+#include
+#include
namespace Sandbox { class Server; }
class Sandbox::Server : Session_state::Ready_callback,
- Session_state::Closed_callback
+ Session_state::Closed_callback,
+ public Service_model::Factory
{
private:
@@ -113,7 +115,17 @@ class Sandbox::Server : Session_state::Ready_callback,
_report_update_trigger(report_update_trigger)
{ }
- void apply_config(Xml_node);
+ 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/state_reporter.h b/repos/os/src/lib/sandbox/state_reporter.h
index abb65e0fd6..bc0d6d5ee1 100644
--- a/repos/os/src/lib/sandbox/state_reporter.h
+++ b/repos/os/src/lib/sandbox/state_reporter.h
@@ -29,6 +29,8 @@ class Sandbox::State_reporter : public Report_update_trigger
{
public:
+ typedef String<64> Version;
+
struct Producer : Interface
{
virtual void produce_state_report(Xml_generator &xml,
@@ -53,7 +55,6 @@ class Sandbox::State_reporter : public Report_update_trigger
uint64_t _report_period_ms = 0;
/* version string from config, to be reflected in the report */
- typedef String<64> Version;
Version _version { };
Constructible _timer { };
@@ -111,22 +112,18 @@ class Sandbox::State_reporter : public Report_update_trigger
_producer.produce_state_report(xml, *_report_detail);
}
- void apply_config(Xml_node config)
+ void apply_config(Version const &version, Xml_node const &report)
{
- try {
- Xml_node report = config.sub_node("report");
-
+ if (report.type() == "report") {
_report_detail.construct(report);
_report_delay_ms = report.attribute_value("delay_ms", 100UL);
- }
- catch (Xml_node::Nonexistent_sub_node) {
+ } else {
_report_detail.construct();
_report_delay_ms = 0;
}
bool trigger_update = false;
- Version const version = config.attribute_value("version", Version());
if (version != _version) {
_version = version;
trigger_update = true;
diff --git a/repos/os/src/lib/sandbox/types.h b/repos/os/src/lib/sandbox/types.h
index 7e54780b6f..5d8ee16c56 100644
--- a/repos/os/src/lib/sandbox/types.h
+++ b/repos/os/src/lib/sandbox/types.h
@@ -14,9 +14,9 @@
#ifndef _LIB__SANDBOX__TYPES_H_
#define _LIB__SANDBOX__TYPES_H_
-#include
#include
-#include
+#include
+#include
namespace Sandbox {
@@ -72,6 +72,22 @@ namespace Sandbox {
.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/update_list_model.h b/repos/os/src/lib/sandbox/update_list_model.h
new file mode 100644
index 0000000000..2703986826
--- /dev/null
+++ b/repos/os/src/lib/sandbox/update_list_model.h
@@ -0,0 +1,69 @@
+/*
+ * \brief Convenience wrapper around 'List_model'
+ * \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 _UPDATE_LIST_MODEL_H_
+#define _UPDATE_LIST_MODEL_H_
+
+/* Genode includes */
+#include
+
+/* local includes */
+#include
+
+namespace Sandbox {
+
+ template
+ static inline void update_list_model_from_xml(List_model &,
+ Xml_node const &,
+ CREATE_FN const &,
+ DESTROY_FN const &,
+ UPDATE_FN const &);
+}
+
+
+template
+void Sandbox::update_list_model_from_xml(List_model &model,
+ Xml_node const &xml,
+ CREATE_FN const &create,
+ DESTROY_FN const &destroy,
+ UPDATE_FN const &update)
+{
+ struct Model_update_policy : List_model::Update_policy
+ {
+ CREATE_FN const &_create_fn;
+ DESTROY_FN const &_destroy_fn;
+ UPDATE_FN const &_update_fn;
+
+ Model_update_policy(CREATE_FN const &create_fn,
+ DESTROY_FN const &destroy_fn,
+ UPDATE_FN const &update_fn)
+ :
+ _create_fn(create_fn), _destroy_fn(destroy_fn), _update_fn(update_fn)
+ { }
+
+ void destroy_element(NODE &node) { _destroy_fn(node); }
+
+ NODE &create_element(Xml_node xml) { return _create_fn(xml); }
+
+ void update_element(NODE &node, Xml_node xml) { _update_fn(node, xml); }
+
+ static bool element_matches_xml_node(NODE const &node, Xml_node xml)
+ {
+ return node.matches(xml);
+ }
+ } policy(create, destroy, update);
+
+ model.update_from_xml(policy, xml);
+}
+
+#endif /* _UPDATE_LIST_MODEL_H_ */
diff --git a/repos/os/src/lib/sandbox/utils.h b/repos/os/src/lib/sandbox/utils.h
index a21fcca8ea..853cb6da0f 100644
--- a/repos/os/src/lib/sandbox/utils.h
+++ b/repos/os/src/lib/sandbox/utils.h
@@ -124,7 +124,8 @@ namespace Sandbox {
/* count number of services with the specified name */
unsigned cnt = 0;
services.for_each([&] (T const &service) {
- cnt += (service.name() == name); });
+ if (!service.abandoned())
+ cnt += (service.name() == name); });
return cnt > 1;
}
@@ -164,7 +165,7 @@ namespace Sandbox {
/**
* Read priority-levels declaration from config
*/
- inline Prio_levels prio_levels_from_xml(Xml_node config)
+ inline Prio_levels prio_levels_from_xml(Xml_node const &config)
{
long const prio_levels = config.attribute_value("prio_levels", 0UL);
@@ -233,24 +234,6 @@ namespace Sandbox {
}
catch (...) { return Location(0, 0, space.width(), space.height()); }
}
-
-
- /**
- * Read affinity-space parameters from config
- *
- * If no affinity space is declared, construct a space with a single element,
- * width and height being 1. If only one of both dimensions is specified, the
- * other dimension is set to 1.
- */
- inline Affinity::Space affinity_space_from_xml(Xml_node config)
- {
- try {
- Xml_node node = config.sub_node("affinity-space");
- return Affinity::Space(node.attribute_value("width", 1),
- node.attribute_value("height", 1));
- } catch (...) {
- return Affinity::Space(1, 1); }
- }
}
#endif /* _LIB__SANDBOX__UTILS_H_ */
diff --git a/repos/os/src/lib/sandbox/verbose.h b/repos/os/src/lib/sandbox/verbose.h
index db0c7adb2e..ab3e6050a6 100644
--- a/repos/os/src/lib/sandbox/verbose.h
+++ b/repos/os/src/lib/sandbox/verbose.h
@@ -34,7 +34,7 @@ class Sandbox::Verbose : Genode::Noncopyable
Verbose() { }
- Verbose(Genode::Xml_node config)
+ Verbose(Xml_node const &config)
: _enabled(config.attribute_value("verbose", false)) { }
bool enabled() const { return _enabled; }