init: refactoring into multiple files

This patch splits the implementation of init into several headers to
make the implementation easier to digest and to maintain.
This commit is contained in:
Norman Feske 2017-03-03 16:57:41 +01:00 committed by Christian Helmuth
parent 9dca1503a8
commit 1cf830497a
16 changed files with 1852 additions and 1832 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,138 +0,0 @@
/*
* \brief Utility for handling child configuration
* \author Norman Feske
* \date 2008-03-22
*/
/*
* Copyright (C) 2008-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 _INCLUDE__INIT__CHILD_CONFIG_H_
#define _INCLUDE__INIT__CHILD_CONFIG_H_
#warning header is deprecated, used os/dynamic_rom_session.h instead
#include <util/xml_node.h>
#include <base/attached_dataspace.h>
#include <ram_session/ram_session.h>
namespace Init { class Child_config; }
class Init::Child_config
{
private:
Genode::Ram_session &_ram;
typedef Genode::String<64> Rom_name;
Rom_name const _rom_name;
Genode::Ram_dataspace_capability const _ram_ds;
Rom_name _rom_name_from_start_node(Genode::Xml_node start)
{
if (!start.has_sub_node("configfile"))
return Rom_name();
return start.sub_node("configfile").attribute_value("name", Rom_name());
}
/**
* Buffer '<config>' sub node in a dedicated RAM dataspace
*
* \throw Ram_session::Alloc_failed
* \throw Rm_session::Attach_failed
*/
Genode::Ram_dataspace_capability
_ram_ds_from_start_node(Genode::Xml_node start,
Genode::Ram_session &ram, Genode::Region_map &rm)
{
/*
* If the start node contains a 'config' entry, we copy this entry
* into a fresh dataspace to be provided to our child.
*/
Genode::Xml_node const config = start.has_sub_node("config")
? start.sub_node("config")
: Genode::Xml_node("<config/>");
Genode::Ram_dataspace_capability ram_ds;
try {
/*
* Allocate RAM dataspace that is big enough to hold the
* configuration and the null termination.
*/
ram_ds = ram.alloc(config.size() + 1);
/*
* Make dataspace locally accessible, copy configuration into the
* dataspace, and append a string-terminating zero.
*/
Genode::Attached_dataspace attached(rm, ram_ds);
Genode::memcpy(attached.local_addr<char>(),
config.addr(), config.size());
attached.local_addr<char>()[config.size()] = 0;
return ram_ds;
}
catch (Genode::Region_map::Attach_failed) { ram.free(ram_ds); throw; }
}
public:
/**
* Constructor
*
* The provided RAM session is used to obtain a dataspace for
* holding the copy of the child's configuration data unless the
* configuration is supplied via a config ROM module.
*
* \throw Ram_session::Alloc_failed failed to allocate the backing
* store for holding config data
*
* \throw Region_map::Attach_failed failed to temporarily attach the
* config dataspace to the local
* address space
*
* If the start node contains a 'filename' entry, we only keep the
* information about the ROM module name.
*/
Child_config(Genode::Ram_session &ram, Genode::Region_map &local_rm,
Genode::Xml_node start)
:
_ram(ram),
_rom_name(_rom_name_from_start_node(start)),
_ram_ds(_rom_name.valid() ? Genode::Ram_dataspace_capability()
: _ram_ds_from_start_node(start, ram, local_rm))
{ }
/**
* Destructor
*/
~Child_config() { if (_ram_ds.valid()) _ram.free(_ram_ds); }
/**
* Return file name if configuration comes from a file
*
* If the configuration is provided inline, the method returns 0.
*/
char const *filename() const {
return _rom_name.valid() ? _rom_name.string() : nullptr; }
/**
* Request dataspace holding the start node's configuration data
*
* This method returns a valid dataspace only when using an
* inline configuration (if 'filename()' returns 0).
*/
Genode::Dataspace_capability dataspace() {
return Genode::Dataspace_capability(_ram_ds); }
};
#endif /* _INCLUDE__INIT__CHILD_CONFIG_H_ */

53
repos/os/src/init/alias.h Normal file
View File

@ -0,0 +1,53 @@
/*
* \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_ */

