mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 07:08:18 +00:00
New sandbox library extracted from init component
This patch extracts the child-management functionality from the init component into a new library called "sandbox". The library API is located at 'os/include/os/sandbox.h'. The sandbox API allows for the interaction of the component with the sandboxed children by providing locally implemented services. This mechanism is illustrated by the new test at os/src/test/sandbox. Issue #3601
This commit is contained in:
committed by
Christian Helmuth
parent
f82e7df0ba
commit
78c0e5f6b6
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* \brief Representation of an alias for a child
|
||||
* \author Norman Feske
|
||||
* \date 2010-04-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__ALIAS_H_
|
||||
#define _SRC__INIT__ALIAS_H_
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
|
||||
namespace Init { struct Alias; }
|
||||
|
||||
struct Init::Alias : List<Alias>::Element
|
||||
{
|
||||
typedef String<128> Name;
|
||||
typedef String<128> Child;
|
||||
|
||||
Name name;
|
||||
Child child;
|
||||
|
||||
/**
|
||||
* Exception types
|
||||
*/
|
||||
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()))
|
||||
{
|
||||
|
||||
if (!name.valid()) throw Name_is_missing();
|
||||
if (!child.valid()) throw Child_is_missing();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__ALIAS_H_ */
|
@ -1,767 +0,0 @@
|
||||
/*
|
||||
* \brief Child representation
|
||||
* \author Norman Feske
|
||||
* \date 2010-05-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <vm_session/vm_session.h>
|
||||
|
||||
/* local includes */
|
||||
#include <child.h>
|
||||
|
||||
|
||||
void Init::Child::destroy_services()
|
||||
{
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
if (service.has_id_space(_session_requester.id_space()))
|
||||
destroy(_alloc, &service); });
|
||||
}
|
||||
|
||||
|
||||
Init::Child::Apply_config_result
|
||||
Init::Child::apply_config(Xml_node start_node)
|
||||
{
|
||||
if (_state == STATE_ABANDONED || _exited)
|
||||
return NO_SIDE_EFFECTS;
|
||||
|
||||
/*
|
||||
* If the child's environment is incomplete, restart it to attempt
|
||||
* the re-routing of its environment sessions.
|
||||
*/
|
||||
{
|
||||
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();
|
||||
env_log_exists |= (id == Parent::Env::log());
|
||||
env_binary_exists |= (id == Parent::Env::binary());
|
||||
});
|
||||
|
||||
if (!env_binary_exists || !env_log_exists) {
|
||||
abandon();
|
||||
return MAY_HAVE_SIDE_EFFECTS;
|
||||
}
|
||||
}
|
||||
|
||||
bool provided_services_changed = false;
|
||||
|
||||
enum Config_update { CONFIG_APPEARED, CONFIG_VANISHED,
|
||||
CONFIG_CHANGED, CONFIG_UNCHANGED };
|
||||
|
||||
Config_update config_update = CONFIG_UNCHANGED;
|
||||
|
||||
/*
|
||||
* Import new start node if it differs
|
||||
*/
|
||||
if (start_node.differs_from(_start_node->xml())) {
|
||||
|
||||
/*
|
||||
* Start node changed
|
||||
*
|
||||
* 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)
|
||||
config_update = CONFIG_VANISHED;
|
||||
|
||||
if (!config_was_present && config_is_present)
|
||||
config_update = CONFIG_APPEARED;
|
||||
|
||||
if (config_was_present && config_is_present) {
|
||||
|
||||
Xml_node const old_config = _start_node->xml().sub_node(tag);
|
||||
Xml_node const new_config = start_node.sub_node(tag);
|
||||
|
||||
if (new_config.differs_from(old_config))
|
||||
config_update = CONFIG_CHANGED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Import updated <provides> node
|
||||
*
|
||||
* First abandon services that are no longer present in the
|
||||
* <provides> node. Then add services that have newly appeared.
|
||||
*/
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
|
||||
if (!_provided_by_this(service))
|
||||
return;
|
||||
|
||||
typedef Service::Name Name;
|
||||
Name const name = service.name();
|
||||
|
||||
bool still_provided = false;
|
||||
_provides_sub_node(start_node)
|
||||
.for_each_sub_node("service", [&] (Xml_node node) {
|
||||
if (name == node.attribute_value("name", Name()))
|
||||
still_provided = true; });
|
||||
|
||||
if (!still_provided) {
|
||||
service.abandon();
|
||||
provided_services_changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
_provides_sub_node(start_node).for_each_sub_node("service",
|
||||
[&] (Xml_node node) {
|
||||
if (_service_exists(node))
|
||||
return;
|
||||
|
||||
_add_service(node);
|
||||
provided_services_changed = true;
|
||||
});
|
||||
|
||||
/*
|
||||
* Import new binary name. A change may affect the route for
|
||||
* the binary's ROM session, triggering the restart of the
|
||||
* child.
|
||||
*/
|
||||
_binary_name = _binary_from_xml(start_node, _unique_name);
|
||||
|
||||
_heartbeat_enabled = start_node.has_sub_node("heartbeat");
|
||||
|
||||
/* import new start node */
|
||||
_start_node.construct(_alloc, 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.
|
||||
*/
|
||||
switch (config_update) {
|
||||
case CONFIG_UNCHANGED: break;
|
||||
case CONFIG_CHANGED: _config_rom_service->trigger_update(); break;
|
||||
case CONFIG_APPEARED: _config_rom_service.construct(*this); break;
|
||||
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 NO_SIDE_EFFECTS;
|
||||
}
|
||||
|
||||
|
||||
Init::Ram_quota Init::Child::_configured_ram_quota() const
|
||||
{
|
||||
size_t assigned = 0;
|
||||
|
||||
_start_node->xml().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 };
|
||||
}
|
||||
|
||||
|
||||
Init::Cap_quota Init::Child::_configured_cap_quota() const
|
||||
{
|
||||
size_t const default_caps = _default_caps_accessor.default_caps().value;
|
||||
|
||||
return Cap_quota { _start_node->xml().attribute_value("caps", default_caps) };
|
||||
}
|
||||
|
||||
|
||||
template <typename QUOTA, typename LIMIT_ACCESSOR>
|
||||
void Init::Child::_apply_resource_upgrade(QUOTA &assigned, QUOTA const configured,
|
||||
LIMIT_ACCESSOR const &limit_accessor)
|
||||
{
|
||||
if (configured.value <= assigned.value)
|
||||
return;
|
||||
|
||||
QUOTA const limit = limit_accessor.resource_limit(QUOTA{});
|
||||
size_t const increment = configured.value - assigned.value;
|
||||
|
||||
/*
|
||||
* If the configured quota exceeds our own quota, we donate all remaining
|
||||
* quota to the child.
|
||||
*/
|
||||
if (increment > limit.value)
|
||||
if (_verbose.enabled())
|
||||
warn_insuff_quota(limit.value);
|
||||
|
||||
QUOTA const transfer { min(increment, limit.value) };
|
||||
|
||||
/*
|
||||
* Remember assignment and apply 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.value) {
|
||||
|
||||
assigned.value += transfer.value;
|
||||
|
||||
ref_pd().transfer_quota(_child.pd_session_cap(), transfer);
|
||||
|
||||
/* wake up child that blocks on a resource request */
|
||||
if (_requested_resources.constructed()) {
|
||||
_child.notify_resource_avail();
|
||||
_requested_resources.destruct();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::apply_upgrade()
|
||||
{
|
||||
/* pd_session_cap of exited child is invalid and unusable for transfers */
|
||||
if (_exited)
|
||||
return;
|
||||
|
||||
if (_resources.effective_ram_quota().value == 0)
|
||||
warning(name(), ": no valid RAM quota defined");
|
||||
|
||||
_apply_resource_upgrade(_resources.assigned_ram_quota,
|
||||
_configured_ram_quota(), _ram_limit_accessor);
|
||||
|
||||
if (_resources.effective_cap_quota().value == 0)
|
||||
warning(name(), ": no valid capability quota defined");
|
||||
|
||||
_apply_resource_upgrade(_resources.assigned_cap_quota,
|
||||
_configured_cap_quota(), _cap_limit_accessor);
|
||||
}
|
||||
|
||||
|
||||
template <typename QUOTA, typename CHILD_AVAIL_QUOTA_FN>
|
||||
void Init::Child::_apply_resource_downgrade(QUOTA &assigned, QUOTA const configured,
|
||||
QUOTA const preserved,
|
||||
CHILD_AVAIL_QUOTA_FN const &child_avail_quota_fn)
|
||||
{
|
||||
if (configured.value >= assigned.value)
|
||||
return;
|
||||
|
||||
QUOTA const decrement { assigned.value - configured.value };
|
||||
|
||||
/*
|
||||
* The child may concurrently consume quota from its PD 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 quota is exhausted */
|
||||
size_t const avail = child_avail_quota_fn().value;
|
||||
if (avail < preserved.value)
|
||||
break;
|
||||
|
||||
QUOTA const transfer { min(avail - preserved.value, decrement.value) };
|
||||
|
||||
try {
|
||||
_child.pd().transfer_quota(ref_pd_cap(), transfer);
|
||||
assigned.value -= transfer.value;
|
||||
break;
|
||||
} catch (...) { }
|
||||
}
|
||||
|
||||
if (attempts == max_attempts)
|
||||
warning(name(), ": downgrade failed after ", max_attempts, " attempts");
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::apply_downgrade()
|
||||
{
|
||||
Ram_quota const configured_ram_quota = _configured_ram_quota();
|
||||
Cap_quota const configured_cap_quota = _configured_cap_quota();
|
||||
|
||||
_apply_resource_downgrade(_resources.assigned_ram_quota,
|
||||
configured_ram_quota, Ram_quota{16*1024},
|
||||
[&] () { return _child.pd().avail_ram(); });
|
||||
|
||||
_apply_resource_downgrade(_resources.assigned_cap_quota,
|
||||
configured_cap_quota, Cap_quota{5},
|
||||
[&] () { return _child.pd().avail_caps(); });
|
||||
|
||||
/*
|
||||
* If designated resource quota is lower than the child's consumed quota,
|
||||
* issue a yield request to the child.
|
||||
*/
|
||||
size_t demanded_ram_quota = 0;
|
||||
size_t demanded_cap_quota = 0;
|
||||
|
||||
if (configured_ram_quota.value < _resources.assigned_ram_quota.value)
|
||||
demanded_ram_quota = _resources.assigned_ram_quota.value - configured_ram_quota.value;
|
||||
|
||||
if (configured_cap_quota.value < _resources.assigned_cap_quota.value)
|
||||
demanded_cap_quota = _resources.assigned_cap_quota.value - configured_cap_quota.value;
|
||||
|
||||
if (demanded_ram_quota || demanded_cap_quota) {
|
||||
Parent::Resource_args const
|
||||
args { "ram_quota=", Number_of_bytes(demanded_ram_quota), ", ",
|
||||
"cap_quota=", demanded_cap_quota};
|
||||
_child.yield(args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::report_state(Xml_generator &xml, Report_detail const &detail) const
|
||||
{
|
||||
if (abandoned())
|
||||
return;
|
||||
|
||||
/* true if it's safe to call the PD for requesting resource information */
|
||||
bool const pd_alive = !abandoned() && !_exited;
|
||||
|
||||
xml.node("child", [&] () {
|
||||
|
||||
xml.attribute("name", _unique_name);
|
||||
xml.attribute("binary", _binary_name);
|
||||
|
||||
if (_version.valid())
|
||||
xml.attribute("version", _version);
|
||||
|
||||
if (detail.ids())
|
||||
xml.attribute("id", _id.value);
|
||||
|
||||
if (!_child.active())
|
||||
xml.attribute("state", "incomplete");
|
||||
|
||||
if (_exited)
|
||||
xml.attribute("exited", _exit_value);
|
||||
|
||||
if (_heartbeat_enabled && _child.skipped_heartbeats())
|
||||
xml.attribute("skipped_heartbeats", _child.skipped_heartbeats());
|
||||
|
||||
if (detail.child_ram() && _child.pd_session_cap().valid()) {
|
||||
xml.node("ram", [&] () {
|
||||
|
||||
xml.attribute("assigned", String<32> {
|
||||
Number_of_bytes(_resources.assigned_ram_quota.value) });
|
||||
|
||||
if (pd_alive)
|
||||
generate_ram_info(xml, _child.pd());
|
||||
|
||||
if (_requested_resources.constructed() && _requested_resources->ram.value)
|
||||
xml.attribute("requested", String<32>(_requested_resources->ram));
|
||||
});
|
||||
}
|
||||
|
||||
if (detail.child_caps() && _child.pd_session_cap().valid()) {
|
||||
xml.node("caps", [&] () {
|
||||
|
||||
xml.attribute("assigned", String<32>(_resources.assigned_cap_quota));
|
||||
|
||||
if (pd_alive)
|
||||
generate_caps_info(xml, _child.pd());
|
||||
|
||||
if (_requested_resources.constructed() && _requested_resources->caps.value)
|
||||
xml.attribute("requested", String<32>(_requested_resources->caps));
|
||||
});
|
||||
}
|
||||
|
||||
Session_state::Detail const
|
||||
session_detail { detail.session_args() ? Session_state::Detail::ARGS
|
||||
: Session_state::Detail::NO_ARGS};
|
||||
|
||||
if (detail.requested()) {
|
||||
xml.node("requested", [&] () {
|
||||
_child.for_each_session([&] (Session_state const &session) {
|
||||
xml.node("session", [&] () {
|
||||
session.generate_client_side_info(xml, session_detail); }); }); });
|
||||
}
|
||||
|
||||
if (detail.provided()) {
|
||||
xml.node("provided", [&] () {
|
||||
|
||||
auto fn = [&] (Session_state const &session) {
|
||||
xml.node("session", [&] () {
|
||||
session.generate_server_side_info(xml, session_detail); }); };
|
||||
|
||||
_session_requester.id_space().for_each<Session_state const>(fn);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::init(Pd_session &session, Pd_session_capability cap)
|
||||
{
|
||||
session.ref_account(_env.pd_session_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 };
|
||||
|
||||
Cap_quota const cap_quota { _resources.effective_cap_quota().value };
|
||||
|
||||
try { _env.pd().transfer_quota(cap, cap_quota); }
|
||||
catch (Out_of_caps) {
|
||||
error(name(), ": unable to initialize cap quota of PD"); }
|
||||
|
||||
try { _env.pd().transfer_quota(cap, ram_quota); }
|
||||
catch (Out_of_ram) {
|
||||
error(name(), ": unable to initialize RAM quota of PD"); }
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::init(Cpu_session &session, Cpu_session_capability cap)
|
||||
{
|
||||
static size_t avail = Cpu_session::quota_lim_upscale( 100, 100);
|
||||
size_t const need = Cpu_session::quota_lim_upscale(_resources.cpu_quota_pc, 100);
|
||||
size_t need_adj = 0;
|
||||
|
||||
if (need > avail || avail == 0) {
|
||||
warn_insuff_quota(Cpu_session::quota_lim_downscale(avail, 100));
|
||||
need_adj = Cpu_session::quota_lim_upscale(100, 100);
|
||||
avail = 0;
|
||||
} else {
|
||||
need_adj = Cpu_session::quota_lim_upscale(need, avail);
|
||||
avail -= need;
|
||||
}
|
||||
session.ref_account(_env.cpu_session_cap());
|
||||
_env.cpu().transfer_quota(cap, need_adj);
|
||||
}
|
||||
|
||||
|
||||
Init::Child::Route Init::Child::resolve_session_request(Service::Name const &service_name,
|
||||
Session_label const &label)
|
||||
{
|
||||
/* check for "config" ROM request */
|
||||
if (service_name == Rom_session::service_name() &&
|
||||
label.last_element() == "config") {
|
||||
|
||||
if (_config_rom_service.constructed() &&
|
||||
!_config_rom_service->abandoned())
|
||||
return Route { _config_rom_service->service(), label,
|
||||
Session::Diag{false} };
|
||||
|
||||
/*
|
||||
* \deprecated the support for the <configfile> tag will
|
||||
* be removed
|
||||
*/
|
||||
if (_start_node->xml().has_sub_node("configfile")) {
|
||||
|
||||
typedef String<50> Name;
|
||||
Name const rom =
|
||||
_start_node->xml().sub_node("configfile")
|
||||
.attribute_value("name", Name());
|
||||
|
||||
/* prevent infinite recursion */
|
||||
if (rom == "config") {
|
||||
error("configfile must not be named 'config'");
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
return resolve_session_request(service_name,
|
||||
prefixed_label(name(), rom));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is neither an inline '<config>' nor a
|
||||
* '<configfile>' node present, we apply the regular session
|
||||
* routing to the "config" ROM request.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for the binary's ROM request
|
||||
*
|
||||
* The binary is requested as a ROM with the child's unique
|
||||
* name ('Child_policy::binary_name' equals 'Child_policy::name').
|
||||
* If the binary name differs from the child's unique name,
|
||||
* we resolve the session request with the binary name as label.
|
||||
* Otherwise the regular routing is applied.
|
||||
*/
|
||||
if (service_name == Rom_session::service_name() &&
|
||||
label == _unique_name && _unique_name != _binary_name)
|
||||
return resolve_session_request(service_name, _binary_name);
|
||||
|
||||
/* supply binary as dynamic linker if '<start ld="no">' */
|
||||
if (!_use_ld && service_name == Rom_session::service_name() && label == "ld.lib.so")
|
||||
return resolve_session_request(service_name, _binary_name);
|
||||
|
||||
/* check for "session_requests" ROM request */
|
||||
if (service_name == Rom_session::service_name()
|
||||
&& label.last_element() == Session_requester::rom_name())
|
||||
return Route { _session_requester.service(),
|
||||
Session::Label(), Session::Diag{false} };
|
||||
|
||||
try {
|
||||
Xml_node route_node = _default_route_accessor.default_route();
|
||||
try {
|
||||
route_node = _start_node->xml().sub_node("route"); }
|
||||
catch (...) { }
|
||||
Xml_node service_node = route_node.sub_node();
|
||||
|
||||
for (; ; service_node = service_node.next()) {
|
||||
|
||||
bool service_wildcard = service_node.has_type("any-service");
|
||||
|
||||
if (!service_node_matches(service_node, label, name(), service_name))
|
||||
continue;
|
||||
|
||||
Xml_node target = service_node.sub_node();
|
||||
for (; ; target = target.next()) {
|
||||
|
||||
/*
|
||||
* Determine session label to be provided to the server
|
||||
*
|
||||
* By default, the client's identity (accompanied with the a
|
||||
* client-provided label) is presented as session label to the
|
||||
* server. However, the target node can explicitly override the
|
||||
* client's identity by a custom label via the 'label'
|
||||
* attribute.
|
||||
*/
|
||||
typedef String<Session_label::capacity()> Label;
|
||||
Label const target_label =
|
||||
target.attribute_value("label", Label(label.string()));
|
||||
|
||||
Session::Diag const
|
||||
target_diag { target.attribute_value("diag", false) };
|
||||
|
||||
auto no_filter = [] (Service &) -> bool { return false; };
|
||||
|
||||
if (target.has_type("parent")) {
|
||||
|
||||
try {
|
||||
return Route { find_service(_parent_services, service_name, no_filter),
|
||||
target_label, target_diag };
|
||||
} catch (Service_denied) { }
|
||||
}
|
||||
|
||||
if (target.has_type("child")) {
|
||||
|
||||
typedef Name_registry::Name Name;
|
||||
Name server_name = target.attribute_value("name", Name());
|
||||
server_name = _name_registry.deref_alias(server_name);
|
||||
|
||||
auto filter_server_name = [&] (Routed_service &s) -> bool {
|
||||
return s.child_name() != server_name; };
|
||||
|
||||
try {
|
||||
return Route { find_service(_child_services, service_name, filter_server_name),
|
||||
target_label, target_diag };
|
||||
|
||||
} catch (Service_denied) { }
|
||||
}
|
||||
|
||||
if (target.has_type("any-child")) {
|
||||
|
||||
if (is_ambiguous(_child_services, service_name)) {
|
||||
error(name(), ": ambiguous routes to "
|
||||
"service \"", service_name, "\"");
|
||||
throw Service_denied();
|
||||
}
|
||||
try {
|
||||
return Route { find_service(_child_services, service_name, no_filter),
|
||||
target_label, target_diag };
|
||||
|
||||
} catch (Service_denied) { }
|
||||
}
|
||||
|
||||
if (!service_wildcard) {
|
||||
warning(name(), ": lookup for service \"", service_name, "\" failed");
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
if (target.last())
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||
|
||||
warning(name(), ": no route to service \"", service_name, "\" (label=\"", label, "\")");
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::filter_session_args(Service::Name const &service,
|
||||
char *args, size_t args_len)
|
||||
{
|
||||
/*
|
||||
* Intercept CPU session requests to scale priorities
|
||||
*/
|
||||
if ((service == Cpu_session::service_name() ||
|
||||
service == Vm_session::service_name())
|
||||
&& _prio_levels_log2 > 0) {
|
||||
|
||||
unsigned long priority = Arg_string::find_arg(args, "priority").ulong_value(0);
|
||||
|
||||
/* clamp priority value to valid range */
|
||||
priority = min((unsigned)Cpu_session::PRIORITY_LIMIT - 1, priority);
|
||||
|
||||
long discarded_prio_lsb_bits_mask = (1 << _prio_levels_log2) - 1;
|
||||
if (priority & discarded_prio_lsb_bits_mask)
|
||||
warning("priority band too small, losing least-significant priority bits");
|
||||
|
||||
priority >>= _prio_levels_log2;
|
||||
|
||||
/* assign child priority to the most significant priority bits */
|
||||
priority |= _priority*(Cpu_session::PRIORITY_LIMIT >> _prio_levels_log2);
|
||||
|
||||
/* override priority when delegating the session request to the parent */
|
||||
String<64> value { Hex(priority) };
|
||||
Arg_string::set_arg(args, args_len, "priority", value.string());
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove phys_start and phys_size RAM-session arguments unless
|
||||
* explicitly permitted by the child configuration.
|
||||
*/
|
||||
if (service == Pd_session::service_name()) {
|
||||
|
||||
/*
|
||||
* If the child is allowed to constrain physical memory allocations,
|
||||
* pass the child-provided constraints as session arguments to core.
|
||||
* If no constraints are specified, we apply the constraints for
|
||||
* allocating DMA memory (as the only use case for the constrain-phys
|
||||
* mechanism).
|
||||
*/
|
||||
if (_constrain_phys) {
|
||||
addr_t start = 0;
|
||||
addr_t size = (sizeof(long) == 4) ? 0xc0000000UL : 0x100000000UL;
|
||||
|
||||
Arg_string::find_arg(args, "phys_start").ulong_value(start);
|
||||
Arg_string::find_arg(args, "phys_size") .ulong_value(size);
|
||||
|
||||
Arg_string::set_arg(args, args_len, "phys_start", String<32>(Hex(start)).string());
|
||||
Arg_string::set_arg(args, args_len, "phys_size", String<32>(Hex(size)) .string());
|
||||
} else {
|
||||
Arg_string::remove_arg(args, "phys_start");
|
||||
Arg_string::remove_arg(args, "phys_size");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Genode::Affinity Init::Child::filter_session_affinity(Affinity const &session_affinity)
|
||||
{
|
||||
Affinity::Space const &child_space = _resources.affinity.space();
|
||||
Affinity::Location const &child_location = _resources.affinity.location();
|
||||
|
||||
/* check if no valid affinity space was specified */
|
||||
if (session_affinity.space().total() == 0)
|
||||
return Affinity(child_space, child_location);
|
||||
|
||||
Affinity::Space const &session_space = session_affinity.space();
|
||||
Affinity::Location const &session_location = session_affinity.location();
|
||||
|
||||
/* scale resolution of resulting space */
|
||||
Affinity::Space space(child_space.multiply(session_space));
|
||||
|
||||
/* subordinate session affinity to child affinity subspace */
|
||||
Affinity::Location location(child_location
|
||||
.multiply_position(session_space)
|
||||
.transpose(session_location.xpos() * child_space.width(),
|
||||
session_location.ypos() * child_space.height()));
|
||||
|
||||
return Affinity(space, location);
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::announce_service(Service::Name const &service_name)
|
||||
{
|
||||
if (_verbose.enabled())
|
||||
log("child \"", name(), "\" announces service \"", service_name, "\"");
|
||||
|
||||
bool found = false;
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
if (service.has_id_space(_session_requester.id_space())
|
||||
&& service.name() == service_name)
|
||||
found = true; });
|
||||
|
||||
if (!found)
|
||||
error(name(), ": illegal announcement of "
|
||||
"service \"", service_name, "\"");
|
||||
}
|
||||
|
||||
|
||||
void Init::Child::resource_request(Parent::Resource_args const &args)
|
||||
{
|
||||
log("child \"", name(), "\" requests resources: ", args);
|
||||
|
||||
_requested_resources.construct(args);
|
||||
_report_update_trigger.trigger_immediate_report_update();
|
||||
}
|
||||
|
||||
|
||||
Init::Child::Child(Env &env,
|
||||
Allocator &alloc,
|
||||
Verbose const &verbose,
|
||||
Id id,
|
||||
Report_update_trigger &report_update_trigger,
|
||||
Xml_node start_node,
|
||||
Default_route_accessor &default_route_accessor,
|
||||
Default_caps_accessor &default_caps_accessor,
|
||||
Name_registry &name_registry,
|
||||
Ram_quota ram_limit,
|
||||
Cap_quota cap_limit,
|
||||
Ram_limit_accessor &ram_limit_accessor,
|
||||
Cap_limit_accessor &cap_limit_accessor,
|
||||
Prio_levels prio_levels,
|
||||
Affinity::Space const &affinity_space,
|
||||
Registry<Parent_service> &parent_services,
|
||||
Registry<Routed_service> &child_services)
|
||||
:
|
||||
_env(env), _alloc(alloc), _verbose(verbose), _id(id),
|
||||
_report_update_trigger(report_update_trigger),
|
||||
_list_element(this),
|
||||
_start_node(_alloc, start_node),
|
||||
_default_route_accessor(default_route_accessor),
|
||||
_default_caps_accessor(default_caps_accessor),
|
||||
_ram_limit_accessor(ram_limit_accessor),
|
||||
_cap_limit_accessor(cap_limit_accessor),
|
||||
_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)),
|
||||
_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: ", _resources.effective_ram_quota());
|
||||
log(" cap quota: ", _resources.effective_cap_quota());
|
||||
log(" ELF binary: ", _binary_name);
|
||||
log(" priority: ", _resources.priority);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine services provided by the child
|
||||
*/
|
||||
_provides_sub_node(start_node)
|
||||
.for_each_sub_node("service",
|
||||
[&] (Xml_node node) { _add_service(node); });
|
||||
|
||||
/*
|
||||
* Construct inline config ROM service if "config" node is present.
|
||||
*/
|
||||
if (start_node.has_sub_node("config"))
|
||||
_config_rom_service.construct(*this);
|
||||
}
|
||||
|
||||
|
||||
Init::Child::~Child() { }
|
@ -1,648 +0,0 @@
|
||||
/*
|
||||
* \brief Child representation
|
||||
* \author Norman Feske
|
||||
* \date 2010-05-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__CHILD_H_
|
||||
#define _SRC__INIT__CHILD_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/log.h>
|
||||
#include <base/child.h>
|
||||
#include <os/session_requester.h>
|
||||
#include <os/session_policy.h>
|
||||
#include <os/buffered_xml.h>
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
#include <verbose.h>
|
||||
#include <report.h>
|
||||
#include <name_registry.h>
|
||||
#include <service.h>
|
||||
#include <utils.h>
|
||||
|
||||
namespace Init { class Child; }
|
||||
|
||||
class Init::Child : Child_policy, Routed_service::Wakeup
|
||||
{
|
||||
public:
|
||||
|
||||
typedef String<80> Version;
|
||||
|
||||
/**
|
||||
* Exception types
|
||||
*/
|
||||
struct Child_name_is_not_unique : Exception { };
|
||||
struct Missing_name_attribute : Exception { };
|
||||
|
||||
/**
|
||||
* Unique ID of the child, solely used for diagnostic purposes
|
||||
*/
|
||||
struct Id { unsigned value; };
|
||||
|
||||
struct Default_route_accessor : Interface { virtual Xml_node default_route() = 0; };
|
||||
struct Default_caps_accessor : Interface { virtual Cap_quota default_caps() = 0; };
|
||||
|
||||
template <typename QUOTA>
|
||||
struct Resource_limit_accessor : Interface
|
||||
{
|
||||
/*
|
||||
* The argument is unused. It exists solely as an overload selector.
|
||||
*/
|
||||
virtual QUOTA resource_limit(QUOTA const &) const = 0;
|
||||
};
|
||||
|
||||
typedef Resource_limit_accessor<Ram_quota> Ram_limit_accessor;
|
||||
typedef Resource_limit_accessor<Cap_quota> Cap_limit_accessor;
|
||||
|
||||
private:
|
||||
|
||||
friend class Child_registry;
|
||||
|
||||
Env &_env;
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
Verbose const &_verbose;
|
||||
|
||||
Id const _id;
|
||||
|
||||
enum State { STATE_INITIAL, STATE_RAM_INITIALIZED, STATE_ALIVE,
|
||||
STATE_ABANDONED };
|
||||
|
||||
State _state = STATE_INITIAL;
|
||||
|
||||
Report_update_trigger &_report_update_trigger;
|
||||
|
||||
List_element<Child> _list_element;
|
||||
|
||||
Reconstructible<Buffered_xml> _start_node;
|
||||
|
||||
/*
|
||||
* Version attribute of the start node, used to force child restarts.
|
||||
*/
|
||||
Version _version { _start_node->xml().attribute_value("version", Version()) };
|
||||
|
||||
/*
|
||||
* True if the binary is loaded with ld.lib.so
|
||||
*/
|
||||
bool const _use_ld = _start_node->xml().attribute_value("ld", true);
|
||||
|
||||
Default_route_accessor &_default_route_accessor;
|
||||
Default_caps_accessor &_default_caps_accessor;
|
||||
Ram_limit_accessor &_ram_limit_accessor;
|
||||
Cap_limit_accessor &_cap_limit_accessor;
|
||||
|
||||
Name_registry &_name_registry;
|
||||
|
||||
/**
|
||||
* Read name from XML and check for name confict with other children
|
||||
*
|
||||
* \throw Missing_name_attribute
|
||||
*/
|
||||
static Name _name_from_xml(Xml_node start_node)
|
||||
{
|
||||
Name const name = start_node.attribute_value("name", Name());
|
||||
if (name.valid())
|
||||
return name;
|
||||
|
||||
warning("missint 'name' attribute in '<start>' entry");
|
||||
throw Missing_name_attribute();
|
||||
}
|
||||
|
||||
typedef String<64> Name;
|
||||
Name const _unique_name { _name_from_xml(_start_node->xml()) };
|
||||
|
||||
static Binary_name _binary_from_xml(Xml_node start_node,
|
||||
Name const &unique_name)
|
||||
{
|
||||
if (!start_node.has_sub_node("binary"))
|
||||
return unique_name;
|
||||
|
||||
return start_node.sub_node("binary").attribute_value("name", Name());
|
||||
}
|
||||
|
||||
/* updated on configuration update */
|
||||
Binary_name _binary_name { _binary_from_xml(_start_node->xml(), _unique_name) };
|
||||
|
||||
/* initialized in constructor, updated by 'apply_config' */
|
||||
bool _heartbeat_enabled;
|
||||
|
||||
/*
|
||||
* Number of skipped heartbeats when last checked
|
||||
*
|
||||
* This variable is used for the triggering of state-report updates
|
||||
* due to heartbeat events.
|
||||
*/
|
||||
unsigned _last_skipped_heartbeats = 0;
|
||||
|
||||
/* return true if heartbeat tracking is active */
|
||||
bool _heartbeat_expected() const
|
||||
{
|
||||
/* don't expect heartbeats from a child that is not yet complete */
|
||||
return _heartbeat_enabled && (_state == STATE_ALIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resources assigned to the child
|
||||
*/
|
||||
struct Resources
|
||||
{
|
||||
long prio_levels_log2;
|
||||
long priority;
|
||||
Affinity affinity;
|
||||
Ram_quota assigned_ram_quota;
|
||||
Cap_quota assigned_cap_quota;
|
||||
size_t cpu_quota_pc;
|
||||
bool constrain_phys;
|
||||
|
||||
Ram_quota effective_ram_quota() const
|
||||
{
|
||||
return Genode::Child::effective_quota(assigned_ram_quota);
|
||||
}
|
||||
|
||||
Cap_quota effective_cap_quota() const
|
||||
{
|
||||
/* capabilities consumed by 'Genode::Child' */
|
||||
Cap_quota const effective =
|
||||
Genode::Child::effective_quota(assigned_cap_quota);
|
||||
|
||||
/* capabilities additionally consumed by init */
|
||||
enum {
|
||||
STATIC_COSTS = 1 /* possible heap backing-store
|
||||
allocation for session object */
|
||||
+ 1 /* buffered XML start node */
|
||||
+ 2 /* dynamic ROM for config */
|
||||
+ 2 /* dynamic ROM for session requester */
|
||||
};
|
||||
|
||||
if (effective.value < STATIC_COSTS)
|
||||
return Cap_quota{0};
|
||||
|
||||
return Cap_quota{effective.value - STATIC_COSTS};
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
size_t cpu_quota_pc = 0;
|
||||
bool constrain_phys = false;
|
||||
Number_of_bytes ram_bytes = 0;
|
||||
|
||||
size_t caps = start_node.attribute_value("caps", default_cap_quota.value);
|
||||
|
||||
start_node.for_each_sub_node("resource", [&] (Xml_node rsc) {
|
||||
|
||||
typedef String<8> Name;
|
||||
Name const name = rsc.attribute_value("name", Name());
|
||||
|
||||
if (name == "RAM") {
|
||||
ram_bytes = rsc.attribute_value("quantum", ram_bytes);
|
||||
constrain_phys = rsc.attribute_value("constrain_phys", false);
|
||||
}
|
||||
|
||||
if (name == "CPU") {
|
||||
cpu_quota_pc = rsc.attribute_value("quantum", 0UL);
|
||||
}
|
||||
|
||||
if (name == "CAP") {
|
||||
caps = rsc.attribute_value("quantum", 0UL);
|
||||
}
|
||||
});
|
||||
|
||||
return Resources { log2(prio_levels.value),
|
||||
priority_from_xml(start_node, prio_levels),
|
||||
Affinity(affinity_space,
|
||||
affinity_location_from_xml(affinity_space, start_node)),
|
||||
Ram_quota { ram_bytes },
|
||||
Cap_quota { caps },
|
||||
cpu_quota_pc,
|
||||
constrain_phys };
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
Registry<Parent_service> &_parent_services;
|
||||
Registry<Routed_service> &_child_services;
|
||||
|
||||
struct Inline_config_rom_service : Abandonable, Dynamic_rom_session::Content_producer
|
||||
{
|
||||
typedef Local_service<Dynamic_rom_session> Service;
|
||||
|
||||
Child &_child;
|
||||
|
||||
Dynamic_rom_session _session { _child._env.ep().rpc_ep(),
|
||||
_child.ref_pd(), _child._env.rm(),
|
||||
*this };
|
||||
|
||||
Service::Single_session_factory _factory { _session };
|
||||
Service _service { _factory };
|
||||
|
||||
Inline_config_rom_service(Child &child) : _child(child) { }
|
||||
|
||||
/**
|
||||
* Dynamic_rom_session::Content_producer interface
|
||||
*/
|
||||
void produce_content(char *dst, Genode::size_t dst_len) override
|
||||
{
|
||||
Xml_node config = _child._start_node->xml().has_sub_node("config")
|
||||
? _child._start_node->xml().sub_node("config")
|
||||
: Xml_node("<config/>");
|
||||
|
||||
size_t const config_len = config.size();
|
||||
|
||||
if (config_len + 1 /* null termination */ >= dst_len)
|
||||
throw Buffer_capacity_exceeded();
|
||||
|
||||
config.with_raw_node([&] (char const *start, size_t length) {
|
||||
|
||||
/*
|
||||
* The 'length' is the number of bytes of the config-node
|
||||
* content, which is not null-terminated. Since
|
||||
* 'Genode::strncpy' always null-terminates the result, the
|
||||
* last byte of the source string is not copied. Hence, it
|
||||
* is safe to add '1' to 'length' and thereby include the
|
||||
* last actual config-content character in the result.
|
||||
*/
|
||||
Genode::strncpy(dst, start, length + 1);
|
||||
});
|
||||
}
|
||||
|
||||
void trigger_update() { _session.trigger_update(); }
|
||||
|
||||
Service &service() { return _service; }
|
||||
};
|
||||
|
||||
Constructible<Inline_config_rom_service> _config_rom_service { };
|
||||
|
||||
Session_requester _session_requester;
|
||||
|
||||
/**
|
||||
* CPU-session priority parameters
|
||||
*/
|
||||
long const _prio_levels_log2 { _resources.prio_levels_log2 };
|
||||
long const _priority { _resources.priority };
|
||||
|
||||
/**
|
||||
* If set to true, the child is allowed to constrain physical RAM
|
||||
* allocations.
|
||||
*/
|
||||
bool const _constrain_phys { _resources.constrain_phys };
|
||||
|
||||
/**
|
||||
* Resource request initiated by the child
|
||||
*/
|
||||
struct Requested_resources
|
||||
{
|
||||
Ram_quota const ram;
|
||||
Cap_quota const caps;
|
||||
|
||||
Requested_resources(Parent::Resource_args const &args)
|
||||
:
|
||||
ram (ram_quota_from_args(args.string())),
|
||||
caps(cap_quota_from_args(args.string()))
|
||||
{ }
|
||||
};
|
||||
|
||||
Constructible<Requested_resources> _requested_resources { };
|
||||
|
||||
Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), *this };
|
||||
|
||||
struct Pd_accessor : Routed_service::Pd_accessor
|
||||
{
|
||||
Genode::Child &_child;
|
||||
|
||||
Pd_accessor(Genode::Child &child) : _child(child) { }
|
||||
|
||||
Pd_session &pd() override { return _child.pd(); }
|
||||
Pd_session_capability pd_cap() const override { return _child.pd_session_cap(); }
|
||||
|
||||
} _pd_accessor { _child };
|
||||
|
||||
struct Ram_accessor : Routed_service::Ram_accessor
|
||||
{
|
||||
Genode::Child &_child;
|
||||
|
||||
Ram_accessor(Genode::Child &child) : _child(child) { }
|
||||
|
||||
Pd_session &ram() override { return _child.pd(); }
|
||||
Pd_session_capability ram_cap() const override { return _child.pd_session_cap(); }
|
||||
|
||||
} _ram_accessor { _child };
|
||||
|
||||
/**
|
||||
* Async_service::Wakeup callback
|
||||
*/
|
||||
void wakeup_async_service() override
|
||||
{
|
||||
_session_requester.trigger_update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the policy results in the current route of the session
|
||||
*
|
||||
* This method is used to check if a policy change affects an existing
|
||||
* client session of a child, i.e., to determine whether the child must
|
||||
* be restarted.
|
||||
*/
|
||||
bool _route_valid(Session_state const &session)
|
||||
{
|
||||
try {
|
||||
Route const route =
|
||||
resolve_session_request(session.service().name(),
|
||||
session.client_label());
|
||||
|
||||
return (session.service() == route.service)
|
||||
&& (route.label == session.label());
|
||||
}
|
||||
catch (Service_denied) { return false; }
|
||||
}
|
||||
|
||||
static Xml_node _provides_sub_node(Xml_node start_node)
|
||||
{
|
||||
return start_node.has_sub_node("provides")
|
||||
? start_node.sub_node("provides") : Xml_node("<provides/>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if service is provided by this child
|
||||
*/
|
||||
bool _provided_by_this(Routed_service const &service) const
|
||||
{
|
||||
return service.has_id_space(_session_requester.id_space());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if service of specified <provides> sub node is known
|
||||
*/
|
||||
bool _service_exists(Xml_node node) const
|
||||
{
|
||||
bool exists = false;
|
||||
_child_services.for_each([&] (Routed_service const &service) {
|
||||
if (_provided_by_this(service) &&
|
||||
service.name() == node.attribute_value("name", Service::Name()))
|
||||
exists = true; });
|
||||
|
||||
return exists && !abandoned();
|
||||
}
|
||||
|
||||
void _add_service(Xml_node service)
|
||||
{
|
||||
Service::Name const name =
|
||||
service.attribute_value("name", Service::Name());
|
||||
|
||||
if (_verbose.enabled())
|
||||
log(" provides service ", name);
|
||||
|
||||
new (_alloc)
|
||||
Routed_service(_child_services, this->name(),
|
||||
_pd_accessor, _ram_accessor,
|
||||
_session_requester.id_space(),
|
||||
_child.session_factory(),
|
||||
name, *this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exit state of the child set when 'exit()' is executed
|
||||
* and reported afterwards through the state report.
|
||||
*/
|
||||
|
||||
bool _exited { false };
|
||||
int _exit_value { -1 };
|
||||
|
||||
void _destroy_services();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc allocator solely used for configuration-
|
||||
* dependent allocations. It is not used for
|
||||
* allocations on behalf of the child's
|
||||
* behavior.
|
||||
*
|
||||
* \param ram_limit 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,
|
||||
Verbose const &verbose,
|
||||
Id id,
|
||||
Report_update_trigger &report_update_trigger,
|
||||
Xml_node start_node,
|
||||
Default_route_accessor &default_route_accessor,
|
||||
Default_caps_accessor &default_caps_accessor,
|
||||
Name_registry &name_registry,
|
||||
Ram_quota ram_limit,
|
||||
Cap_quota cap_limit,
|
||||
Ram_limit_accessor &ram_limit_accessor,
|
||||
Cap_limit_accessor &cap_limit_accessor,
|
||||
Prio_levels prio_levels,
|
||||
Affinity::Space const &affinity_space,
|
||||
Registry<Parent_service> &parent_services,
|
||||
Registry<Routed_service> &child_services);
|
||||
|
||||
virtual ~Child();
|
||||
|
||||
/**
|
||||
* Return true if the child has the specified name
|
||||
*/
|
||||
bool has_name(Child_policy::Name const &str) const { return str == name(); }
|
||||
|
||||
bool has_version(Version const &version) const { return version == _version; }
|
||||
|
||||
Ram_quota ram_quota() const { return _resources.assigned_ram_quota; }
|
||||
Cap_quota cap_quota() const { return _resources.assigned_cap_quota; }
|
||||
|
||||
void initiate_env_pd_session()
|
||||
{
|
||||
if (_state == STATE_INITIAL) {
|
||||
_child.initiate_env_pd_session();
|
||||
_state = STATE_RAM_INITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
void initiate_env_sessions()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void abandon()
|
||||
{
|
||||
_state = STATE_ABANDONED;
|
||||
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
if (service.has_id_space(_session_requester.id_space()))
|
||||
service.abandon(); });
|
||||
}
|
||||
|
||||
void destroy_services();
|
||||
|
||||
void close_all_sessions() { _child.close_all_sessions(); }
|
||||
|
||||
bool abandoned() const { return _state == STATE_ABANDONED; }
|
||||
|
||||
bool env_sessions_closed() const { return _child.env_sessions_closed(); }
|
||||
|
||||
enum Apply_config_result { MAY_HAVE_SIDE_EFFECTS, NO_SIDE_EFFECTS };
|
||||
|
||||
/**
|
||||
* Apply new configuration to child
|
||||
*
|
||||
* \throw Allocator::Out_of_memory unable to allocate buffer for new
|
||||
* config
|
||||
*/
|
||||
Apply_config_result apply_config(Xml_node start_node);
|
||||
|
||||
/* common code for upgrading RAM and caps */
|
||||
template <typename QUOTA, typename LIMIT_ACCESSOR>
|
||||
void _apply_resource_upgrade(QUOTA &, QUOTA, LIMIT_ACCESSOR const &);
|
||||
|
||||
template <typename QUOTA, typename CHILD_AVAIL_QUOTA_FN>
|
||||
void _apply_resource_downgrade(QUOTA &, QUOTA, QUOTA,
|
||||
CHILD_AVAIL_QUOTA_FN const &);
|
||||
|
||||
void apply_upgrade();
|
||||
void apply_downgrade();
|
||||
|
||||
void heartbeat()
|
||||
{
|
||||
if (_heartbeat_expected())
|
||||
_child.heartbeat();
|
||||
|
||||
unsigned const skipped_heartbeats = _child.skipped_heartbeats();
|
||||
|
||||
if (_last_skipped_heartbeats != skipped_heartbeats)
|
||||
_report_update_trigger.trigger_report_update();
|
||||
|
||||
_last_skipped_heartbeats = skipped_heartbeats;
|
||||
|
||||
}
|
||||
|
||||
unsigned skipped_heartbeats() const
|
||||
{
|
||||
return _heartbeat_expected() ? _child.skipped_heartbeats() : 0;
|
||||
}
|
||||
|
||||
void report_state(Xml_generator &xml, Report_detail const &detail) const;
|
||||
|
||||
|
||||
/****************************
|
||||
** Child-policy interface **
|
||||
****************************/
|
||||
|
||||
Child_policy::Name name() const override { return _unique_name; }
|
||||
|
||||
Pd_session &ref_pd() override { return _env.pd(); }
|
||||
Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); }
|
||||
|
||||
void init(Pd_session &, Pd_session_capability) override;
|
||||
void init(Cpu_session &, Cpu_session_capability) override;
|
||||
|
||||
Id_space<Parent::Server> &server_id_space() override {
|
||||
return _session_requester.id_space(); }
|
||||
|
||||
Route resolve_session_request(Service::Name const &,
|
||||
Session_label const &) override;
|
||||
|
||||
void filter_session_args(Service::Name const &, char *, size_t) override;
|
||||
Affinity filter_session_affinity(Affinity const &) override;
|
||||
void announce_service(Service::Name const &) override;
|
||||
void resource_request(Parent::Resource_args const &) override;
|
||||
|
||||
void exit(int exit_value) override
|
||||
{
|
||||
try {
|
||||
if (_start_node->xml().sub_node("exit").attribute_value("propagate", false)) {
|
||||
_env.parent().exit(exit_value);
|
||||
return;
|
||||
}
|
||||
} catch (...) { }
|
||||
|
||||
/*
|
||||
* Trigger a new report for exited children so that any management
|
||||
* component may react upon it.
|
||||
*/
|
||||
_exited = true;
|
||||
_exit_value = exit_value;
|
||||
|
||||
_child.close_all_sessions();
|
||||
|
||||
_report_update_trigger.trigger_report_update();
|
||||
|
||||
/*
|
||||
* Print a message as the exit is not handled otherwise. There are
|
||||
* a number of automated tests that rely on this message. It is
|
||||
* printed by the default implementation of 'Child_policy::exit'.
|
||||
*/
|
||||
Child_policy::exit(exit_value);
|
||||
}
|
||||
|
||||
void session_state_changed() override
|
||||
{
|
||||
_report_update_trigger.trigger_report_update();
|
||||
}
|
||||
|
||||
bool initiate_env_sessions() const override { return false; }
|
||||
|
||||
void yield_response() override
|
||||
{
|
||||
apply_downgrade();
|
||||
_report_update_trigger.trigger_report_update();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__CHILD_H_ */
|
@ -1,149 +0,0 @@
|
||||
/*
|
||||
* \brief Child registry
|
||||
* \author Norman Feske
|
||||
* \date 2010-04-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__CHILD_REGISTRY_H_
|
||||
#define _SRC__INIT__CHILD_REGISTRY_H_
|
||||
|
||||
/* local includes */
|
||||
#include <child.h>
|
||||
#include <name_registry.h>
|
||||
#include <alias.h>
|
||||
#include <report.h>
|
||||
|
||||
namespace Init { struct Child_registry; }
|
||||
|
||||
|
||||
class Init::Child_registry : public Name_registry, Child_list
|
||||
{
|
||||
private:
|
||||
|
||||
List<Alias> _aliases { };
|
||||
|
||||
bool _unique(const char *name) const
|
||||
{
|
||||
/* check for name clash with an existing child */
|
||||
Genode::List_element<Init::Child> 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
|
||||
*/
|
||||
void insert(Child *child)
|
||||
{
|
||||
Child_list::insert(&child->_list_element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister child
|
||||
*/
|
||||
void remove(Child *child)
|
||||
{
|
||||
Child_list::remove(&child->_list_element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register alias
|
||||
*/
|
||||
void insert_alias(Alias *alias)
|
||||
{
|
||||
if (!_unique(alias->name.string())) {
|
||||
error("alias name ", alias->name, " is not unique");
|
||||
throw Alias_name_is_not_unique();
|
||||
}
|
||||
_aliases.insert(alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister alias
|
||||
*/
|
||||
void remove_alias(Alias *alias)
|
||||
{
|
||||
_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 <typename FN>
|
||||
void for_each_child(FN const &fn) const
|
||||
{
|
||||
Genode::List_element<Child> const *curr = first();
|
||||
for (; curr; curr = curr->next())
|
||||
fn(*curr->object());
|
||||
}
|
||||
|
||||
template <typename FN>
|
||||
void for_each_child(FN const &fn)
|
||||
{
|
||||
Genode::List_element<Child> *curr = first(), *next = nullptr;
|
||||
for (; curr; curr = next) {
|
||||
next = curr->next();
|
||||
fn(*curr->object());
|
||||
}
|
||||
}
|
||||
|
||||
void report_state(Xml_generator &xml, Report_detail const &detail) const
|
||||
{
|
||||
for_each_child([&] (Child &child) { child.report_state(xml, detail); });
|
||||
|
||||
/* 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);
|
||||
xml.attribute("child", a->child);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Child::Name deref_alias(Child::Name const &name) override
|
||||
{
|
||||
for (Alias const *a = _aliases.first(); a; a = a->next())
|
||||
if (name == a->name)
|
||||
return a->child;
|
||||
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__CHILD_REGISTRY_H_ */
|
@ -1,82 +0,0 @@
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<start name="timer">
|
||||
<binary name="timer"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
<route>
|
||||
<service name="IO_PORT"> <parent/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="fb_sdl">
|
||||
<binary name="fb_sdl"/>
|
||||
<resource name="RAM" quantum="3M"/>
|
||||
<provides>
|
||||
<service name="Framebuffer"/>
|
||||
<service name="Input"/>
|
||||
</provides>
|
||||
<route>
|
||||
<service name="LOG"><parent/></service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="nitpicker">
|
||||
<binary name="nitpicker"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Nitpicker"/></provides>
|
||||
<route>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Framebuffer"> <child name="fb_sdl"/> </service>
|
||||
<service name="Input"> <child name="fb_sdl"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="liquid_fb">
|
||||
<binary name="liquid_fb"/>
|
||||
<resource name="RAM" quantum="6M"/>
|
||||
<provides>
|
||||
<service name="Framebuffer"/>
|
||||
<service name="Input"/>
|
||||
</provides>
|
||||
<route>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Nitpicker"> <child name="nitpicker"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="nested_nitpicker">
|
||||
<binary name="nitpicker"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Nitpicker"/></provides>
|
||||
<route>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Framebuffer"> <child name="liquid_fb"/> </service>
|
||||
<service name="Input"> <child name="liquid_fb"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="launchpad">
|
||||
<binary name="launchpad"/>
|
||||
<resource name="RAM" quantum="32M"/>
|
||||
<route>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Nitpicker"> <child name="nested_nitpicker"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
</config>
|
||||
|
||||
<!-- vim:set syntax=xml:-->
|
@ -1,67 +0,0 @@
|
||||
<config prio_levels="4" verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="fb_sdl">
|
||||
<resource name="RAM" quantum="3M"/>
|
||||
<provides>
|
||||
<service name="Framebuffer"/>
|
||||
<service name="Input"/>
|
||||
</provides>
|
||||
</start>
|
||||
<start name="nitpicker">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Nitpicker"/></provides>
|
||||
<route>
|
||||
<any-service>
|
||||
<parent/> <child name="fb_sdl"/> <any-child/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="liquid_fb">
|
||||
<resource name="RAM" quantum="6M"/>
|
||||
<provides>
|
||||
<service name="Framebuffer"/>
|
||||
<service name="Input"/>
|
||||
</provides>
|
||||
<route>
|
||||
<any-service>
|
||||
<parent/> <child name="nitpicker"/> <any-child/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="nested_nitpicker">
|
||||
<binary name="nitpicker"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Nitpicker"/></provides>
|
||||
<route>
|
||||
<any-service>
|
||||
<parent/> <child name="liquid_fb"/> <any-child/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="launchpad" priority="-1">
|
||||
<resource name="RAM" quantum="32M"/>
|
||||
<route>
|
||||
<any-service>
|
||||
<parent/> <child name="nested_nitpicker"/> <any-child/>
|
||||
</any-service>
|
||||
</route>
|
||||
</start>
|
||||
</config>
|
||||
|
||||
<!-- vim:set syntax=xml:-->
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* \brief Heartbeat monitoring
|
||||
* \author Norman Feske
|
||||
* \date 2018-11-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__HEARTBEAT_H_
|
||||
#define _SRC__INIT__HEARTBEAT_H_
|
||||
|
||||
/* local includes */
|
||||
#include <state_reporter.h>
|
||||
#include <child_registry.h>
|
||||
#include <util/noncopyable.h>
|
||||
|
||||
namespace Init { class Heartbeat; }
|
||||
|
||||
|
||||
class Init::Heartbeat : Genode::Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
|
||||
Child_registry &_children;
|
||||
|
||||
Report_update_trigger &_report_update_trigger;
|
||||
|
||||
Constructible<Timer::Connection> _timer { };
|
||||
|
||||
uint64_t _rate_ms = 0;
|
||||
|
||||
Signal_handler<Heartbeat> _timer_handler;
|
||||
|
||||
void _handle_timer()
|
||||
{
|
||||
bool any_skipped_heartbeats = false;
|
||||
|
||||
_children.for_each_child([&] (Child &child) {
|
||||
|
||||
if (child.skipped_heartbeats())
|
||||
any_skipped_heartbeats = true;
|
||||
|
||||
child.heartbeat();
|
||||
});
|
||||
|
||||
if (any_skipped_heartbeats)
|
||||
_report_update_trigger.trigger_report_update();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Heartbeat(Env &env, Child_registry &children,
|
||||
Report_update_trigger &report_update_trigger)
|
||||
:
|
||||
_env(env), _children(children),
|
||||
_report_update_trigger(report_update_trigger),
|
||||
_timer_handler(_env.ep(), *this, &Heartbeat::_handle_timer)
|
||||
{ }
|
||||
|
||||
void apply_config(Xml_node config)
|
||||
{
|
||||
bool const enabled = config.has_sub_node("heartbeat");
|
||||
|
||||
_timer.conditional(enabled, _env);
|
||||
|
||||
if (!enabled) {
|
||||
_rate_ms = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned const rate_ms =
|
||||
config.sub_node("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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__HEARTBEAT_H_ */
|
@ -14,149 +14,38 @@
|
||||
/* Genode includes */
|
||||
#include <base/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <os/sandbox.h>
|
||||
|
||||
/* local includes */
|
||||
#include <child.h>
|
||||
#include <alias.h>
|
||||
#include <server.h>
|
||||
#include <heartbeat.h>
|
||||
namespace Init {
|
||||
|
||||
namespace Init { struct Main; }
|
||||
using namespace Genode;
|
||||
|
||||
struct Main;
|
||||
}
|
||||
|
||||
|
||||
struct Init::Main : State_reporter::Producer,
|
||||
Child::Default_route_accessor, Child::Default_caps_accessor,
|
||||
Child::Ram_limit_accessor, Child::Cap_limit_accessor
|
||||
struct Init::Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Registry<Init::Parent_service> _parent_services { };
|
||||
Registry<Routed_service> _child_services { };
|
||||
Child_registry _children { };
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
Sandbox _sandbox { _env };
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
Xml_node _config_xml = _config.xml();
|
||||
|
||||
Reconstructible<Verbose> _verbose { _config_xml };
|
||||
|
||||
Constructible<Buffered_xml> _default_route { };
|
||||
|
||||
Cap_quota _default_caps { 0 };
|
||||
|
||||
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 _avail_ram() const
|
||||
{
|
||||
Ram_quota const preserved_ram = _preserved_ram_from_config(_config_xml);
|
||||
|
||||
Ram_quota avail_ram = _env.pd().avail_ram();
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
Cap_quota _avail_caps() const
|
||||
{
|
||||
Cap_quota const preserved_caps = _preserved_caps_from_config(_config_xml);
|
||||
|
||||
Cap_quota avail_caps { _env.pd().avail_caps().value };
|
||||
|
||||
if (preserved_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 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Child::Ram_limit_accessor interface
|
||||
*/
|
||||
Ram_quota resource_limit(Ram_quota const &) const override { return _avail_ram(); }
|
||||
|
||||
/**
|
||||
* Child::Cap_limit_accessor interface
|
||||
*/
|
||||
Cap_quota resource_limit(Cap_quota const &) const override { return _avail_caps(); }
|
||||
|
||||
void _handle_resource_avail() { }
|
||||
|
||||
void produce_state_report(Xml_generator &xml, Report_detail const &detail) const override
|
||||
{
|
||||
if (detail.init_ram())
|
||||
xml.node("ram", [&] () { generate_ram_info (xml, _env.pd()); });
|
||||
|
||||
if (detail.init_caps())
|
||||
xml.node("caps", [&] () { generate_caps_info(xml, _env.pd()); });
|
||||
|
||||
if (detail.children())
|
||||
_children.report_state(xml, detail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default_route_accessor interface
|
||||
*/
|
||||
Xml_node default_route() override
|
||||
{
|
||||
return _default_route.constructed() ? _default_route->xml()
|
||||
: Xml_node("<empty/>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Default_caps_accessor interface
|
||||
*/
|
||||
Cap_quota default_caps() override { return _default_caps; }
|
||||
|
||||
State_reporter _state_reporter { _env, *this };
|
||||
|
||||
Heartbeat _heartbeat { _env, _children, _state_reporter };
|
||||
|
||||
Signal_handler<Main> _resource_avail_handler {
|
||||
_env.ep(), *this, &Main::_handle_resource_avail };
|
||||
|
||||
void _update_aliases_from_config();
|
||||
void _update_parent_services_from_config();
|
||||
void _abandon_obsolete_children();
|
||||
void _update_children_config();
|
||||
void _destroy_abandoned_parent_services();
|
||||
void _handle_config();
|
||||
void _handle_config()
|
||||
{
|
||||
_config.update();
|
||||
_sandbox.apply_config(_config.xml());
|
||||
}
|
||||
|
||||
Signal_handler<Main> _config_handler {
|
||||
_env.ep(), *this, &Main::_handle_config };
|
||||
|
||||
Server _server { _env, _heap, _child_services, _state_reporter };
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
_config.sigh(_config_handler);
|
||||
@ -169,312 +58,5 @@ struct Init::Main : State_reporter::Producer,
|
||||
};
|
||||
|
||||
|
||||
void Init::Main::_update_parent_services_from_config()
|
||||
{
|
||||
Xml_node const node = _config_xml.has_sub_node("parent-provides")
|
||||
? _config_xml.sub_node("parent-provides")
|
||||
: Xml_node("<empty/>");
|
||||
|
||||
/* 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) Init::Parent_service(_parent_services, _env, name);
|
||||
if (_verbose->enabled()) {
|
||||
if (first_log)
|
||||
log("parent provides");
|
||||
log(" service \"", name, "\"");
|
||||
first_log = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Init::Main::_destroy_abandoned_parent_services()
|
||||
{
|
||||
_parent_services.for_each([&] (Parent_service &service) {
|
||||
if (service.abandoned())
|
||||
destroy(_heap, &service); });
|
||||
}
|
||||
|
||||
|
||||
void Init::Main::_update_aliases_from_config()
|
||||
{
|
||||
/* remove all known aliases */
|
||||
while (_children.any_alias()) {
|
||||
Init::Alias *alias = _children.any_alias();
|
||||
_children.remove_alias(alias);
|
||||
destroy(_heap, alias);
|
||||
}
|
||||
|
||||
/* create aliases */
|
||||
_config_xml.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 '<alias>' entry"); }
|
||||
catch (Alias::Child_is_missing) {
|
||||
warning("missing 'child' attribute in '<alias>' entry"); }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Init::Main::_abandon_obsolete_children()
|
||||
{
|
||||
_children.for_each_child([&] (Child &child) {
|
||||
|
||||
bool obsolete = true;
|
||||
_config_xml.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 Init::Main::_update_children_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_xml.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 Init::Main::_handle_config()
|
||||
{
|
||||
bool update_state_report = false;
|
||||
|
||||
_config.update();
|
||||
|
||||
_config_xml = _config.xml();
|
||||
|
||||
_verbose.construct(_config_xml);
|
||||
_state_reporter.apply_config(_config_xml);
|
||||
_heartbeat.apply_config(_config_xml);
|
||||
|
||||
/* determine default route for resolving service requests */
|
||||
try {
|
||||
_default_route.construct(_heap, _config_xml.sub_node("default-route")); }
|
||||
catch (...) { }
|
||||
|
||||
_default_caps = Cap_quota { 0 };
|
||||
try {
|
||||
_default_caps = Cap_quota { _config_xml.sub_node("default")
|
||||
.attribute_value("caps", 0UL) }; }
|
||||
catch (...) { }
|
||||
|
||||
Prio_levels const prio_levels = prio_levels_from_xml(_config_xml);
|
||||
Affinity::Space const affinity_space = affinity_space_from_xml(_config_xml);
|
||||
bool const space_defined = _config_xml.has_sub_node("affinity-space");
|
||||
|
||||
_update_aliases_from_config();
|
||||
_update_parent_services_from_config();
|
||||
_abandon_obsolete_children();
|
||||
_update_children_config();
|
||||
|
||||
/* kill abandoned children */
|
||||
_children.for_each_child([&] (Child &child) {
|
||||
|
||||
if (!child.abandoned())
|
||||
return;
|
||||
|
||||
/* make the child's services unavailable */
|
||||
child.destroy_services();
|
||||
child.close_all_sessions();
|
||||
update_state_report = true;
|
||||
|
||||
/* destroy child once all environment sessions are gone */
|
||||
if (child.env_sessions_closed()) {
|
||||
_children.remove(&child);
|
||||
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();
|
||||
|
||||
/* variable used to track the RAM and caps taken by new started children */
|
||||
Ram_quota used_ram { 0 };
|
||||
Cap_quota used_caps { 0 };
|
||||
|
||||
/* create new children */
|
||||
try {
|
||||
_config_xml.for_each_sub_node("start", [&] (Xml_node start_node) {
|
||||
|
||||
bool exists = false;
|
||||
|
||||
unsigned num_abandoned = 0;
|
||||
|
||||
Child_policy::Name const child_name(start_node.attribute_value("name", Child_policy::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 {
|
||||
Init::Child &child = *new (_heap)
|
||||
Init::Child(_env, _heap, *_verbose,
|
||||
Init::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);
|
||||
_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(Init::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"); }
|
||||
});
|
||||
}
|
||||
catch (Xml_node::Nonexistent_sub_node) { error("no children to start"); }
|
||||
catch (Xml_node::Invalid_syntax) { error("config has invalid syntax"); }
|
||||
catch (Init::Child_registry::Alias_name_is_not_unique) { }
|
||||
|
||||
/*
|
||||
* Initiate RAM sessions of all new children
|
||||
*/
|
||||
_children.for_each_child([&] (Child &child) {
|
||||
if (!child.abandoned())
|
||||
child.initiate_env_pd_session(); });
|
||||
|
||||
/*
|
||||
* Initiate remaining environment sessions of all new children
|
||||
*/
|
||||
_children.for_each_child([&] (Child &child) {
|
||||
if (!child.abandoned())
|
||||
child.initiate_env_sessions(); });
|
||||
|
||||
/*
|
||||
* (Re-)distribute RAM and capability quota among the children, given their
|
||||
* resource assignments and the available slack memory. We first apply
|
||||
* possible downgrades to free as much resources as we can. These resources
|
||||
* are then incorporated in the subsequent upgrade step.
|
||||
*/
|
||||
_children.for_each_child([&] (Child &child) { child.apply_downgrade(); });
|
||||
_children.for_each_child([&] (Child &child) { child.apply_upgrade(); });
|
||||
|
||||
_server.apply_config(_config_xml);
|
||||
|
||||
if (update_state_report)
|
||||
_state_reporter.trigger_immediate_report_update();
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Init::Main main(env); }
|
||||
|
||||
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* \brief Interface for database of child names
|
||||
* \author Norman Feske
|
||||
* \date 2017-03-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__NAME_REGISTRY_H_
|
||||
#define _SRC__INIT__NAME_REGISTRY_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/child.h>
|
||||
|
||||
/* local includes */
|
||||
#include <types.h>
|
||||
|
||||
namespace Init { struct Name_registry; }
|
||||
|
||||
struct Init::Name_registry
|
||||
{
|
||||
virtual ~Name_registry() { }
|
||||
|
||||
typedef Child_policy::Name Name;
|
||||
|
||||
/**
|
||||
* Return child name for a given alias name
|
||||
*
|
||||
* If there is no alias, the function returns the original name.
|
||||
*/
|
||||
virtual Name deref_alias(Name const &) = 0;
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__NAME_REGISTRY_H_ */
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* \brief Report configuration
|
||||
* \author Norman Feske
|
||||
* \date 2017-01-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__REPORT_H_
|
||||
#define _SRC__INIT__REPORT_H_
|
||||
|
||||
#include <util/noncopyable.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
namespace Init {
|
||||
struct Report_update_trigger;
|
||||
struct Report_detail;
|
||||
}
|
||||
|
||||
|
||||
class Init::Report_detail : Genode::Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
bool _children = false;
|
||||
bool _ids = false;
|
||||
bool _requested = false;
|
||||
bool _provided = false;
|
||||
bool _session_args = false;
|
||||
bool _child_ram = false;
|
||||
bool _child_caps = false;
|
||||
bool _init_ram = false;
|
||||
bool _init_caps = false;
|
||||
|
||||
public:
|
||||
|
||||
Report_detail() { }
|
||||
|
||||
Report_detail(Genode::Xml_node report)
|
||||
{
|
||||
_children = true;
|
||||
_ids = report.attribute_value("ids", false);
|
||||
_requested = report.attribute_value("requested", false);
|
||||
_provided = report.attribute_value("provided", false);
|
||||
_session_args = report.attribute_value("session_args", false);
|
||||
_child_ram = report.attribute_value("child_ram", false);
|
||||
_child_caps = report.attribute_value("child_caps", false);
|
||||
_init_ram = report.attribute_value("init_ram", false);
|
||||
_init_caps = report.attribute_value("init_caps", false);
|
||||
}
|
||||
|
||||
bool children() const { return _children; }
|
||||
bool ids() const { return _ids; }
|
||||
bool requested() const { return _requested; }
|
||||
bool provided() const { return _provided; }
|
||||
bool session_args() const { return _session_args; }
|
||||
bool child_ram() const { return _child_ram; }
|
||||
bool child_caps() const { return _child_caps; }
|
||||
bool init_ram() const { return _init_ram; }
|
||||
bool init_caps() const { return _init_caps; }
|
||||
};
|
||||
|
||||
|
||||
struct Init::Report_update_trigger : Interface
|
||||
{
|
||||
/**
|
||||
* Trigger regular (rate-limited) report update
|
||||
*/
|
||||
virtual void trigger_report_update() = 0;
|
||||
|
||||
/**
|
||||
* Trigger immediate report update
|
||||
*
|
||||
* This method is intended for situations that require a timely response of
|
||||
* the consumer of the report. This is particularly important for resource
|
||||
* requests that would otherwise unnecessarily stall the execution of the
|
||||
* respective child.
|
||||
*/
|
||||
virtual void trigger_immediate_report_update() = 0;
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__REPORT_H_ */
|
@ -1,411 +0,0 @@
|
||||
/*
|
||||
* \brief Server role of init, forwarding session requests to children
|
||||
* \author Norman Feske
|
||||
* \date 2017-03-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/quota_transfer.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
/* local includes */
|
||||
#include "server.h"
|
||||
|
||||
|
||||
/***************************
|
||||
** Init::Server::Service **
|
||||
***************************/
|
||||
|
||||
struct Init::Server::Service
|
||||
{
|
||||
Registry<Service>::Element _registry_element;
|
||||
|
||||
Buffered_xml _service_node;
|
||||
|
||||
typedef Genode::Service::Name Name;
|
||||
|
||||
Registry<Routed_service> &_child_services;
|
||||
|
||||
Name const _name { _service_node.xml().attribute_value("name", Name()) };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc allocator used for buffering the 'service_node'
|
||||
*/
|
||||
Service(Registry<Service> &services,
|
||||
Allocator &alloc,
|
||||
Xml_node service_node,
|
||||
Registry<Routed_service> &child_services)
|
||||
:
|
||||
_registry_element(services, *this),
|
||||
_service_node(alloc, service_node),
|
||||
_child_services(child_services)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Determine route to child service for a given label according
|
||||
* to the <service> node policy
|
||||
*
|
||||
* \throw Service_denied
|
||||
*/
|
||||
Route resolve_session_request(Session_label const &);
|
||||
|
||||
Name name() const { return _name; }
|
||||
};
|
||||
|
||||
|
||||
Init::Server::Route
|
||||
Init::Server::Service::resolve_session_request(Session_label const &label)
|
||||
{
|
||||
try {
|
||||
Session_policy policy(label, _service_node.xml());
|
||||
|
||||
if (!policy.has_sub_node("child"))
|
||||
throw Service_denied();
|
||||
|
||||
Xml_node target_node = policy.sub_node("child");
|
||||
|
||||
Child_policy::Name const child_name =
|
||||
target_node.attribute_value("name", Child_policy::Name());
|
||||
|
||||
typedef String<Session_label::capacity()> Label;
|
||||
Label const target_label =
|
||||
target_node.attribute_value("label", Label(label.string()));
|
||||
|
||||
Routed_service *match = nullptr;
|
||||
_child_services.for_each([&] (Routed_service &service) {
|
||||
if (service.child_name() == child_name && service.name() == name())
|
||||
match = &service; });
|
||||
|
||||
if (!match || match->abandoned())
|
||||
throw Service_denied();
|
||||
|
||||
return Route { *match, target_label };
|
||||
}
|
||||
catch (Session_policy::No_policy_defined) {
|
||||
throw Service_denied(); }
|
||||
}
|
||||
|
||||
|
||||
/******************
|
||||
** Init::Server **
|
||||
******************/
|
||||
|
||||
Init::Server::Route
|
||||
Init::Server::_resolve_session_request(Service::Name const &service_name,
|
||||
Session_label const &label)
|
||||
{
|
||||
Service *matching_service = nullptr;
|
||||
_services.for_each([&] (Service &service) {
|
||||
if (service.name() == service_name)
|
||||
matching_service = &service; });
|
||||
|
||||
if (!matching_service)
|
||||
throw Service_not_present();
|
||||
|
||||
return matching_service->resolve_session_request(label);
|
||||
}
|
||||
|
||||
|
||||
static void close_session(Genode::Session_state &session)
|
||||
{
|
||||
session.phase = Genode::Session_state::CLOSE_REQUESTED;
|
||||
session.service().initiate_request(session);
|
||||
session.service().wakeup();
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::session_ready(Session_state &session)
|
||||
{
|
||||
_report_update_trigger.trigger_report_update();
|
||||
|
||||
/*
|
||||
* If 'session_ready' is called as response to a session-quota upgrade,
|
||||
* the 'phase' is set to 'CAP_HANDED_OUT' by 'Child::session_response'.
|
||||
* We just need to forward the state change to our parent.
|
||||
*/
|
||||
if (session.phase == Session_state::CAP_HANDED_OUT) {
|
||||
Parent::Server::Id id { session.id_at_client().value };
|
||||
_env.parent().session_response(id, Parent::SESSION_OK);
|
||||
}
|
||||
|
||||
if (session.phase == Session_state::AVAILABLE) {
|
||||
Parent::Server::Id id { session.id_at_client().value };
|
||||
_env.parent().deliver_session_cap(id, session.cap);
|
||||
session.phase = Session_state::CAP_HANDED_OUT;
|
||||
}
|
||||
|
||||
if (session.phase == Session_state::SERVICE_DENIED)
|
||||
_close_session(session, Parent::SERVICE_DENIED);
|
||||
|
||||
if (session.phase == Session_state::INSUFFICIENT_RAM_QUOTA)
|
||||
_close_session(session, Parent::INSUFFICIENT_RAM_QUOTA);
|
||||
|
||||
if (session.phase == Session_state::INSUFFICIENT_CAP_QUOTA)
|
||||
_close_session(session, Parent::INSUFFICIENT_CAP_QUOTA);
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::_close_session(Session_state &session,
|
||||
Parent::Session_response response)
|
||||
{
|
||||
_report_update_trigger.trigger_report_update();
|
||||
|
||||
Ram_transfer::Account &service_ram_account = session.service();
|
||||
Cap_transfer::Account &service_cap_account = session.service();
|
||||
|
||||
service_ram_account.try_transfer(_env.pd_session_cap(),
|
||||
session.donated_ram_quota());
|
||||
|
||||
service_cap_account.try_transfer(_env.pd_session_cap(),
|
||||
session.donated_cap_quota());
|
||||
|
||||
Parent::Server::Id id { session.id_at_client().value };
|
||||
|
||||
session.destroy();
|
||||
|
||||
_env.parent().session_response(id, response);
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::session_closed(Session_state &session)
|
||||
{
|
||||
_close_session(session, Parent::SESSION_CLOSED);
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::_handle_create_session_request(Xml_node request,
|
||||
Parent::Client::Id id)
|
||||
{
|
||||
/*
|
||||
* Ignore requests that are already successfully forwarded (by a prior call
|
||||
* of '_handle_create_session_request') but still remain present in the
|
||||
* 'session_requests' ROM because the server child has not responded yet.
|
||||
*/
|
||||
try {
|
||||
_client_id_space.apply<Parent::Client>(id, [&] (Parent::Client const &) { });
|
||||
return;
|
||||
} catch (Id_space<Parent::Client>::Unknown_id) { /* normal case */ }
|
||||
|
||||
if (!request.has_sub_node("args"))
|
||||
return;
|
||||
|
||||
typedef Session_state::Args Args;
|
||||
Args const args = request.sub_node("args").decoded_content<Args>();
|
||||
|
||||
Service::Name const name = request.attribute_value("service", Service::Name());
|
||||
|
||||
Session_label const label = label_from_args(args.string());
|
||||
|
||||
try {
|
||||
Route const route = _resolve_session_request(name, label);
|
||||
|
||||
/*
|
||||
* Reduce session quota by local session costs
|
||||
*/
|
||||
char argbuf[Parent::Session_args::MAX_SIZE];
|
||||
strncpy(argbuf, args.string(), sizeof(argbuf));
|
||||
|
||||
Cap_quota const cap_quota = cap_quota_from_args(argbuf);
|
||||
Ram_quota const ram_quota = ram_quota_from_args(argbuf);
|
||||
|
||||
size_t const keep_quota = route.service.factory().session_costs();
|
||||
|
||||
if (ram_quota.value < keep_quota)
|
||||
throw Genode::Insufficient_ram_quota();
|
||||
|
||||
Ram_quota const forward_ram_quota { ram_quota.value - keep_quota };
|
||||
|
||||
Arg_string::set_arg(argbuf, sizeof(argbuf), "ram_quota", forward_ram_quota.value);
|
||||
|
||||
Session_state &session =
|
||||
route.service.create_session(route.service.factory(),
|
||||
_client_id_space, id, route.label,
|
||||
argbuf, Affinity());
|
||||
|
||||
/* transfer session quota */
|
||||
try {
|
||||
Ram_transfer::Remote_account env_ram_account(_env.pd(), _env.pd_session_cap());
|
||||
Cap_transfer::Remote_account env_cap_account(_env.pd(), _env.pd_session_cap());
|
||||
|
||||
Ram_transfer ram_transfer(forward_ram_quota, env_ram_account, route.service);
|
||||
Cap_transfer cap_transfer(cap_quota, env_cap_account, route.service);
|
||||
|
||||
ram_transfer.acknowledge();
|
||||
cap_transfer.acknowledge();
|
||||
}
|
||||
catch (...) {
|
||||
/*
|
||||
* This should never happen unless our parent missed to
|
||||
* transfor the session quota to us prior issuing the session
|
||||
* request.
|
||||
*/
|
||||
warning("unable to transfer session quota "
|
||||
"(", ram_quota, " bytes, ", cap_quota, " caps) "
|
||||
"of forwarded ", name, " session");
|
||||
session.destroy();
|
||||
throw Service_denied();
|
||||
}
|
||||
|
||||
session.ready_callback = this;
|
||||
session.closed_callback = this;
|
||||
|
||||
/* initiate request */
|
||||
route.service.initiate_request(session);
|
||||
|
||||
/* if request was not handled synchronously, kick off async operation */
|
||||
if (session.phase == Session_state::CREATE_REQUESTED)
|
||||
route.service.wakeup();
|
||||
|
||||
if (session.phase == Session_state::SERVICE_DENIED)
|
||||
throw Service_denied();
|
||||
|
||||
if (session.phase == Session_state::INSUFFICIENT_RAM_QUOTA)
|
||||
throw Insufficient_ram_quota();
|
||||
|
||||
if (session.phase == Session_state::INSUFFICIENT_CAP_QUOTA)
|
||||
throw Insufficient_cap_quota();
|
||||
}
|
||||
catch (Service_denied) {
|
||||
_env.parent().session_response(Parent::Server::Id { id.value },
|
||||
Parent::SERVICE_DENIED); }
|
||||
catch (Insufficient_ram_quota) {
|
||||
_env.parent().session_response(Parent::Server::Id { id.value },
|
||||
Parent::INSUFFICIENT_RAM_QUOTA); }
|
||||
catch (Insufficient_cap_quota) {
|
||||
_env.parent().session_response(Parent::Server::Id { id.value },
|
||||
Parent::INSUFFICIENT_CAP_QUOTA); }
|
||||
catch (Service_not_present) { /* keep request pending */ }
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::_handle_upgrade_session_request(Xml_node request,
|
||||
Parent::Client::Id id)
|
||||
{
|
||||
_client_id_space.apply<Session_state>(id, [&] (Session_state &session) {
|
||||
|
||||
Ram_quota const ram_quota { request.attribute_value("ram_quota", 0UL) };
|
||||
Cap_quota const cap_quota { request.attribute_value("cap_quota", 0UL) };
|
||||
|
||||
session.phase = Session_state::UPGRADE_REQUESTED;
|
||||
|
||||
try {
|
||||
Ram_transfer::Remote_account env_ram_account(_env.pd(), _env.pd_session_cap());
|
||||
Cap_transfer::Remote_account env_cap_account(_env.pd(), _env.pd_session_cap());
|
||||
|
||||
Ram_transfer ram_transfer(ram_quota, env_ram_account, session.service());
|
||||
Cap_transfer cap_transfer(cap_quota, env_cap_account, session.service());
|
||||
|
||||
ram_transfer.acknowledge();
|
||||
cap_transfer.acknowledge();
|
||||
}
|
||||
catch (...) {
|
||||
warning("unable to upgrade session quota "
|
||||
"(", ram_quota, " bytes, ", cap_quota, " caps) "
|
||||
"of forwarded ", session.service().name(), " session");
|
||||
return;
|
||||
}
|
||||
|
||||
session.increase_donated_quota(ram_quota, cap_quota);
|
||||
session.service().initiate_request(session);
|
||||
session.service().wakeup();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::_handle_close_session_request(Xml_node, Parent::Client::Id id)
|
||||
{
|
||||
_client_id_space.apply<Session_state>(id, [&] (Session_state &session) {
|
||||
close_session(session); });
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::_handle_session_request(Xml_node request)
|
||||
{
|
||||
if (!request.has_attribute("id"))
|
||||
return;
|
||||
|
||||
/*
|
||||
* We use the 'Parent::Server::Id' of the incoming request as the
|
||||
* 'Parent::Client::Id' of the forwarded request.
|
||||
*/
|
||||
Parent::Client::Id const id { request.attribute_value("id", 0UL) };
|
||||
|
||||
if (request.has_type("create"))
|
||||
_handle_create_session_request(request, id);
|
||||
|
||||
if (request.has_type("upgrade"))
|
||||
_handle_upgrade_session_request(request, id);
|
||||
|
||||
if (request.has_type("close"))
|
||||
_handle_close_session_request(request, id);
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::_handle_session_requests()
|
||||
{
|
||||
_session_requests->update();
|
||||
|
||||
Xml_node const requests = _session_requests->xml();
|
||||
|
||||
requests.for_each_sub_node([&] (Xml_node request) {
|
||||
_handle_session_request(request); });
|
||||
|
||||
_report_update_trigger.trigger_report_update();
|
||||
}
|
||||
|
||||
|
||||
void Init::Server::apply_config(Xml_node config)
|
||||
{
|
||||
_services.for_each([&] (Service &service) { destroy(_alloc, &service); });
|
||||
|
||||
config.for_each_sub_node("service", [&] (Xml_node node) {
|
||||
new (_alloc) Service(_services, _alloc, node, _child_services); });
|
||||
|
||||
/*
|
||||
* Construct mechanics for responding to our parent's session requests
|
||||
* on demand.
|
||||
*/
|
||||
bool services_provided = false;
|
||||
_services.for_each([&] (Service const &) { services_provided = true; });
|
||||
|
||||
if (services_provided && !_session_requests.constructed()) {
|
||||
_session_requests.construct(_env, "session_requests");
|
||||
_session_request_handler.construct(_env.ep(), *this,
|
||||
&Server::_handle_session_requests);
|
||||
_session_requests->sigh(*_session_request_handler);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to resolve pending session requests that may become serviceable with
|
||||
* the new configuration.
|
||||
*/
|
||||
if (services_provided && _session_requests.constructed())
|
||||
_handle_session_requests();
|
||||
|
||||
/*
|
||||
* Re-validate routes of existing sessions, close sessions whose routes
|
||||
* changed.
|
||||
*/
|
||||
_client_id_space.for_each<Session_state>([&] (Session_state &session) {
|
||||
try {
|
||||
Route const route = _resolve_session_request(session.service().name(),
|
||||
session.client_label());
|
||||
|
||||
bool const route_unchanged = (route.service == session.service())
|
||||
&& (route.label == session.label());
|
||||
if (!route_unchanged)
|
||||
throw Service_denied();
|
||||
}
|
||||
catch (Service_denied) { close_session(session); }
|
||||
catch (Service_not_present) { close_session(session); }
|
||||
});
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* \brief Server role of init, forwarding session requests to children
|
||||
* \author Norman Feske
|
||||
* \date 2017-03-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__SERVER_H_
|
||||
#define _SRC__INIT__SERVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <os/buffered_xml.h>
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
#include "service.h"
|
||||
#include "state_reporter.h"
|
||||
|
||||
namespace Init { class Server; }
|
||||
|
||||
|
||||
class Init::Server : Session_state::Ready_callback,
|
||||
Session_state::Closed_callback
|
||||
{
|
||||
private:
|
||||
|
||||
struct Route
|
||||
{
|
||||
Routed_service &service;
|
||||
Session_label label;
|
||||
};
|
||||
|
||||
Env &_env;
|
||||
|
||||
Allocator &_alloc;
|
||||
|
||||
/*
|
||||
* ID space of requests originating from the parent
|
||||
*/
|
||||
Id_space<Parent::Server> _server_id_space { };
|
||||
|
||||
/*
|
||||
* ID space of requests issued to the children of init
|
||||
*/
|
||||
Id_space<Parent::Client> _client_id_space { };
|
||||
|
||||
/**
|
||||
* Exception type
|
||||
*/
|
||||
class Service_not_present : Exception { };
|
||||
|
||||
/**
|
||||
* Meta data of service provided to our parent
|
||||
*/
|
||||
struct Service;
|
||||
|
||||
Registry<Service> _services { };
|
||||
|
||||
/**
|
||||
* Services provided by our children
|
||||
*/
|
||||
Registry<Routed_service> &_child_services;
|
||||
|
||||
Report_update_trigger &_report_update_trigger;
|
||||
|
||||
Constructible<Attached_rom_dataspace> _session_requests { };
|
||||
Constructible<Signal_handler<Server> > _session_request_handler { };
|
||||
|
||||
/**
|
||||
* \throw Service_denied
|
||||
*/
|
||||
Route _resolve_session_request(Genode::Service::Name const &,
|
||||
Session_label const &);
|
||||
|
||||
void _handle_create_session_request (Xml_node, Parent::Client::Id);
|
||||
void _handle_upgrade_session_request(Xml_node, Parent::Client::Id);
|
||||
void _handle_close_session_request (Xml_node, Parent::Client::Id);
|
||||
|
||||
void _handle_session_request(Xml_node);
|
||||
void _handle_session_requests();
|
||||
|
||||
void _close_session(Session_state &, Parent::Session_response response);
|
||||
|
||||
/**
|
||||
* Session_state::Closed_callback interface
|
||||
*/
|
||||
void session_closed(Session_state &) override;
|
||||
|
||||
/**
|
||||
* Session_state::Ready_callback interface
|
||||
*/
|
||||
void session_ready(Session_state &) override;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc allocator used for buffering XML config data and
|
||||
* for allocating per-service meta data
|
||||
*/
|
||||
Server(Env &env, Allocator &alloc, Registry<Routed_service> &services,
|
||||
Report_update_trigger &report_update_trigger)
|
||||
:
|
||||
_env(env), _alloc(alloc), _child_services(services),
|
||||
_report_update_trigger(report_update_trigger)
|
||||
{ }
|
||||
|
||||
void apply_config(Xml_node);
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__SERVER_H_ */
|
@ -1,151 +0,0 @@
|
||||
/*
|
||||
* \brief Services as targeted by session routes
|
||||
* \author Norman Feske
|
||||
* \date 2017-03-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__SERVICE_H_
|
||||
#define _SRC__INIT__SERVICE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/service.h>
|
||||
#include <base/child.h>
|
||||
|
||||
namespace Init {
|
||||
class Abandonable;
|
||||
class Parent_service;
|
||||
class Routed_service;
|
||||
class Forwarded_service;
|
||||
}
|
||||
|
||||
|
||||
class Init::Abandonable : Interface
|
||||
{
|
||||
private:
|
||||
|
||||
bool _abandoned = false;
|
||||
|
||||
public:
|
||||
|
||||
void abandon() { _abandoned = true; }
|
||||
|
||||
bool abandoned() const { return _abandoned; }
|
||||
};
|
||||
|
||||
|
||||
class Init::Parent_service : public Genode::Parent_service, public Abandonable
|
||||
{
|
||||
private:
|
||||
|
||||
Registry<Parent_service>::Element _reg_elem;
|
||||
|
||||
public:
|
||||
|
||||
Parent_service(Registry<Parent_service> ®istry, Env &env,
|
||||
Service::Name const &name)
|
||||
:
|
||||
Genode::Parent_service(env, name), _reg_elem(registry, *this)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Init-specific representation of a child service
|
||||
*/
|
||||
class Init::Routed_service : public Async_service, public Abandonable
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Child_policy::Name Child_name;
|
||||
|
||||
struct Pd_accessor : Interface
|
||||
{
|
||||
virtual Pd_session &pd() = 0;
|
||||
virtual Pd_session_capability pd_cap() const = 0;
|
||||
};
|
||||
|
||||
struct Ram_accessor : Interface
|
||||
{
|
||||
virtual Pd_session &ram() = 0;
|
||||
virtual Pd_session_capability ram_cap() const = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Child_name _child_name;
|
||||
|
||||
Pd_accessor &_pd_accessor;
|
||||
|
||||
Session_state::Factory &_factory;
|
||||
|
||||
Registry<Routed_service>::Element _registry_element;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param services registry of all services provides by children
|
||||
* \param child_name child name of server, used for session routing
|
||||
*
|
||||
* The other arguments correspond to the arguments of 'Async_service'.
|
||||
*/
|
||||
Routed_service(Registry<Routed_service> &services,
|
||||
Child_name const &child_name,
|
||||
Pd_accessor &pd_accessor,
|
||||
Ram_accessor &,
|
||||
Id_space<Parent::Server> &server_id_space,
|
||||
Session_state::Factory &factory,
|
||||
Service::Name const &name,
|
||||
Wakeup &wakeup)
|
||||
:
|
||||
Async_service(name, server_id_space, factory, wakeup),
|
||||
_child_name(child_name), _pd_accessor(pd_accessor),
|
||||
_factory(factory), _registry_element(services, *this)
|
||||
{ }
|
||||
|
||||
Child_name const &child_name() const { return _child_name; }
|
||||
|
||||
Session_state::Factory &factory() { return _factory; }
|
||||
|
||||
/**
|
||||
* Ram_transfer::Account interface
|
||||
*/
|
||||
void transfer(Pd_session_capability to, Ram_quota amount) override
|
||||
{
|
||||
if (to.valid()) _pd_accessor.pd().transfer_quota(to, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ram_transfer::Account interface
|
||||
*/
|
||||
Pd_session_capability cap(Ram_quota) const override
|
||||
{
|
||||
return _pd_accessor.pd_cap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cap_transfer::Account interface
|
||||
*/
|
||||
void transfer(Pd_session_capability to, Cap_quota amount) override
|
||||
{
|
||||
if (to.valid()) _pd_accessor.pd().transfer_quota(to, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cap_transfer::Account interface
|
||||
*/
|
||||
Pd_session_capability cap(Cap_quota) const override
|
||||
{
|
||||
return _pd_accessor.pd_cap();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__SERVICE_H_ */
|
@ -1,197 +0,0 @@
|
||||
/*
|
||||
* \brief Init-state reporting mechanism
|
||||
* \author Norman Feske
|
||||
* \date 2017-03-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__STATE_REPORTER_H_
|
||||
#define _SRC__INIT__STATE_REPORTER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/reporter.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
/* local includes */
|
||||
#include "report.h"
|
||||
|
||||
namespace Init { class State_reporter; }
|
||||
|
||||
class Init::State_reporter : public Report_update_trigger
|
||||
{
|
||||
public:
|
||||
|
||||
struct Producer : Interface
|
||||
{
|
||||
virtual void produce_state_report(Xml_generator &xml,
|
||||
Report_detail const &) const = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
|
||||
Producer &_producer;
|
||||
|
||||
Constructible<Reporter> _reporter { };
|
||||
|
||||
size_t _buffer_size = 0;
|
||||
|
||||
Reconstructible<Report_detail> _report_detail { };
|
||||
|
||||
uint64_t _report_delay_ms = 0;
|
||||
|
||||
/* interval used when child-ram reporting is enabled */
|
||||
uint64_t _report_period_ms = 0;
|
||||
|
||||
/* version string from config, to be reflected in the report */
|
||||
typedef String<64> Version;
|
||||
Version _version { };
|
||||
|
||||
Constructible<Timer::Connection> _timer { };
|
||||
Constructible<Timer::Connection> _timer_periodic { };
|
||||
|
||||
Signal_handler<State_reporter> _timer_handler {
|
||||
_env.ep(), *this, &State_reporter::_handle_timer };
|
||||
|
||||
Signal_handler<State_reporter> _timer_periodic_handler {
|
||||
_env.ep(), *this, &State_reporter::_handle_timer };
|
||||
|
||||
Signal_handler<State_reporter> _immediate_handler {
|
||||
_env.ep(), *this, &State_reporter::_handle_timer };
|
||||
|
||||
bool _scheduled = false;
|
||||
|
||||
void _handle_timer()
|
||||
{
|
||||
_scheduled = false;
|
||||
|
||||
try {
|
||||
Reporter::Xml_generator xml(*_reporter, [&] () {
|
||||
|
||||
if (_version.valid())
|
||||
xml.attribute("version", _version);
|
||||
|
||||
_producer.produce_state_report(xml, *_report_detail);
|
||||
});
|
||||
}
|
||||
catch(Xml_generator::Buffer_exceeded) {
|
||||
|
||||
error("state report exceeds maximum size");
|
||||
|
||||
/* try to reflect the error condition as state report */
|
||||
try {
|
||||
Reporter::Xml_generator xml(*_reporter, [&] () {
|
||||
xml.attribute("error", "report buffer exceeded"); });
|
||||
}
|
||||
catch (...) { }
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
State_reporter(Env &env, Producer &producer)
|
||||
:
|
||||
_env(env), _producer(producer)
|
||||
{ }
|
||||
|
||||
void apply_config(Xml_node config)
|
||||
{
|
||||
try {
|
||||
Xml_node report = config.sub_node("report");
|
||||
|
||||
/* (re-)construct reporter whenever the buffer size is changed */
|
||||
Number_of_bytes const buffer_size =
|
||||
report.attribute_value("buffer", Number_of_bytes(4096));
|
||||
|
||||
if (buffer_size != _buffer_size || !_reporter.constructed()) {
|
||||
_buffer_size = buffer_size;
|
||||
_reporter.construct(_env, "state", "state", _buffer_size);
|
||||
}
|
||||
|
||||
_report_detail.construct(report);
|
||||
_report_delay_ms = report.attribute_value("delay_ms", 100UL);
|
||||
_reporter->enabled(true);
|
||||
}
|
||||
catch (Xml_node::Nonexistent_sub_node) {
|
||||
_report_detail.construct();
|
||||
_report_delay_ms = 0;
|
||||
if (_reporter.constructed())
|
||||
_reporter->enabled(false);
|
||||
}
|
||||
|
||||
bool trigger_update = false;
|
||||
|
||||
Version const version = config.attribute_value("version", Version());
|
||||
if (version != _version) {
|
||||
_version = version;
|
||||
trigger_update = true;
|
||||
}
|
||||
|
||||
if (_report_delay_ms) {
|
||||
if (!_timer.constructed()) {
|
||||
_timer.construct(_env);
|
||||
_timer->sigh(_timer_handler);
|
||||
}
|
||||
trigger_update = true;
|
||||
}
|
||||
|
||||
if (trigger_update)
|
||||
trigger_report_update();
|
||||
|
||||
/*
|
||||
* If the report features information about child-RAM quotas, we
|
||||
* update the report periodically. Even in the absence of any other
|
||||
* report-triggering event, a child may consume/free RAM from its
|
||||
* RAM session without any interplay with init. The periodic
|
||||
* reports ensure that such changes are reflected by init's state
|
||||
* report.
|
||||
*
|
||||
* By default, the interval is one second. However, when the
|
||||
* 'delay_ms' attribute is defined with a higher value than that,
|
||||
* the user intends to limit the rate of state reports. If so, we
|
||||
* use the value of 'delay_ms' as interval.
|
||||
*/
|
||||
uint64_t const period_ms = max(1000U, _report_delay_ms);
|
||||
bool const period_changed = (_report_period_ms != period_ms);
|
||||
bool const report_periodically = _report_detail->child_ram()
|
||||
|| _report_detail->child_caps();
|
||||
|
||||
if (report_periodically && !_timer_periodic.constructed()) {
|
||||
_timer_periodic.construct(_env);
|
||||
_timer_periodic->sigh(_timer_periodic_handler);
|
||||
}
|
||||
|
||||
if (!report_periodically && _timer_periodic.constructed()) {
|
||||
_report_period_ms = 0;
|
||||
_timer_periodic.destruct();
|
||||
}
|
||||
|
||||
if (period_changed && _timer_periodic.constructed()) {
|
||||
_report_period_ms = period_ms;
|
||||
_timer_periodic->trigger_periodic(1000*_report_period_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_report_update() override
|
||||
{
|
||||
if (!_scheduled && _timer.constructed() && _report_delay_ms) {
|
||||
_timer->trigger_once(_report_delay_ms*1000);
|
||||
_scheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_immediate_report_update() override
|
||||
{
|
||||
if (_report_delay_ms)
|
||||
Signal_transmitter(_immediate_handler).submit();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__STATE_REPORTER_H_ */
|
@ -1,6 +1,11 @@
|
||||
TARGET = init
|
||||
SRC_CC = main.cc child.cc server.cc
|
||||
SRC_CC = main.cc
|
||||
LIBS = base
|
||||
INC_DIR += $(PRG_DIR)
|
||||
|
||||
CONFIG_XSD = config.xsd
|
||||
|
||||
# statically link sandbox library to avoid dependency from sandbox.lib.so
|
||||
SRC_CC += library.cc child.cc server.cc
|
||||
INC_DIR += $(REP_DIR)/src/lib/sandbox
|
||||
vpath %.cc $(REP_DIR)/src/lib/sandbox
|
||||
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* \brief Common types used within init
|
||||
* \author Norman Feske
|
||||
* \date 2017-03-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__TYPES_H_
|
||||
#define _SRC__INIT__TYPES_H_
|
||||
|
||||
#include <util/string.h>
|
||||
#include <util/list.h>
|
||||
#include <session/session.h>
|
||||
|
||||
namespace Init {
|
||||
|
||||
class Child;
|
||||
|
||||
using namespace Genode;
|
||||
using Genode::size_t;
|
||||
using Genode::strlen;
|
||||
|
||||
struct Prio_levels { long value; };
|
||||
|
||||
typedef List<List_element<Init::Child> > Child_list;
|
||||
}
|
||||
|
||||
#endif /* _SRC__INIT__TYPES_H_ */
|
||||
|
@ -1,273 +0,0 @@
|
||||
/*
|
||||
* \brief Utilities
|
||||
* \author Norman Feske
|
||||
* \date 2010-05-04
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__UTIL_H_
|
||||
#define _SRC__INIT__UTIL_H_
|
||||
|
||||
namespace Init {
|
||||
|
||||
static inline void warn_insuff_quota(size_t const avail)
|
||||
{
|
||||
warning("specified quota exceeds available quota, "
|
||||
"proceeding with a quota of ", avail);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return sub string of label with the leading child name stripped out
|
||||
*
|
||||
* \return character pointer to the scoped part of the label,
|
||||
* or nullptr if the label is not correctly prefixed with the child's
|
||||
* name
|
||||
*/
|
||||
inline char const *skip_label_prefix(char const *child_name, char const *label)
|
||||
{
|
||||
size_t const child_name_len = strlen(child_name);
|
||||
|
||||
if (strcmp(child_name, label, child_name_len) != 0)
|
||||
return nullptr;
|
||||
|
||||
label += child_name_len;
|
||||
|
||||
/*
|
||||
* Skip label separator. This condition should be always satisfied.
|
||||
*/
|
||||
if (strcmp(" -> ", label, 4) != 0)
|
||||
return nullptr;
|
||||
|
||||
return label + 4;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if service XML node matches service request
|
||||
*
|
||||
* \param args session arguments, inspected for the session label
|
||||
* \param child_name name of the originator of the session request
|
||||
* \param service_name name of the requested service
|
||||
*/
|
||||
inline bool service_node_matches(Xml_node const service_node,
|
||||
Session_label const &label,
|
||||
Child_policy::Name const &child_name,
|
||||
Service::Name const &service_name)
|
||||
{
|
||||
bool const service_matches =
|
||||
service_node.has_type("any-service") ||
|
||||
(service_node.has_type("service") &&
|
||||
service_node.attribute_value("name", Service::Name()) == service_name);
|
||||
|
||||
if (!service_matches)
|
||||
return false;
|
||||
|
||||
typedef String<Session_label::capacity()> Label;
|
||||
|
||||
char const *unscoped_attr = "unscoped_label";
|
||||
char const *label_last_attr = "label_last";
|
||||
|
||||
bool const route_depends_on_child_provided_label =
|
||||
service_node.has_attribute("label") ||
|
||||
service_node.has_attribute("label_prefix") ||
|
||||
service_node.has_attribute("label_suffix") ||
|
||||
service_node.has_attribute(label_last_attr);
|
||||
|
||||
if (service_node.has_attribute(unscoped_attr)) {
|
||||
|
||||
/*
|
||||
* If an 'unscoped_label' attribute is provided, don't consider any
|
||||
* scoped label attribute.
|
||||
*/
|
||||
if (route_depends_on_child_provided_label)
|
||||
warning("service node contains both scoped and unscoped label attributes");
|
||||
|
||||
return label == service_node.attribute_value(unscoped_attr, Label());
|
||||
}
|
||||
|
||||
if (service_node.has_attribute(label_last_attr))
|
||||
return service_node.attribute_value(label_last_attr, Label()) == label.last_element();
|
||||
|
||||
if (!route_depends_on_child_provided_label)
|
||||
return true;
|
||||
|
||||
char const * const scoped_label = skip_label_prefix(
|
||||
child_name.string(), label.string());
|
||||
|
||||
if (!scoped_label)
|
||||
return false;
|
||||
|
||||
Session_label const session_label(scoped_label);
|
||||
|
||||
return !Xml_node_label_score(service_node, session_label).conflict();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if service name is ambiguous
|
||||
*
|
||||
* \return true if the same service is provided multiple
|
||||
* times
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
template <typename T>
|
||||
inline bool is_ambiguous(Registry<T> const &services, Service::Name const &name)
|
||||
{
|
||||
/* count number of services with the specified name */
|
||||
unsigned cnt = 0;
|
||||
services.for_each([&] (T const &service) {
|
||||
cnt += (service.name() == name); });
|
||||
|
||||
return cnt > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find service with certain values in given registry
|
||||
*
|
||||
* \param services service registry
|
||||
* \param name name of wanted service
|
||||
* \param filter_fn function that applies additional filters
|
||||
*
|
||||
* \throw Service_denied
|
||||
*/
|
||||
template <typename T, typename FILTER_FN>
|
||||
inline T &find_service(Registry<T> &services,
|
||||
Service::Name const &name,
|
||||
FILTER_FN const &filter_fn)
|
||||
{
|
||||
T *service = nullptr;
|
||||
services.for_each([&] (T &s) {
|
||||
|
||||
if (service || s.name() != name || filter_fn(s))
|
||||
return;
|
||||
|
||||
service = &s;
|
||||
});
|
||||
|
||||
if (!service)
|
||||
throw Service_denied();
|
||||
|
||||
if (service->abandoned())
|
||||
throw Service_denied();
|
||||
|
||||
return *service;
|
||||
}
|
||||
|
||||
|
||||
inline void generate_ram_info(Xml_generator &xml, Pd_session const &pd)
|
||||
{
|
||||
typedef String<32> Value;
|
||||
xml.attribute("quota", Value(pd.ram_quota()));
|
||||
xml.attribute("used", Value(pd.used_ram()));
|
||||
xml.attribute("avail", Value(pd.avail_ram()));
|
||||
}
|
||||
|
||||
inline void generate_caps_info(Xml_generator &xml, Pd_session const &pd)
|
||||
{
|
||||
typedef String<32> Value;
|
||||
xml.attribute("quota", Value(pd.cap_quota()));
|
||||
xml.attribute("used", Value(pd.used_caps()));
|
||||
xml.attribute("avail", Value(pd.avail_caps()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read priority-levels declaration from config
|
||||
*/
|
||||
inline Prio_levels prio_levels_from_xml(Xml_node config)
|
||||
{
|
||||
long const prio_levels = config.attribute_value("prio_levels", 0UL);
|
||||
|
||||
if (prio_levels && ((prio_levels >= (long)sizeof(prio_levels)*8) ||
|
||||
(prio_levels != (1L << log2(prio_levels))))) {
|
||||
warning("prio levels is not power of two, priorities are disabled");
|
||||
return Prio_levels { 0 };
|
||||
}
|
||||
return Prio_levels { prio_levels };
|
||||
}
|
||||
|
||||
|
||||
inline long priority_from_xml(Xml_node start_node, Prio_levels prio_levels)
|
||||
{
|
||||
long const default_priority = Cpu_session::DEFAULT_PRIORITY;
|
||||
|
||||
long priority = start_node.attribute_value("priority", default_priority);
|
||||
|
||||
/*
|
||||
* All priority declarations in the config file are
|
||||
* negative because child priorities can never be higher
|
||||
* than parent priorities. To simplify priority
|
||||
* calculations, we use inverted values. Lower values
|
||||
* correspond to higher priorities.
|
||||
*/
|
||||
priority = -priority;
|
||||
|
||||
if (priority && (priority >= prio_levels.value)) {
|
||||
long new_prio = prio_levels.value ? prio_levels.value - 1 : 0;
|
||||
|
||||
Service::Name const name = start_node.attribute_value("name", Service::Name());
|
||||
warning(name, ": invalid priority, upgrading "
|
||||
"from ", -priority, " to ", -new_prio);
|
||||
return new_prio;
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
|
||||
inline Affinity::Location
|
||||
affinity_location_from_xml(Affinity::Space const &space, Xml_node start_node)
|
||||
{
|
||||
typedef Affinity::Location Location;
|
||||
try {
|
||||
Xml_node node = start_node.sub_node("affinity");
|
||||
|
||||
/* if no position value is specified, select the whole row/column */
|
||||
unsigned long const
|
||||
default_width = node.has_attribute("xpos") ? 1 : space.width(),
|
||||
default_height = node.has_attribute("ypos") ? 1 : space.height();
|
||||
|
||||
unsigned long const
|
||||
width = node.attribute_value<unsigned long>("width", default_width),
|
||||
height = node.attribute_value<unsigned long>("height", default_height);
|
||||
|
||||
long const x1 = node.attribute_value<long>("xpos", 0),
|
||||
y1 = node.attribute_value<long>("ypos", 0),
|
||||
x2 = x1 + width - 1,
|
||||
y2 = y1 + height - 1;
|
||||
|
||||
/* clip location to space boundary */
|
||||
return Location(max(x1, 0L), max(y1, 0L),
|
||||
min((unsigned)(x2 - x1 + 1), space.width()),
|
||||
min((unsigned)(y2 - y1 + 1), space.height()));
|
||||
}
|
||||
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<unsigned long>("width", 1),
|
||||
node.attribute_value<unsigned long>("height", 1));
|
||||
} catch (...) {
|
||||
return Affinity::Space(1, 1); }
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _SRC__INIT__UTIL_H_ */
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* \brief Init verbosity
|
||||
* \author Norman Feske
|
||||
* \date 2017-01-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__INIT__VERBOSE_H_
|
||||
#define _SRC__INIT__VERBOSE_H_
|
||||
|
||||
#include <util/noncopyable.h>
|
||||
#include <util/xml_node.h>
|
||||
|
||||
namespace Init { struct Verbose; }
|
||||
|
||||
|
||||
class Init::Verbose : Genode::Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
bool _enabled;
|
||||
|
||||
public:
|
||||
|
||||
Verbose(Genode::Xml_node config)
|
||||
: _enabled(config.attribute_value("verbose", false)) { }
|
||||
|
||||
bool enabled() const { return _enabled; }
|
||||
};
|
||||
|
||||
#endif /* _SRC__INIT__VERBOSE_H_ */
|
Reference in New Issue
Block a user