View File

@ -0,0 +1,55 @@
/*
* \brief Utility for buffering XML nodes
* \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__BUFFERED_XML_H_
#define _SRC__INIT__BUFFERED_XML_H_
namespace Init { class Buffered_xml; }
class Init::Buffered_xml
{
private:
Allocator &_alloc;
char const * const _ptr; /* pointer to dynamically allocated buffer */
Xml_node const _xml; /* referring to buffer of '_ptr' */
/**
* \throw Allocator::Out_of_memory
*/
static char const *_init_ptr(Allocator &alloc, Xml_node node)
{
char *ptr = (char *)alloc.alloc(node.size());
Genode::memcpy(ptr, node.addr(), node.size());
return ptr;
}
public:
/**
* Constructor
*
* \throw Allocator::Out_of_memory
*/
Buffered_xml(Allocator &alloc, Xml_node node)
:
_alloc(alloc), _ptr(_init_ptr(alloc, node)), _xml(_ptr, node.size())
{ }
~Buffered_xml() { _alloc.free(const_cast<char *>(_ptr), _xml.size()); }
Xml_node xml() const { return _xml; }
};
#endif /* _SRC__INIT__BUFFERED_XML_H_ */

559
repos/os/src/init/child.cc Normal file
View File

@ -0,0 +1,559 @@
/*
* \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.
*/
/* local includes */
#include <child.h>
Init::Child::Apply_config_result Init::Child::apply_config(Xml_node start_node)
{
Child_policy &policy = *this;
if (_state == STATE_ABANDONED)
return NO_SIDE_EFFECTS;
/*
* If the child's environment is incomplete, restart it to attempt
* the re-routing of its environment sessions.
*/
if (!_child.active()) {
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 new version differs */
if (start_node.size() != _start_node->xml().size() ||
Genode::memcmp(start_node.addr(), _start_node->xml().addr(),
start_node.size()) != 0)
{
/*
* Check for a change of the version attribute, force restart
* if the version changed.
*/
if (_version != start_node.attribute_value("version", Version())) {
abandon();
return MAY_HAVE_SIDE_EFFECTS;
}
/*
* 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 old_config = _start_node->xml().sub_node(tag);
Xml_node new_config = start_node.sub_node(tag);
if (Genode::memcmp(old_config.addr(), new_config.addr(),
min(old_config.size(), new_config.size())))
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);
/* 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;
}
void Init::Child::report_state(Xml_generator &xml, Report_detail const &detail) const
{
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 (detail.child_ram() && _child.ram_session_cap().valid()) {
xml.node("ram", [&] () {
/*
* The const cast is needed because there is no const
* accessor for the RAM session of the child.
*/
auto &nonconst_child = const_cast<Genode::Child &>(_child);
generate_ram_info(xml, nonconst_child.ram());
});
}
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); }); };
server_id_space().for_each<Session_state const>(fn);
});
}
});
}
void Init::Child::init(Ram_session &session, Ram_session_capability cap)
{
session.ref_account(_env.ram_session_cap());
size_t const initial_session_costs =
session_alloc_batch_size()*_child.session_factory().session_costs();
size_t const transfer_ram = _resources.effective_ram_quota.value > initial_session_costs
? _resources.effective_ram_quota.value - initial_session_costs
: 0;
if (transfer_ram)
_env.ram().transfer_quota(cap, transfer_ram);
}
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 };
/*
* \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 Parent::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);
/* 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() };
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()));
if (target.has_type("parent")) {
Parent_service *service = nullptr;
if ((service = find_service(_parent_services, service_name)))
return Route { *service, target_label };
if (service && service->abandoned())
throw Parent::Service_denied();
if (!service_wildcard) {
warning(name(), ": service lookup for "
"\"", service_name, "\" at parent failed");
throw Parent::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);
Routed_service *service = nullptr;
_child_services.for_each([&] (Routed_service &s) {
if (s.name() == Service::Name(service_name)
&& s.child_name() == server_name)
service = &s; });
if (service && service->abandoned())
throw Parent::Service_denied();
if (service)
return Route { *service, target_label };
if (!service_wildcard) {
warning(name(), ": lookup to child "
"server \"", server_name, "\" failed");
throw Parent::Service_denied();
}
}
if (target.has_type("any-child")) {
if (is_ambiguous(_child_services, service_name)) {
error(name(), ": ambiguous routes to "
"service \"", service_name, "\"");
throw Parent::Service_denied();
}
Routed_service *service = nullptr;
if ((service = find_service(_child_services, service_name)))
return Route { *service, target_label };
if (!service_wildcard) {
warning(name(), ": lookup for service "
"\"", service_name, "\" failed");
throw Parent::Service_denied();
}
}
if (target.last())
break;
}
}
} catch (Xml_node::Nonexistent_sub_node) { }
warning(name(), ": no route to service \"", service_name, "\"");
throw Parent::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" && _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 == "RAM" && !_constrain_phys) {
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(),
session_location.ypos()));
return Affinity(space, location);
}
void Init::Child::announce_service(Service::Name const &service_name)
{
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.string());
size_t const requested_ram_quota =
Arg_string::find_arg(args.string(), "ram_quota")
.ulong_value(0);
if (_ram_limit_accessor.ram_limit().value < requested_ram_quota) {
warning("cannot respond to resource request - out of memory");
return;
}
_env.ram().transfer_quota(_child.ram_session_cap(),
requested_ram_quota);
/* wake up child that was starved for resources */
_child.notify_resource_avail();
}
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,
Name_registry &name_registry,
Ram_limit_accessor &ram_limit_accessor,
long 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),
_ram_limit_accessor(ram_limit_accessor),
_name_registry(name_registry),
_resources(_resources_from_start_node(start_node, prio_levels, affinity_space)),
_parent_services(parent_services),
_child_services(child_services),
_session_requester(_env.ep().rpc_ep(), _env.ram(), _env.rm())
{
if (_verbose.enabled()) {
log("child \"", _unique_name, "\"");
log(" RAM quota: ", Number_of_bytes(_resources.effective_ram_quota.value));
log(" 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()
{
_child_services.for_each([&] (Routed_service &service) {
if (service.has_id_space(_session_requester.id_space()))
destroy(_alloc, &service); });
}

465
repos/os/src/init/child.h Normal file
View File

@ -0,0 +1,465 @@
/*
* \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>
/* local includes */
#include <types.h>
#include <verbose.h>
#include <report.h>
#include <buffered_xml.h>
#include <name_registry.h>
#include <service.h>
#include <utils.h>
namespace Init { class Child; }
class Init::Child : Child_policy, Child_service::Wakeup
{
public:
/**
* 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 { virtual Xml_node default_route() = 0; };
struct Ram_limit_accessor { virtual Ram_quota ram_limit() = 0; };
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.
*/
typedef String<80> Version;
Version _version { _start_node->xml().attribute_value("version", Version()) };
Default_route_accessor &_default_route_accessor;
Ram_limit_accessor &_ram_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("missing '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) };
/**
* Resources assigned to the child
*/
struct Resources
{
long prio_levels_log2;
long priority;
Affinity affinity;
Ram_quota assigned_ram_quota;
Ram_quota effective_ram_quota;
size_t cpu_quota_pc;
bool constrain_phys;
};
Resources _resources_from_start_node(Xml_node start_node, long prio_levels,
Affinity::Space const &affinity_space)
{
size_t cpu_quota_pc = 0;
bool constrain_phys = false;
Number_of_bytes ram_bytes = 0;
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);
}
});
return Resources { log2(prio_levels),
priority_from_xml(start_node, prio_levels),
Affinity(affinity_space,
affinity_location_from_xml(affinity_space, start_node)),
Ram_quota { ram_bytes },
Ram_quota { Genode::Child::effective_ram_quota(ram_bytes) },
cpu_quota_pc,
constrain_phys };
}
Resources _resources;
void _check_resource_constraints()
{
if (_resources.effective_ram_quota.value == 0)
warning("no valid RAM RESOURCE for child \"", _unique_name, "\"");
Ram_quota const ram_limit = _ram_limit_accessor.ram_limit();
/*
* If the configured RAM quota exceeds our own quota, we donate
* all remaining quota to the child.
*/
if (_resources.assigned_ram_quota.value > ram_limit.value) {
_resources.assigned_ram_quota.value = ram_limit.value;
if (_verbose.enabled())
warn_insuff_quota(ram_limit.value);
}
}
bool const _resources_checked = (_check_resource_constraints(), true);
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_ram(), _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();
/*
* The 'config.size()' method returns 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 'config_len' and thereby include the
* last actual config-content character in the result.
*/
Genode::strncpy(dst, config.addr(), config_len + 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 };
Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), *this };
struct Ram_accessor : Routed_service::Ram_accessor
{
Genode::Child &_child;
Ram_accessor(Genode::Child &child) : _child(child) { }
Ram_session_capability ram() const override {
return _child.ram_session_cap(); }
} _ram_accessor { _child };
/**
* Child_service::Wakeup callback
*/
void wakeup_child_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 (Parent::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)
{
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;
}
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(), _ram_accessor,
_session_requester.id_space(),
_child.session_factory(),
name, *this);
}
public:
/**
* Constructor
*
* \param alloc allocator solely used for configuration-dependent
* allocations. It is not used for allocations on behalf
* of the child's behavior.
*
* \throw Allocator::Out_of_memory could not buffer the XML start node
*/
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,
Name_registry &name_registry,
Ram_limit_accessor &ram_limit_accessor,
long 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(); }
Ram_quota ram_quota() const { return _resources.assigned_ram_quota; }
void initiate_env_ram_session()
{
if (_state == STATE_INITIAL) {
_child.initiate_env_ram_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(); });
}
bool abandoned() const { return _state == STATE_ABANDONED; }
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);
void report_state(Xml_generator &xml, Report_detail const &detail) const;
/****************************
** Child-policy interface **
****************************/
Child_policy::Name name() const override { return _unique_name; }
Ram_session &ref_ram() override { return _env.ram(); }
Ram_session_capability ref_ram_cap() const override { return _env.ram_session_cap(); }
void init(Ram_session &, Ram_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 (...) { }
/*
* 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; }
};
#endif /* _SRC__INIT__CHILD_H_ */

View File

@ -0,0 +1,149 @@
/*
* \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_ */

View File

@ -14,367 +14,14 @@
/* Genode includes */
#include <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <os/reporter.h>
#include <timer_session/connection.h>
/* init includes */
#include <init/child.h>
/* local includes */
#include <child_registry.h>
#include <child.h>
#include <alias.h>
#include <state_reporter.h>
namespace Init {
using namespace Genode;
/**
* Read priority-levels declaration from config
*/
inline long read_prio_levels(Xml_node config)
{
long const prio_levels = config.attribute_value("prio_levels", 0UL);
if (prio_levels && (prio_levels != (1 << log2(prio_levels)))) {
warning("prio levels is not power of two, priorities are disabled");
return 0;
}
return prio_levels;
}
/**
* 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 Genode::Affinity::Space read_affinity_space(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); }
}
}
/********************
** Child registry **
********************/
namespace Init { struct Alias; }
/**
* Representation of an alias for a child
*/
struct Init::Alias : Genode::List<Alias>::Element
{
typedef Genode::String<128> Name;
typedef Genode::String<128> Child;
Name name;
Child child;
/**
* Exception types
*/
class Name_is_missing { };
class Child_is_missing { };
/**
* Utility to read a string attribute from an XML node
*
* \param STR string type
* \param EXC exception type raised if attribute is not present
*
* \param node XML node
* \param attr_name name of attribute to read
*/
template <typename STR, typename EXC>
static STR _read_string_attr(Genode::Xml_node node, char const *attr_name)
{
char buf[STR::size()];
if (!node.has_attribute(attr_name))
throw EXC();
node.attribute(attr_name).value(buf, sizeof(buf));
return STR(buf);
}
/**
* Constructor
*
* \throw Name_is_missing
* \throw Child_is_missing
*/
Alias(Genode::Xml_node alias)
:
name (_read_string_attr<Name, Name_is_missing> (alias, "name")),
child(_read_string_attr<Name, Child_is_missing>(alias, "child"))
{ }
};
namespace Init {
typedef Genode::List<Genode::List_element<Child> > Child_list;
struct Child_registry;
}
class Init::Child_registry : public Name_registry, Child_list
{
private:
List<Alias> _aliases;
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);
});
}
}
/*****************************
** Name-registry interface **
*****************************/
bool unique(const char *name) const
{
/* check for name clash with an existing child */
Genode::List_element<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;
}
Name deref_alias(Name const &name) override
{
for (Alias const *a = _aliases.first(); a; a = a->next())
if (name == a->name)
return a->child;
return name;
}
};
namespace Init {
struct State_reporter;
struct Main;
}
class Init::State_reporter : public Report_update_trigger
{
public:
struct Producer
{
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;
unsigned _report_delay_ms = 0;
/* version string from config, to be reflected in the report */
typedef String<64> Version;
Version _version;
Constructible<Timer::Connection> _timer;
Signal_handler<State_reporter> _timer_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);
}
_version = config.attribute_value("version", Version());
if (_report_delay_ms) {
if (!_timer.constructed()) {
_timer.construct(_env);
_timer->sigh(_timer_handler);
}
trigger_report_update();
}
}
void trigger_report_update() override
{
if (!_scheduled && _timer.constructed() && _report_delay_ms) {
_timer->trigger_once(_report_delay_ms*1000);
_scheduled = true;
}
}
};
namespace Init { struct Main; }
struct Init::Main : State_reporter::Producer, Child::Default_route_accessor,
@ -651,8 +298,8 @@ void Init::Main::_handle_config()
Init::Child(_env, _heap, *_verbose,
Init::Child::Id { ++_child_cnt }, _state_reporter,
start_node, *this, _children, *this,
read_prio_levels(_config.xml()),
read_affinity_space(_config.xml()),
prio_levels_from_xml(_config.xml()),
affinity_space_from_xml(_config.xml()),
_parent_services, _child_services);
_children.insert(&child);
@ -686,7 +333,6 @@ void Init::Main::_handle_config()
}
catch (Xml_node::Nonexistent_sub_node) { error("no children to start"); }
catch (Xml_node::Invalid_syntax) { error("config has invalid syntax"); }
catch (Init::Child::Child_name_is_not_unique) { }
catch (Init::Child_registry::Alias_name_is_not_unique) { }
/*

View File

@ -0,0 +1,39 @@
/*
* \brief Interface for database of child names
* \author Norman Feske
* \date 2017-03-03
*/
/*
* Copyright (C) 2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _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_ */

View File

@ -11,8 +11,8 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__INIT__REPORT_H_
#define _INCLUDE__INIT__REPORT_H_
#ifndef _SRC__INIT__REPORT_H_
#define _SRC__INIT__REPORT_H_
#include <util/noncopyable.h>
#include <util/xml_node.h>
@ -65,4 +65,4 @@ struct Init::Report_update_trigger
virtual void trigger_report_update() = 0;
};
#endif /* _INCLUDE__INIT__REPORT_H_ */
#endif /* _SRC__INIT__REPORT_H_ */

102
repos/os/src/init/service.h Normal file
View File

@ -0,0 +1,102 @@
/*
* \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_
namespace Init {
class Abandonable;
class Parent_service;
class Routed_service;
}
class Init::Abandonable
{
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> &registry, 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 Child_service, public Abandonable
{
public:
typedef Child_policy::Name Child_name;
struct Ram_accessor { virtual Ram_session_capability ram() const = 0; };
private:
Child_name _child_name;
Ram_accessor &_ram_accessor;
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 'Child_service'.
*/
Routed_service(Registry<Routed_service> &services,
Child_name const &child_name,
Ram_accessor &ram_accessor,
Id_space<Parent::Server> &server_id_space,
Session_state::Factory &factory,
Service::Name const &name,
Child_service::Wakeup &wakeup)
:
Child_service(server_id_space, factory, name,
Ram_session_capability(), wakeup),
_child_name(child_name), _ram_accessor(ram_accessor),
_registry_element(services, *this)
{ }
Child_name const &child_name() const { return _child_name; }
Ram_session_capability ram() const { return _ram_accessor.ram(); }
};
#endif /* _SRC__INIT__SERVICE_H_ */

View File

@ -0,0 +1,137 @@
/*
* \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_
#include <os/reporter.h>
#include <timer_session/connection.h>
namespace Init { class State_reporter; }
class Init::State_reporter : public Report_update_trigger
{
public:
struct Producer
{
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;
unsigned _report_delay_ms = 0;
/* version string from config, to be reflected in the report */
typedef String<64> Version;
Version _version;
Constructible<Timer::Connection> _timer;
Signal_handler<State_reporter> _timer_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);
}
_version = config.attribute_value("version", Version());
if (_report_delay_ms) {
if (!_timer.constructed()) {
_timer.construct(_env);
_timer->sigh(_timer_handler);
}
trigger_report_update();
}
}
void trigger_report_update() override
{
if (!_scheduled && _timer.constructed() && _report_delay_ms) {
_timer->trigger_once(_report_delay_ms*1000);
_scheduled = true;
}
}
};
#endif /* _SRC__INIT__STATE_REPORTER_H_ */

View File

@ -1,3 +1,4 @@
TARGET = init
SRC_CC = main.cc
LIBS = base config
TARGET = init
SRC_CC = main.cc child.cc
LIBS = base
INC_DIR += $(PRG_DIR)

33
repos/os/src/init/types.h Normal file
View File

@ -0,0 +1,33 @@
/*
* \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>
namespace Init {
class Child;
using namespace Genode;
using Genode::size_t;
using Genode::strlen;
struct Ram_quota { size_t value; };
typedef List<List_element<Init::Child> > Child_list;
}
#endif /* _SRC__INIT__TYPES_H_ */

242
repos/os/src/init/utils.h Normal file
View File

@ -0,0 +1,242 @@
/*
* \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 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("name").has_value(service_name.string()));
if (!service_matches)
return false;
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");
char const *unscoped_attr = "unscoped_label";
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");
typedef String<Session_label::capacity()> Label;
return label == service_node.attribute_value(unscoped_attr, Label());
}
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;
}
template <typename T>
inline T *find_service(Registry<T> &services, Service::Name const &name)
{
T *service = nullptr;
services.for_each([&] (T &s) {
if (!service && (s.name() == name))
service = &s; });
return service;
}
inline void generate_ram_info(Xml_generator &xml, Ram_session const &ram)
{
/*
* The const cast is needed because the 'Ram_session' accessors are
* non-const methods.
*/
Ram_session &ram_nonconst = const_cast<Ram_session &>(ram);
typedef String<32> Value;
xml.attribute("quota", Value(Number_of_bytes(ram_nonconst.quota())));
xml.attribute("used", Value(Number_of_bytes(ram_nonconst.used())));
xml.attribute("avail", Value(Number_of_bytes(ram_nonconst.avail())));
}
/**
* Read priority-levels declaration from config
*/
inline long prio_levels_from_xml(Xml_node config)
{
long const prio_levels = config.attribute_value("prio_levels", 0UL);
if (prio_levels && (prio_levels != (1 << log2(prio_levels)))) {
warning("prio levels is not power of two, priorities are disabled");
return 0;
}
return prio_levels;
}
inline long priority_from_xml(Xml_node start_node, long prio_levels)
{
long priority = Cpu_session::DEFAULT_PRIORITY;
try { start_node.attribute("priority").value(&priority); }
catch (...) { }
/*
* 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)) {
long new_prio = prio_levels ? prio_levels-1 : 0;
char name[Service::Name::capacity()];
start_node.attribute("name").value(name, sizeof(name));
warning(Cstring(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_ */

View File

@ -11,8 +11,8 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__INIT__VERBOSE_H_
#define _INCLUDE__INIT__VERBOSE_H_
#ifndef _SRC__INIT__VERBOSE_H_
#define _SRC__INIT__VERBOSE_H_
#include <util/noncopyable.h>
#include <util/xml_node.h>
@ -34,4 +34,4 @@ class Init::Verbose : Genode::Noncopyable
bool enabled() const { return _enabled; }
};
#endif /* _INCLUDE__INIT__VERBOSE_H_ */
#endif /* _SRC__INIT__VERBOSE_H_ */